Subversion 1.5 – Merge Tracking

Subversion’s most talked about new feature in version 1.5 is “merge tracking”. The idea of this feature is not new, but very useful. Subversion keeps track about which versions have been merged making merging faster and more comfortable. With Subversion 1.4 and earlier, Subversion didn’t know anything about what has been merged when by whom, and so on. Managing the whole merging theme was based on conventions and rules every project had to introduce.

E.g., in my projects I merged revision ranges from one branch into another and wrote a commit comment like this: “svn merge trunk 522:541”, meaning that I’ve committed a merge from trunk’s revision 522 to revision 541 into the current branch. Before performing such a merge (e.g., updating my development branch with the newest changes from trunk), I had to inspect the log history of the branch in order to find out up to which revision I already merged before. Then I had to take a look into the trunk’s log and find out up to which revision I wanted to merge now or what the current latest trunk revision was. After finding out these two revision numbers, I could do the merge, and commit changes with a comment like the one above.

Well, once one gets used to this procedure, it isn’t that time consuming… but, it is always error-prone because it depends on users obeying rules. If someone forgets adding the correct comment or writes wrong revision numbers into comments (typos…), it causes problems for the next merge…

After all, these are problems users shouldn’t have to care about. And that’s exactly where “merge tracking” helps.

First of all, merge tracking is one of those features of Subversion 1.5, which require a 1.5 client, 1.5 server and 1.5 repository. So, in order to benefit from this new feature, you have to upgrade your whole Subversion setup plus repository.

I’ll test merge tracking with a new local repository. Let’s do it step by step, so even if you don’t have experience with Subversion, you can follow these steps and try it out yourself. All you need is to have the Subversion client version 1.5 or higher installed.

Preparing a Test Environment

I go to a test directory “mytest/svn” and create a repository in directory “repo”. Then I checkout this new repository into a working copy called “trunk”.

mbp:svn gerhard$ svnadmin create repo
mbp:svn gerhard$ svn co file:///Users/gerhard/mytest/svn/repo trunk
Checked out revision 0.
mbp:svn gerhard$ cd trunk
mbp:trunk gerhard$ ls -al
total 0
drwxr-xr-x 3 gerhard gerhard 102 6 Nov 22:33 .
drwxr-x--- 11 gerhard gerhard 374 6 Nov 22:33 ..
drwxr-xr-x 8 gerhard gerhard 272 6 Nov 22:33 .svn

As there is really nothing in a new repository, let’s create basic directories, which should exist in every Subversion repository, add them to version control and commit them.

mbp:trunk gerhard$ mkdir trunk
mbp:trunk gerhard$ mkdir branches
mbp:trunk gerhard$ svn add trunk
A trunk
mbp:trunk gerhard$ svn add branches
A branches
mbp:trunk gerhard$ svn commit -m "initialization of repository"
Adding branches
Adding trunk
Committed revision 1.

Next, I switch the working copy to folder trunk, that’s where I actually wanted to be…

mbp:trunk gerhard$ svn switch file:///Users/gerhard/mytest/svn/repo/trunk
D trunk
D branches
Updated to revision 1.

Now I create two simple text files for testing. I name them “file1.txt” and “file2.txt” and add some lines of text to each of them.

mbp:trunk gerhard$ svn add file1.txt file2.txt
A file1.txt
A file2.txt
mbp:trunk gerhard$ svn commit -m "added file1.txt and file2.txt with some lines of text"
Adding file1.txt
Adding file2.txt
Transmitting file data ..
Committed revision 2.

So, that’s a good base to start doing some tests.

Creating a Branch and Merging Data

The diagram below shows what I’m planning to do now: create a branch (step 1), keep it up-to-date by merging changes from the trunk (steps 2 and 3) and finally merge the branch’s changes back into the trunk (step 4).

Branching and Merging

Branching and Merging

The first step is to create a branch. I create it under “branches” and name it “b1”.

mbp:trunk gerhard$ svn copy file:///Users/gerhard/mytest/svn/repo/trunk file:///Users/gerhard/mytest/svn/repo/branches/b1 -m "created branch b1 from trunk r2"
Committed revision 3.

Then I checkout the branch to a new local working copy in directory “b1”, add a line to file1.txt and commit changes.

mbp:trunk gerhard$ cd ..
mbp:svn gerhard$ svn co file:///Users/gerhard/mytest/svn/repo/branches/b1 b1
A b1/file2.txt
A b1/file1.txt
Checked out revision 3.
mbp:svn gerhard$ cd b1
mbp:b1 gerhard$ vi file1.txt
mbp:b1 gerhard$ svn commit -m "added a line to file1.txt"
Sending file1.txt
Transmitting file data .
Committed revision 4.

Now I go back to my trunk working copy and change file file1.txt, too. This time I add a line at the beginning of the file, so I will not have conflicts when merging later on.

mbp:trunk gerhard$ vi file1.txt
mbp:trunk gerhard$ svn commit -m "added a line to file1.txt"
Sending file1.txt
Transmitting file data .
Committed revision 5.

Afterwards, I merge changes from trunk to branch b1. At this point “merge tracking” can be used for the first time. Subversion knows that branch b1 has been created from trunk’s revision 2 and that there hasn’t been any merges performed so far. So, all I have to do here is to call “svn merge” and add the trunk’s path as parameter.

mbp:b1 gerhard$ svn merge file:///Users/gerhard/mytest/svn/repo/trunk
--- Merging r3 through r5 into '.':
U file1.txt
mbp:b1 gerhard$ svn status
M .
M file1.txt

Now that’s easy… works great. Note: Subversion changed the base directory “.”, too. It added metadata in a property called “svn:mergeinfo” for merge tracking. “svn diff” shows that this new property has been added to my base directory. It stores merge information in a readable form.

mbp:b1 gerhard$ svn diff
Property changes on: .
___________________________________________________________________
Added: svn:mergeinfo
Merged /trunk:r3-5

Next step: I commit the merged data to branch “b1”, go back to the trunk, change files again, and perform another merge to branch “b1”. Let’s see what the “svn:mergeinfo” property looks like afterwards.

mbp:b1 gerhard$ svn commit -m "merged latest changes from trunk"
Sending .
Sending file1.txt
Transmitting file data .
Committed revision 6.
mbp:b1 gerhard$ cd ../trunk
mbp:trunk gerhard$ vi file2.txt
mbp:trunk gerhard$ svn commit -m "added text to file2.txt"
Sending file2.txt
Transmitting file data .
Committed revision 7.
mbp:trunk gerhard$ cd ../b1
mbp:b1 gerhard$ svn update
At revision 7.
mbp:b1 gerhard$ svn merge file:///Users/gerhard/mytest/svn/repo/trunk
--- Merging r6 through r7 into '.':
U file2.txt
mbp:b1 gerhard$ svn commit -m "merged latest changes from trunk"
Sending .
Sending file2.txt
Transmitting file data .
Committed revision 8.
mbp:b1 gerhard$ svn propget svn:mergeinfo
/trunk:3-7

Well, works as expected. Subversion knew, that only revisions 6 and 7 had to be merged into branch “b1”. The property “svn:mergeinfo” has been updated and now shows that revisions 3 throughout 7 have been merged.

I change “file1.txt” in branch “b1” again and then merge data back from branch “b1” into the trunk. At this point it’s important to use the “–reintegrate” option of “svn merge”. Merging a branches work back into the trunk works just one time (with this method, at least).

mbp:b1 gerhard$ vi file1.txt
mbp:b1 gerhard$ svn commit -m "changed some text in file1.txt"
Sending file1.txt
Transmitting file data .
Committed revision 9.
mbp:b1 gerhard$ cd ../trunk
mbp:trunk gerhard$ svn update
At revision 9.
mbp:trunk gerhard$ svn merge --reintegrate file:///Users/gerhard/mytest/svn/repo/branches/b1
--- Merging differences between repository URLs into '.':
U file1.txt
U .
mbp:trunk gerhard$ svn commit -m "merged branch b1 back into trunk"
Sending .
Sending file1.txt
Transmitting file data .
Committed revision 10.

Finally, branch “b1” can be deleted, as no more changes should be done there. This way it will not distract anyone (history is not lost, of course).

mbp:trunk gerhard$ svn delete file:///Users/gerhard/mytest/svn/repo/branches/b1 -m "removed branch b1"
Committed revision 11.

This was a simple example of how to create a branch, keep it up-to-date and finally reintegrate it into the trunk. With Subversion 1.5 these typical steps became much easier and safer.

For further reading, I recommend the Subversion Book’s chapter Branching and Merging.

One Response to “Subversion 1.5 – Merge Tracking”

  1. Nice post u have here 😀 Added to my RSS reader