For merging to work, you need two things:
- You need a common ancestry between the two branches.
- If you place on keeping the two branches, in sync, you need to do regular merging between the two. For example, I assume that all work on 3.1 should be in 3.2. As you do work in both branches, you should have been merging your 3.1 changes into 3.2.
Merge conflicts happen because merging is a three-way comparison. The two development streams (a development stream can refer to a branch or trunk) are compared to each other, and they're compared to their last common ancestor. Imagine a file where line #100 was changed in both development streams. A merge from one to the other will result in a conflict.
Subversion doesn't just diff files, it diffs directories too. If I rename a file in one development steam (or move it), and I do a merge, it will be renamed (or moved) in the other development stream. If I delete or add a file in one development stream, and merge, it will be deleted or added in the other development stream too.
In Subversion, a tree conflict can happen if I do two different things to the same file on the two development streams. For example, I rename the file on both the 3.1 and 3.2 branch. Even if I rename them the same name, Subversion will pick it up as a conflict.
To prevent your disaster from happening again: Make sure that your two branches do share the same ancestry. Why not branch 3.2 from the 3.1 branch, or at least reintegrate the 3.1 branch back into trunk before branching 3.2. Subversion usually is pretty good at following history, but you need to make sure that the branches share a common history.
The other is regular merging. You need to merge 3.1 to the 3.2 branch on a regular basis -- maybe even every day. This makes sure that people working on the 3.2 branch have the changes they need in 3.1 and aren't duplicating the work. Constant merging also keeps the changes small, and when there are merge conflicts they're much easier to handle. Remember the svn mergeinfo
command which can let you know what's already been merged into 3.2 and what on 3.1 still needs to be merged. You can always use the --record-only
parameter to prevent a particular revision from being considered for a future merge. For example, you fix a bug on both branches, you use --record-only
to make sure that the fix on 3.1 isn't done on 3.2. Or, if you made a change in 3.1 you don't want on 3.2, doing a --record-only
will prevent you from considering that change on the 3.2 branch the next merge.
What to do now? You need to untangle your work. Look at a svn log
between the two branches. If you see a bugfix taking place on both branches, use a svn merge --record-only
to let Subversion know that a particular change on the 3.1 branch was also done (albeit manually) on the 3.2 branch. Look at file moves and renames and see how they were done on each branch.
Once you've done that, merge changes on 3.1 to 3.2 a few revisions at a time, cherry pick merges. Make sure that a change on 3.1 is something you want on 3.2.
This is a long draw out process, but you it needs to be done. Use this as a lesson to improve your development process.
We use to use what sounds like a similar system you used. Trunk was suppose to be pristine and match our releases (it never really did). We kept losing changes from one release to another because of when we made a new branch and when we merged the previous branch to trunk (or we simply lost where we branched from).
Branching is like children: You make one, and you better be ready to take care of it and not lose track where it goes.
We switched to a continuous development process. We do our development on trunk, and then branch right before we do a release. The branch point usually comes around the time when we finished all of the features for that release, and we are now in a process of bug fixing.
The release is done on the branch. If we find a bug, we will fix the bug on the branch, and then only merge that revision to the trunk (if that bug is also on the trunk). Once a release is done, the branch is locked. If we need to do a hotfix, we can reuse that branch for the hotfix.
Occasionally, we have feature branches. When we do, I make sure that we are constantly merging from the development stream to that feature branch. This way, when we finally reintegrate that feature back into trunk, we have few if any conflicts.
By keeping branching to a minimum and by tracking where we branch from, and doing constant merges (like in a feature branch), we have reduced merge conflicts, and no longer have the issues that once plagued our releases.