Skip to content

cherry-picking changesets is hard but possible

November 21, 2007

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.

Advertisements

Comments are closed.

%d bloggers like this: