Sunday, May 6, 2012

SVN -> Mercurial mirroring

I'm continuing to use libgdx for my game development.  However, locally I'm using Mercurial for software configuration while libgdx is currently stored up on GoogleCode using SVN.  Ugh.  How to stay in sync or otherwise mirror the SVN repo?  So far, I've been maintaining things manually.  The approach I take for pulling in SVN updates is outlined on the Selenic site here:

http://mercurial.selenic.com/wiki/SubversionToMercurialHowto

This boils down to keeping a single directory that serves both as a SVN *and* Hg working directory.  From this directory, I do a clone per project to set things up.  For example, I have a clone of libgdx for my MatchCard game, another clone for Doodle Bug, another clone for Pyromancer's Laboratory, and yet another one for DB42 and DB42Lite.

The nasty part of this business is that means I have to go to my local SVN repo, perform an SVN update, and then do an add/remove/commit on the Hg repo.  Since this is a manual operation, it doesn't happen all that often.  I'm between games right now and wanted to more or less set things straight in terms of keeping in sync without having to rely on a manual process.  Here's the solution I came up with:

1) I'm sticking with the shared SVN / Hg working directory.  This is really straightforward and I have my brain completely wrapped around what's going on.

2) I've got cygwin installed on my Win7 x64 workstation to give me all the great tools that Unix developers know and love... grep, head, tail, bash scripting, etc.

3) I've got Jenkins installed on this same machine.  This does periodic job runs that query the state of the SVN repo, pull in new updates, and then add/remove/commit those changes to the Hg side of the shared directory.  The build trigger is simply "Build Periodically" that runs the following script:

for ((COUNT=`svn log -r base:head | grep -E "^r[0-9].*" | wc -l`; COUNT > 1; COUNT-- ))
do
   GETREV=`svn log -r base:head -l 2 | grep -E "^r[0-9].*" | tail -n1`
   TOKENS=( $GETREV )
   REV=${TOKENS[0]}
   AUTHOR=${TOKENS[2]}
   COMMENT=`svn log -r $REV | tail -n +4 | head -n-1`
   CHECKINCOMMENT=$(echo -e "$COMMENT\nSVN $REV")
   svn update -r $REV
   hg addremove
   hg ci -u "$AUTHOR" -m "$CHECKINCOMMENT"
done
What this script does is read the number of changes between the local base and the GoogleCode head.  If it is > 1 (ie. there are new changes), it iteratively pulls each SVN changeset and then commits those to the Hg repo using SVN revision, author, and comment info pulled from SVN's log command.  This took a while to get going -- my bash scripting skills are extremely rusty.

The one thing I'm lacking at this point is an "hg push" up to a default repo out on the Intertubes.  I have it in mind to share the repo on Assembla, but I currently don't have the libgdx's team blessing to do this (yet) and I also have a support request into the Assembla team to see if they have any server side tools that might accomplish the same thing I'm doing locally.  This whole process is highly reliant on my build server not dying.  If I could push those operations out to some other server, that'd be better in the long run.  For now, though, it works

Hope this helps someone trying to accomplish something similar.