Question

I have a branch A started from trunk and a branch B started from A. Periodically, a merge from trunk to A is made and later a merge from A to B is also performed. When I want to merge A and B to trunk, I think to follow this way:

  • reintegrate merge from B to A
  • reintegrate merge from A to trunk

Is it correct? Or can it cause problem to mergeinfo or other problems? What is the best practice in this case?

Was it helpful?

Solution

Subversion has been (sometimes unfairly) criticized for bad merging. In truth, Subversion is really good at merging. To understand what you need to do, you need to understand how Subversion does merging.

Subversion normally, sort of (which I'll explain later) does a standard 3-way merge. It compares the two files being merged to the last ancestor those two files shared. That way, it can tell what changes in the file came from the branch it is on, and what changes came from the other branch.

Because Branch "B" comes from Branch "A" which came from trunk. You can merge from trunk to B or trunk to A. These merges should work just fine. If you are merging a particular set of changes from wither Branch B or Branch A to trunk or to the other branch, everything should work just fine.

Now, I said (sort of) does a standard 3-way merge. Subversion was written to allow you to specify specific set of changes you made in one branch to merge with the other. When you merge, Subversion doesn't look at the file you're merging, it starts with the base, the last common ancestor, then applies those changes that are being considered for the merge. Under these circumstances, when you specify exactly what you want to merge, Subversion does a wonderful job of merging.

Therefore, if you are specifying that Branch "A" has a particular change and you want to merge this change to Branch "B" or to the trunk, Subversion does a wonderful job at merging.

Things started changing in Subversion 1.5 when Subversion started tracking repository revisions that were previously merged into a branch or trunk. This prevented me from merging changes previously merged into the branch, but it also allowed me to be lazy. There's nothing wrong with being lazy. I highly endorse it.

Imagine I make a feature branch at revision 100 from trunk. I make the branch at revision 100. Let's say this is a list of the revisions made in my repo:

  • Branch: 103 105 108
  • Trunk: 101 102 104 105 106 107

When I merge from trunk to my feature branch, and I don't specify what revisions I want to merge, Subversion does the cherry picking for me. It knows the last common ancestor was revision 100. It then looks and sees that the changes on trunk of revision 101, 102, 104, 105, 106, and 107 have not been applied to my branch. It then cherry picks these specific changes and merges them to my branch.

  • Branch: 103 105 108 109
  • Trunk: 101 102 104 105 106 107

Revision 109 is the result of my merge. To track the changes, Subversion puts onto Revision 109 a svn:mergeinfo property that says trunk:101-107 have been merged onto the branch:. Lets do more work:

  • Branch: 103 105 108 109 110 111
  • Trunk: 101 102 104 105 106 107 112 113

I want to merge from my trunk onto my branch again. If I wasn't lazy, I would specify I want revisions 112 and 113 merged onto my branch. However, being lazy, I let Subversion do the work. Subversion sees from svn:mergeinfo that all trunk revisions from 101 to 107 have already been merged into my branch. Therefore, Subversion picks revisions 112 and 113 for me:

  • Branch: 103 105 108 109 110 111 114
  • Trunk: 101 102 104 105 106 107 112 113

And Subversion creates revision 114, and sets svn:mergeinfo to say that all changes on trunk from revision 101 to revision 113 have been merged onto my branch.

Now, I have finished my feature, and I want to merge all of my changes I made on my branch back onto my trunk. If I was not such a lazy bum, I would specify that I want revisions 103, 105, 108, 110, and 111 to be merged onto my trunk. If I did this, there's absolutely no problem at all. Subversion does a wonderful job with the merge.

Note I didn't specify that I wanted changes 109 and 114 merged (the two changes in bold). Why? Because those weren't changes on my branch, they were trunk changes I merged onto my branch. If I specified those two revisions, I would be reapplying my trunk changes back onto my trunk.

But, I am lazy, and I want Subversion to do the work for me. Subversion looks at my branch, and sees all of those changes, including change 109 and 114, and wants to merge those onto my trunk. Not good.

So, Subversion does something special here. If we think of a file and the revisions as change sets to apply to a file, I am basically applying all the changes that took place on my trunk onto my branch and all those changes on my branch onto my trunk. In other words, when I finish this branch to trunk merge, my branch and my trunk will match.

This is what a reintegration merge does. Unlike the normal merge where changes from my two files are compared to a base revision and then carefully applied, all changes on my branch will be applied to my trunk without comparing it to the base revision. The reintegration merge is a two way merge. Any difference between the branch and the trunk will be overwritten.

Let's look at what happens:

Before Reintegration Merge

  • Branch: 103 105 108 109 110 111 114

  • Trunk: 101 102 104 105 106 107 112 113

  • svn:mergeinfo on branch now says trunk:101-113

After Reintegration Merge

  • Branch: 103 105 108 109 110 111 114

  • Trunk: 101 102 104 105 106 107 112 113 115

  • svn:mergeinfo on branch now says trunk:101-113

  • svn:mergeinfo on trunk now says branch:103-114

Let's do more work on trunk just to show you what happens if I continue to use my feature branch. I'll put two more changes onto trunk:

  • Branch: 103 105 108 109 110 111 114

  • Trunk: 101 102 104 105 106 107 112 113 115 116 117

  • svn:mergeinfo on branch now says trunk:101-113

  • svn:mergeinfo on trunk now says branch:103-114

Now, I want to merge these changes back to my feature branch. If I did the cherry picking myself, I would specify I want revisions 116 and 117 onto my branch because those were the two changes I just made. Again, if I was studious and hard working, Subversion merging would be fine. However, I'm lazy, and will let Subversion do the cherry picking.

Subversion looks at svn:mergeinfo and sees that I've applied changes 103 to 114 onto my branch. It now sees three new changes on my trunk: changes 115, 116, and 117. However, Change 115 is the result of my reintegration merge! All of the changes in 115 are already on the branch. Letting Subversion do the picking will lead to disaster.

This is why you're told not to reuse a branch once you reintegrated it. The way around this is to do a svn merge --record-only -c 115 from trunk onto my branch. No merge will actually take place, but now Subversion will record that changes 101 to 115 are on my branch. If I did that, and then did a merge from trunk to branch, Subversion would skip over selecting change 115 and only do changes 116 and 117.


Now that you understand how Subversion does merge tracking and what a reintegration merge is, we can look at your situation:

  • You branched from trunk to Branch A
  • You branched from Branch A to Branch B
  • You merge from Trunk into Branch A
  • You merge from Branch A into Branch B

If you are a hardworking young programmer who wakes up every morning at 5am to run 5 miles just to get you into the mood for work, you will specify what revisions from each branch you want to merge into the other branches or trunk. If you did this, there is no problem at all, you do the standard merge, and get ready for your daily 100 kilometer bike ride you do after a vigorous workout session at the gym.

If you are a typical technical worker (i.e., lazy) and you want Subversion to handle the selecting of the revisions for you, things are a bit different:

  • You merge all of your changes from Branch A into Branch B. Now, your branch B will include all revision on Branch A.
  • You do a reintegration merge of branch B back into Branch A. Now all of your changes on Branch A and Branch B will be on both branches.
  • You do a regular merge of your trunk to Branch A. Branch A contains all of your changes you've made on Branch A. Plus, all of the changes on Branch B and trunk.
  • You do a reintegration merge of Branch A (which already contains your Branch B changes) onto trunk. Now Trunk, Branch A, and Branch B all contain the same set of changes, and all three should match.
  • You should now never use Branch A or Branch B again unless you do a svn merge --record-only to record that Branch B to Branch A reintegration change onto Branch B and that Branch A to trunk reintegration change onto Branch A.

Important Change in Subversion 1.8

Knowing whether to do a regular merge or a reintegration merge in Subversion can be a bit error prone. It's a manual process and something that could be a disaster if I, being a normal human being, make a mistake (which I will do at the very worst time).

In Subversion 1.8, Subversion now tries to determine by looking at svn:mergeinfo which way you do your merging and when you have to do a reintegration merge. Thus, you no longer have to specify the --reintegration flag on merging. Subversion will also make sure that if you reuse a branch after it was reintegrated into another branch or trunk, not to reapply the changes that took place during reintegration.

If you like to do a lot of feature branching, you should use Subversion 1.8 which will handle a lot of the error proneness in merge tracking for you. If you are using Subversion 1.8, you should be able to do merges from Branch B to Branch A and into trunk without any problems.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top