Section 5.7. Branching, Tagging, and Merging


5.7. Branching, Tagging, and Merging

As 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 Tag

Creating 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 Repository

The 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.

[View full width]

$ svn cp --message "Created a new branch" http://svn.example.com/repos/trunk http://svn .example.com/repos/branches/mybranch Committed revision 2353.

Creating a Branch/Tag from the Working Copy

Sometimes, 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.

[View full width]

$ svn cp --message "Tagging a snapshot of the local working copy" ~/repos/trunk http://svn .example.com/repos/tags/mytag Committed revision 2193.

Switching to the Branch/Tag

Regardless 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 Branch

As 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:

 $ svn merge --revision 100:150 http://svn.example.com/repos/branches/mybranch U goodbye.cpp A hello.cpp 

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.

 $ svn merge branches/mybranch@1426 branches/myotherbranch@253 D goodbye.cpp U hello.cpp 

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 Merges

One 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.

 $ svn merge --revision 305:356 branches/mybranch U Makefile $ svn commit --message "Merged in revisions 305 to 356 of mybranch" Committed revision 1398. 

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.

 $ svn log | grep 'Merged' Merged in revisions 305 to 356 of mybranch Merged in revisions 284 to 305 of mybranch Merged in revisions 246 to 320 of myotherbranch $ svn merge --revision 356:423 branches/mybranch 

Reverting Changes with Merge

Another 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).

 $ svn log | grep -E 'removed|^r[0-9]' r15 | bill | 2004-06-14 19:44:06 -0500 (Sat, 14 Jun 2004) | 98 lines r76 | bill | 2004-07-17 12:35:24 -0500 (Sat, 17 Jul 2004) | 32 lines removed doFoo() r85 | bill | 2004-07-23 10:23:19 -0500 (Fri, 23 Jul 2004) | 15 lines r97 | bill | 2003-08-07 15:54:29 -0500 (Thu, 05 Aug 2004) | 145 lines removed getBar() 

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.

 $ svn merge --revision 76:75 foobar.c U foobar.c $ svn merge --revision 97:96 foobar.c U foobar.c $ svn status M      foobar.c $ svn commit --message "Reverted changes from revs 76 & 97 to foobar.c" Sending        foobar.c Transmitting file data . Committed revision 245. 

Looking before You Merge

Merges 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.



    Subversion Version Control. Using The Subversion Version Control System in Development Projects
    Subversion Version Control. Using The Subversion Version Control System in Development Projects
    ISBN: 131855182
    EAN: N/A
    Year: 2005
    Pages: 132

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net