At work we use svn, with svnmerge for managing branches (most projects tend to use a trunk with a stable branch or two). I’m pretty much a svnmerge newbie, so every now and then I mess up one of my trees. When that happens, I’m lucky enough to have a fair share of total svn experts around to help me figure out what happened.
So here’s a little test scenario that shows what kind of merge conflict you tend to get when you cherry-pick a change to merge to stable, out of chronological order of change to the trunk, and then later you merge an older change that has a conflicting line in it:
$ cd /tmp
$ mkdir svnmergetest
$ cd svnmergetest
$ mkdir repo
$ svnadmin create /tmp/svnmergetest/repo
$ svn co file:///tmp/svnmergetest/repo co
Checked out revision 0.
$ cd co
$ svn mkdir trunk
A
$ cd trunk
$ cat > A <<END
> line ASTART
> line A1
> line A2
> line AEND
> END
$ svn add A
A A
$ svn commit -m "Init trunk"
Adding
Adding trunk/A
Transmitting file data .
Committed revision 1.
$ cat > A <<END
> line ASTART
> line A1
> line A3
> line AEND
> END
$ svn diff A
Index: A
===================================================================
--- A (revision 1)
+++ A (working copy)
@@ -1,4 +1,4 @@
line ASTART
line A1
- line A2
+ line A3
line AEND
$ svn commit -m "commit 1"
Sending trunk/A
Transmitting file data .
Committed revision 2.
$ cat > A <<END
> line ASTART
> line A1
> line A4
> line AEND
> END
$ svn diff A
Index: A
===================================================================
--- A (revision 2)
+++ A (working copy)
@@ -1,4 +1,4 @@
line ASTART
line A1
- line A3
+ line A4
line AEND
$ svn commit -m "commit 2"
Sending trunk/A
Transmitting file data .
Committed revision 3.
$ cd ..
# FWIW, I messed up commit in 4, and 5, commit 6 is to un-do the mess-up
$ svn cp -m "Init stable" -r 1 file:///tmp/svnmergetest/repo/trunk \
> file:///tmp/svnmergetest/repo/stable
Committed revision 7.
$ svn up
A stable
A stable/A
Updated to revision 7.
$ cd stable
$ cat A
line ASTART
line A1
line A2
line AEND
$ svnmerge.py init
property 'svnmerge-integrated' set on '.'
$ svn commit -F svnmerge-commit-message.txt
Sending
Committed revision 8.
$ rm svnmerge-commit-message.txt
$ svnmerge.py avail
2-3
$ svnmerge.py merge 3
svnmerge: "3" is not a subversion working directory
$ svnmerge.py merge -r 3
C A
property 'svnmerge-integrated' set on '.'
$ cat A
line ASTART
line A1
<<<<<<>>>>>> .merge-right.r3
line AEND
$ cat > A <<END
> line ASTART
> line A1
> line A4
> line AEND
> END
$ svn resolved A
Resolved conflicted state of 'A'
$ svn diff
Property changes on: .
___________________________________________________________________
Name: svnmerge-integrated
- /trunk:1
+ /trunk:1,3
Index: A
===================================================================
--- A (revision 7)
+++ A (working copy)
@@ -1,4 +1,4 @@
line ASTART
line A1
- line A2
+ line A4
line AEND
$ svn commit -m "Hand-resolve merge conflict"
Sending
Sending stable/A
Transmitting file data .
Committed revision 9.
$ svnmerge.py avail
2
svnmerge.py merge -r 2
C A
property 'svnmerge-integrated' set on '.'
$ cat A
line ASTART
line A1
<<<<<<>>>>>> .merge-right.r2
line AEND
$ cat > A <<END
> line ASTART
> line A1
> line A4
> line AEND
> END
$ svn resolved A
Resolved conflicted state of 'A'
$ svn diff
Property changes on: .
___________________________________________________________________
Name: svnmerge-integrated
- /trunk:1,3
+ /trunk:1-3
$ svn commit -m "Hand-resolve merge conflict"
Sending
Committed revision 10.
Justin tells me there’s very little in terms of handy fancy dandy software tooling that magically does the ‘right thing’ here without a human’s intervention. The main reason for that is that there is, fundamentally, no ‘right thing’.
So it’s possible to resolve this cleanly, and svn + svnmerge make it pretty clear what is going on, but it’s still a bit of work to figure out what to do. To stay out of trouble, it’s a safe bet that you should limit cherry picking as much as possible, and try and do merges between branches in chronological order whenever you can.