Subversion branching with less Pain™ 03 Aug 2007
slide software development

No matter how good of a source control system you use, branching can always cause loads of problems, and even painful merge days. At Slide, and other companies I've worked with that use Subversion, branching has been particularly painful because Subversion just doesn't maintain branch history like some commercial source control systems (like Perforce), in fact "branching" is quite literally making a copy of the trunk in Subversion.

Depending on the lifespan of a particular branch, and how often you refresh your branch from the mainline branch, merging back down to the main branch can go from bad, to even worse. Interestingly enough, running an `svn diff $BRANCH $MAIN` will give you the correct changes on the appropriate files that have been modified in the branch, where as running an `svn merge -r $START:$END $BRANCH ./` (whereas ./ is your working copy of main) can break in tremendously painful ways causing mis-merges and unintended rollbacks of previous changesets.

While merging down to the main branch today I decided to mix and match both svn diff and svn merge such that I would only merge changes down to the main branch that had been modified in my development branch, ensuring that nothing was changed on the main/stable branch that wasn't intended. The end result was a Python script that would execute the appropriate commands and merge the files one by one from one branch to the other, allowing the developer or QA engineer to check each file before commencing the merge.

After some serious tweaking and a couple of test branches in Slide's Subversion repository, was born. The script is a bit hackish right now in that it executes svn(1) instead of using Py-Subversion bindings (which haven't ever worked the way I had hoped). There is definitely room for improvement as well, but the basic flow is there such that will diff the two branches and aggregate a list of files that have been modified since the branch was originally cut from the main/stable branch, then iterate through the file list and either merge (if the file has been edited) or copy (if the file has been added) to the main branch as is necessary.

The script should always be run from the base directory of your working copy of the mainline branch, so if your main working copy is in /home/tyler/slide/main, this script could be run from that directory like:
python ~/scripts/ -h
In general I think the script is easy to use, but I also wrote it so I'm open to suggestions for improvement or ideas on how to more efficiently merge branches together with Subversion.

You can check the code out with:
svn co svn://

ccnet% python qa/svnutil/ -h

The merge-safe script should help you, the lowly startup employee
more effectively merge one branch to another by examining which files have changed, and merge/copy those to the destination branch.

Do a dry-run of merging from $SRC to $DST where r1002 is the starting branch of $SRC and r1050 is the last revision to merge from $SRC

%> python some/dir/ -s $SRC -d $DST -r 1002:1050 --dry-run

Do an interactive merge from $SRC to $DST
%> python some/dir/ -s $SRC -d $DST -r 1002:1050 -i

Usage: $prog [options]

-h, --help show this help message and exit
-s SOURCE, --source=SOURCE
The source branch to merge from
-d DEST, --dest=DEST The destination branch to merge to
-i, --interactive Enable merging interactively on each file
--dry-run Run with --dry-run enabled
-r REVISION, --revision=REVISION
Specify the revisions separated by a colon (i.e. -r