UPDATE: SVN 1.5 gets rid of the need for this. I haven’t actually experimented much with it yet, but this article explains reintegration. Much simpler than my method. Hopefully it works
Since I started working at Mailtrust Subversion and Trac have become my best friends. Due to the intense usage, I have figured out a couple tricks. One of the biggest ones is how to properly do bidirectional merging.
Subversion works great for small applications, but once an application grows large enough to need many different branches with many different developers, it can become very cumbersome to use. The main problem I faced was merging branches back into the trunk whenever they are ready. I have tried 3 different options that I will discuss here.
Merging by range
Merging by range is the traditional merge technique most subversion users learn.
svn merge [Parent URL] [Child Path] -r [Last Merge Rev]:HEAD
This is how you should merge from the parent into the child branches, but it doesn’t work very well for merging children back into the parent. There are two main problems using this method to merge up:
Binary files will always conflict
There is an issue where anything added in a merge down from the parent, then removed in the child, will not be merged back up. This is a huge issue, since code will remain in the parent branch that doesn’t belong there
SVN Copy
SVN also supports copying files and folders just like a filesystem does. Using copy is usually how branches are created. It can also be used to move branches back to trunk. The only requirement is all changes in the parent have been merged down to the child.
svn merge [Parent Branch URL] [Child Branch URL] [Child Branch Path] -r [Last Merge Rev]:HEAD
svn commit [Child Branch Path]
svn rm [Parent Branch URL]
svn copy [Child Branch URL] [Parent Branch URL]
This method seems to always work, but it’s definitely not optimal. If you need to have the complete revision history of the parent branch, this method won’t work. When a copy occurs, the revision history for the parent is overwritten by the revision history of the child. Obviously, the revision history for the parent still exists in past revisions, it’s just a lot harder to find.
Difference merging
Difference merging is a perfect example of how to keep it simple. Difference merging isn’t discussed much, if at all, in Subversion documentation but it is a very powerful way to merge.
svn merge [Parent URL] [Child URL] [Child Path] -r [Last Merge Rev]:HEAD
svn commit [Child Branch Path]
svn merge [Parent URL] [Child URL] [Parent Path] --ignore-ancestry
svn commit [Parent Path]
In this example, the parent branch is merged into the child branch. What this means is that all changes in the parent are also in the child but not vice versa. With this knowledge, the child can then be merged into the parent by merging only the differences. The only important caveat is this operation MUST BE ATOMIC. The merge down then up must occur without any changes to the parent or child in between them.
Another thing you might notice is the ignore-ancestry flag. This tells the SVN to merge entirely based on the current state of the files, and not their ancestry.