First a quick lesson on how merging works and works for almost all version control systems.
The standard Merge is a three way merge. In this, you're comparing the two versions with the last common ancestor of the two versions. For example, I branch from trunk. The version on the trunk before the branch is the last common ancestor.
The purpose is to be able to see the changes on one stream vs. the changes on the other stream. (A stream could be a specific branch or the trunk). For example, If I'm merging from trunk to the branch, I want to make sure I don't overwrite my specific branch changes with those lines on the trunk.
The comparison with the last common ancestor allows you to see only the changes that took place on the trunk since that branch point.
This presents a problem when I go backwards from my previous merges. For example, I am finished with my feature branch, and want all of the feature changes back on trunk. This is a problem because I've merged all of my trunk changes onto the branch. That means my branch's stream shows that I made changes on the branch, and these should be merged into trunk. But, these changes are already on the trunk! To handle this issue, you can force a two-way merge. In this case, you're only comparing the heads of the two separate streams with each other and copying all of the difference from one stream to another.
Now how Subversion handles merges:
First, Subversion loves cherry pick mergin. Subversion allows you to specify which revision you want to merge. For example, I have a branch, found a bug and fixed it on the branch. I can now merge that particular revision or set of revisions that contain my bug fix. When you cherry pick in Subversion, you are doing a three way merge, but you are only considering the changes represented in those particular cherry picked versions and not all changes on either the trunk or branch.
In fact, Subversion almost always does cherry pick merges even if you don't specify cherry picked revisions. Subversion tracks what revisions were merged via the svn:mergeinfo
property. Let's say I branched at revision 100, and now I'm merging my trunk changes into my branch. The last revision on trunk is now revision 150. Subversion considers you're doing a cherry pick of all the versions on trunk from revision 100 (but not including version 100) to revision 150. Next time I do a merge from my trunk to my branch (let's say the last change on my trunk is now 175), Subversion will cherry pick changes on trunk since (but not including 150) to revision 175.
Note that when I specify cherry pick revisions for the merge, I am allowed to go back and forth between my branch and my trunk without any problems. If I merge from my feature branch to my trunk, I could specify the revisions on my feature branch where I implemented the feature and skip the changes on my feature branch that were the results of merge from trunk.
The problem, only comes when I let Subversion handle the cherry picking. When I merge from my trunk to my branch, Subversion tracks the revisions of my trunk that are on my branch. However, Subversion has no way of knowing which revisions on my feature branch were the results of my trunk merges.
Thus, when I merge my feature branch back into trunk, Subversion will consider all unmerged revisions -- including those revisions that were the result of trunk to branch merges, and tries to merge all of those revisions back into my trunk.
To handle this, Subversion has a special merge called a reintegration merge. I merge my last set of changes on my trunk into my branch, then when I merge my branch into my trunk, I want my trunk and branch to be identical. Thus, Subversion wants to do a two-way merge and make trunk and the branch match.
In older versions of Subversion, I would have to manually specify that I am doing this two-way merge with the --reintegration
parameter on the svn merge
command.
Subversion's merge tracking also lead to an interesting problem with reusing a feature branch. Let's say I do my last set of merges from trunk to my branch. The last revision on trunk and the last revision in Subversion was revision 100. When I merge my changes from the trunk onto my branch, I now create revision 101 on my branch. The svn:mergeinfo
property on that branch shows that I've merged all of my changes up to revision 100 from trunk only my branch.
Now, I do my reintegration merge, commit the changes, and create revision 102 on my trunk.
Now, I do more work on my trunk, (let's say revision 103) and I want to merge those changes onto my feature branch.
What was that last revision I merged from trunk to my feature branch? Looking at svn:mergeinfo
, I see it's revision 100. And since that last merge from trunk to branch, there are now two new revisions on trunk that have not yet been merged into my feature branch: Revision 102 and Revision 103.
So, Subversion will do it's cherry picking and attempt to merge the changes contained in revisions 102 and revision 103 onto my feature branch. But wait... Revision 102 are all of my feature branch changes being merged onto trunk! I'm going to be reapplying those feature changes onto my feature branch!
There are two ways to handle this: Method One: Once you do a reintegration merge, you should never ever use that feature branch again. Delete it. Lock it away in a nunnery. Never speak its name again. If I need that feature branch once more, I should create a new feature branch from trunk.
The other way is to munge svn:mergeinfo
on the feature branch to make Subversion know that revision 102 on trunk has already been merged into my feature branch. You can do this with the --record-only
parameter:
$ svn co $REPO/branches/feature/myproj
$ cd myproj
$ svn merge --record-only -r102 $REPO/trunk/myproj
As you can see, all of this manual effort -- knowing when to use that --reintegration
switch and understanding what happens when you do a reintegration merge -- caused confusion. Therefore, newer versions of Subversion (I believe since revision 1.8) now attempt to handle this for you.
If you're using Subversion 1.8 as a client, you never have to specify the --reintegration
parameter. Subversion will programmatically determine whether you are doing a typical three way merge or a reintegration merge and act accordingly.
If I attempt to do what Subversion considers a reintegration merge, and I have not yet merged all of my trunk revisions into my feature branch, Subversion will warn me and not let me do the merge. If I reuse a feature branch once I do my reintegration merge, Subversion is suppose to detect this and automatically handle the issue of branch reuse after reintegration. (Sometimes it does't quite work. However, Subversion won't let you reuse the feature branch after a reintegration merge, but might give you an error and you may have to manually do the --record-only
merge.)
So in Subversion, how should you do your workflow?
Unlike Git where each bug and feature should be on its own branch least you need to merge that bugfix or feature onto multiple branches, you an do almost all of your work right off the trunk (or whatever branch you like). In most Subversion shops, branching is only done for a candidate release. That is, I'm about to do a release, I branch for that release. Some developers work on the release, others continue their work on trunk.
If a bug is found on trunk, it can be fixed on trunk, and that revision or set of revisions where that bug was fixed can be merged onto the release branch. Or, if you find a bug on the feature branch that needs to be fixed on the trunk, you can fix that bug on the feature branch and merge that revision or set of revisions onto trunk.
It's simple, straight forward, and easy to implement.
This doesn't mean you can't branch for a feature. In fact, sometimes you have to. Imagine a feature that's being implemented deep into the future. You don't want those changes on trunk. In that case, create the feature branch, and merge your trunk changes regularly onto that feature branch. Just remember that there may be issues when you merge that feature back into trunk. As long as you understand what's going on. It's no problem.
And, if you want to go hog wild and create a gazillion branches for each feature and bug, you can do that too. However, I recommend you use Subversion 1.8 which will do that for you.