5.7. Branching, Tagging, and MergingAs I explained in earlier chapters, most version control systems allow you to create branches and tags from the main trunk of your repository. Tags allow you to mark specific points in your development (such as releases) for later referral, and branches allow you to split the development of your repository and continue development in parallel on a secondary path. Unlike many other version control systems, Subversion doesn't actually have a built-in concept of branches or tags. Instead, it just uses cheap server-side copies that preserve history, while not taking up any additional space. Subversion just marks the location of the copied file or directory and notes where it came from. It then allows you to merge the changes from a "branch" back into the main trunk of the repository at a later date. Although the current implementation of Subversion's copy-merge system does pose the occasional problemfor instance, you can quickly lose track of a file's full merge/branch history if it becomes more than trivially complexthe flexibility of the paradigm more than makes up for its shortcomings in most cases. 5.7.1. Creating a Branch or TagCreating a branch or tag in Subversion is trivially easy. All you need to do is run svn copy on the file or directory that you want to branch/tag. Generally, your repository will be set up with one or more directories named branches and tags, which will be where you want to place copies that are conceptually branches or tags. This is a convention, however, and is in no way enforced by Subversion. As far as Subversion is concerned, directories named branches or tags are identical to any other directories. A typical repository layout is to have three top-level directories in the repository, named trunk, branches, and tags. The trunk directory contains the main development branch of the repository, and all branches and tags are created by putting copies in their respective directories. By separating the three directories at the top level, you can then check out only the TRunk directory, and deal with branches and tags only in the repository most of the time. When you need to use or edit parts of the repository that are in branches, you can use svn switch to swap them into your working copy of the TRunk directory. Creating a Branch/Tag in the RepositoryThe most efficient way to branch or tag a part of the repository is usually to perform the copy entirely in the remote repository. When Subversion performs a copy in the repository, it doesn't make a copy of the data, but instead just points the copy back to the data from the original. This allows copies of any sized file to occur in a constant amount of time, be it five kilobytes or five hundred megabytes. On the other hand, if the copy is performed in the local working copy, the repository-side copy still occurs in constant time, but you also make a copy of the data in the working copy, which is proportional to the amount of data to be copied. A repository-only copy is performed using svn copy with repository URLs for both source and destination. When the command is run, Subversion performs an immediate commit, which means that it opens an editor to allow you to enter a log message if you don't supply one on the command line. As an example, the following command shows how you might create a branch of your entire trunk in a typical repository.
Creating a Branch/Tag from the Working CopySometimes, creating a branch or tag entirely on the repository is impractical. For example, you might want to create a tag or branch that includes uncommitted local modifications, or consists of multiple mixed revisions. In such cases, you need to pass svn copy a working copy path as a source. You can also pass a working copy path as the destination of the copy, but that is generally not what you want, because that would result in the data being copied on your local copy. Instead, if you make sure the destination is still a URL, Subversion only sends the local changes to the repository and all other copies are done as the usual cheap repository-side copies.
Switching to the Branch/TagRegardless of how you create a branch or tag, the best way to edit it locally is usually to use svn switch to change the URL that all or part of your working copy points to. If you have a working copy of your trunk already checked out, you will generally save a lot of bandwidth by using the switch command, because it only downloads the differences necessary to switch the URL. The other advantage of svn switch is that it allows you to branch only a portion of your trunk, and then switch that portion in your working copy to the branch without invalidating any relative paths to the rest of your working copy. 5.7.2. Merging a BranchAs you work with a branch, you may periodically want to update it with changes from the main trunk, in order to get changes made by others. You will also usually want to merge the changes that you make in the branch back into the main trunk after they have reached a stable point. Both of these situations are handled with the svn merge command. The svn merge command is conceptually the hardest Subversion command to deal with, but after you understand how it works, using it is not that complicated. The most basic usage of svn merge is to run it with two revision numbers and a source, as in this example:
In this example, svn merge takes the differences between revisions 100 and 150 in the mybranch branch and applies them to the current working copy. The application of the changes occurs just as if you had done an svn update, and any conflicts are handled accordingly. Another way to run svn merge is to give it two URLs or working copy paths, with revision numbers, and perform a merge of their differences.
As you can see, in the preceding example, there is no requirement that the first revision number should be lower than the second. In fact, regardless of which revision is lower, Subversion will always merge the differences calculated by subtracting the lefthand revision from the righthand revision. So, if a file exists in the righthand revision and not in the lefthand revision, it will be added by the merge. Conversely, if it exists in the lefthand revision but not the righthand, it will be removed. You will also notice in the last example that I used peg revisions to identify which revisions should be used to identify the files. Peg revisions in a merge work the same as they did for svn diff (see Section 5.4.1, "Getting Information on the Current State"). Keeping Track of MergesOne of the things that Subversion handles poorly is a file or directory's merge history. Subversion does not keep a record of what has been or hasn't been merged. Because of this, it is important for you to keep your own merge history, in order to avoid merging a change twice (which could cause that change to be undone). The easiest way to keep track of your merge history is to record in the commit log what files/revisions were merged.
Then, the next time you want to merge the changes from mybranch into trunk, you can use svn log with grep to quickly see which revisions have already been merged in.
Reverting Changes with MergeAnother use for the merge command is to revert changes that were applied in a previous revision. Say, for instance, that you removed a couple of functions (named doFoo() and getBar()) from your source file, but now realize that you actually need them. Assuming that they were both removed in discrete commits (i.e., nothing else was changed in the source file at the same time those functions were removed), merging them back into the HEAD revision is quite simple. First, you'll want to check the logs to find out which revisions the function removals actually took place in. If the file is an active one, with a long log, you might want to use the grep command to pare the output down to something a little more manageable. As an example, the following command will search for any lines containing the word "removed" as well as lines that begin with "r" and a number (most likely log entry headers).
After you know which revisions the functions were removed in, you can revert the removals by performing a merge with a range that goes backwards, from the revision where the removal took place, to the revision immediately preceding the removal. This will merge the removed sections back into your current working copy, where you can make any necessary modifications and then commit.
Looking before You MergeMerges can cause a lot of changes to be applied to the files in your working copy, and undoing those changes can be difficult if there are a lot of local changes. It can be helpful, then, to find out exactly which files Subversion will change (as well as what conflicts will occur) before actually performing the merge. Subversion allows you to do just that with the --dry-run option. When svn merge is invoked with --dry-run, Subversion will perform all of the necessary calculations for the merge and output a list of files that will be modified (as well as how those files will be modified), but it will not actually change any of your local files. |
Monday, December 21, 2009
Section 5.7. Branching, Tagging, and Merging
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment