Merge Assistance using Subversion

Until I no longer have to work with Subversion (or subsequent releases of Subversion improve), dealing with merges will remain a hassle. Oh, branching is super easy (just a simple server side copy command) but for those branches to be worth anything you need to be able to merge efficiently.

Yes, I worked with the svnmerge.py hack for a couple of months, but it was introducing strange merge bugs and just generally left me uncomfortable trusting to do all its magic (relying on certain property values to guide its merge operations).

In light of going back to the old style svn merge, where I relied on comments to tell me the last merge point, I wanted to make sure that the log messages were standardized. Without some tool to help me do this, I was spending most of the merge time formatting comments and they ended up not being a reliable standard.

Taking advantage of the ability to output Subversion log messages into xml, I wrote a quick python script to generate single line summaries of the revision history:

# slfmt.py LOGFILE
from xml.dom.minidom import parse
import sys

doc = parse(sys.argv[1])

revisions = doc.getElementsByTagName('logentry')

for rev in revisions:
    r = rev.getAttribute('revision')
    try:
        msg = rev.getElementsByTagName('msg')[0].childNodes[0].data.split('\n')[0]
        print 'r%s - %s' % (r, msg)
    except IndexError:
        print 'r%s - NO LOG MESSAGE' % r

Then I wrapped up several other commands I find myself constantly running into a single bash script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash
# prepmerge REV BRANCH
REPO_ROOT=REPLACE_WITH_YOURS

svn merge -r$1:HEAD $REPO_ROOT/$2
echo "Merged r$1:HEAD from $2 into "`svn info | grep URL | awk '{ print $2 }'` > .merge-comment
echo "" >> .merge-comment
echo "  svn info:" >> .merge-comment
svn info $REPO_ROOT/$2 | awk '{ print "\t\t"$0 }' >> .merge-comment
echo "  change log:" >> .merge-comment
svn log --xml -r$1:HEAD $REPO_ROOT/$2 > .log.xml
python ~/bin/slfmt.py .log.xml | awk '{ print "\t\t"$0 }' >> .merge-comment
echo "" >> .merge-comment
echo "  files:" >> .merge-comment
svn st | grep -v "?" | awk '{ print "\t\t"$0 }' >> .merge-comment

With these two scripts in my ~/bin directory and assuming that my repo follows the standard /trunk, /branches, /tags, model, I can now do merges in three simple steps. Assuming you would like to merge changes from a branch called experimental from HEAD into trunk, you would need a local working copy of trunk and be in the the root of that local path and then:

  • ~/bin/prepmerge HEAD branches/experimental
  • review changes, resolve conflicts
  • svn commit -F .merge-comment

That's it. I am sure there are better/cleaner methods to this, but this now allows me to focus solely on resolving conflicts that emerge instead of fiddling with comments. Of course, if everything I worked on was on git, I wouldn't have to fool with these types of scripts.