Mirroring a Google Code Subversion Repository for Trac

Notes on the mirroring of a Google Code Subversion repository to another server so I can run Trac on it.

Some of us working on the FBO/Helios project couldn't live with the ticket tracking and source browsing at Google Code, so I decided to set up a Trac instance for the project on my server at fruct.us. Problem is, Trac can't access remote repositories, so I had to mirror the one at Google over to fruct.us.

I looked at both svk and svnsync, and decided svnsync was the tool for the job. I created a repository at /home/svn/fbo-sync, set up the hook scripts, and ran svnsync as described in the SVN book at red-bean. The svnsync commands looked like this:

$ svnsync init file:///home/svn/fbo-sync http://fac-back-opac.googlecode.com/svn
$ svnsync sync file:///home/svn/fbo-sync

I watched as each revision from the repo at Google Code was checked into fbo-sync. To keep the two in sync, it would have been nice to add a post-commit hook to the Google Code repo that syncs the two whenever there's a commit. Since I don't have access to the hook scripts at Google, I set up a cron job on fruct.us to sync every five minutes:

# crontab for svn
# m h  dom mon dow   command
*/5 * * * * /usr/bin/svnsync --non-interactive sync file:///home/svn/fbo-sync

That's it for the syncing.

The trac setup was pretty standard. I pointed my trac install at the fbo-sync repo and it treated it as it would any other SVN repository. The tricky bit was getting the trac-post-commit-hook script to work. I put the shell script suggested in the comment section of that file (well, a slightly altered version -- see the ticket I filed) at /home/svn/fbo-sync/hooks/post-commit, made it executable, and ran a test commit with a refs #1 in the commit message. Nothing happened in the trac instance. Then I remembered to include the #!/bin/bash at the top of the hook script. I ran another test commit and it worked. I noticed, however, that the user "svn", under whom svnsync ran, was getting credit for the commit. I banged my head on the keyboard until I realized that svnsync makes the commit as "svn", then changes the revision properties to reflect the source repository. How could I tell the post-commit hook to wait until after the revision properties had been modified?

I couldn't, of course. A post-commit hook follows a commit, and must execute before the properties are modified. My first attempt at a solution was to remove the hook and instead parse the committed revision numbers from the output of the svnsync script. I replaced the svnsync line in svn's crontab with a pointer to the following script:

#!/bin/bash

SVN_OUT=`svnsync sync file:///home/svn/fbo-sync`

if [ -n "$SVN_OUT" ]; then
    REV=`echo $SVN_OUT | grep -o '[0-9]*' -m 1`
    echo 'trac-post-commit-hook processing ...'
    /home/svn/trac-post-commit-hook -p '/home/trac/sites/fbo' -r $REV
    echo 'done.'
fi

This worked, but I really don't like the thought of parsing output scripts as an API. So fragile. What if more than one commit occurs in a five-minute interval? I could have extended it to handle that case, but that way lies madness. Instead, I decided to use a post-revprop-change hook on the repository. I wrote the following, at /home/svn/fbo-sync/hooks/post-revprop-change, for testing:

#!/bin/bash

REPOS="$1"
REV="$2"
USER="$3"
PROPNAME="$4"
ACTION="$5"

echo "repos:" $REPOS "rev:" $REV "user:" $USER "propname:" $PROPNAME \
        "action:" $ACTION >> /home/svn/svnrevprop.log

svnrevprop.log looked like this:

repos: /home/svn/fbo-sync rev: 0 user: svn propname: svn:sync-lock action: A
repos: /home/svn/fbo-sync rev: 0 user: svn propname: svn:sync-lock action: D
repos: /home/svn/fbo-sync rev: 0 user: svn propname: svn:sync-lock action: A
repos: /home/svn/fbo-sync rev: 0 user: svn propname: svn:sync-currently-copying action: A
repos: /home/svn/fbo-sync rev: 96 user: svn propname: svn:log action: M
repos: /home/svn/fbo-sync rev: 96 user: svn propname: svn:author action: M
repos: /home/svn/fbo-sync rev: 96 user: svn propname: svn:date action: M
repos: /home/svn/fbo-sync rev: 0 user: svn propname: svn:sync-last-merged-rev action: M
repos: /home/svn/fbo-sync rev: 0 user: svn propname: svn:sync-currently-copying action: D
repos: /home/svn/fbo-sync rev: 0 user: svn propname: svn:sync-lock action: D

The first two lines of that example are the result of an svnsync without a revision update. The rest describe svnsync's processing of revision 96. Of importance is the order in which properties on that revision are modified. Further tests showed that they always occur in the same order (log, author, date). I replaced the post-revprop-change script above with the following:

#!/bin/bash

TRAC_ENV="/home/trac/sites/fbo"
REV="$2"
PROPNAME="$4"

if [ $PROPNAME = "svn:date" ]; then
    /usr/bin/python /home/svn/trac-post-commit-hook -p "$TRAC_ENV" -r "$REV"
fi

This script executes trac-post-commit-hook after each svn:date revprop modification. Because that is the last change svnsync makes on each revision, the trac instance is updated as expected, crediting the user who made the original commit to the repository at Google Code.

This is also fragile. If the order of the revprop changes were to switch, it would break. I'm trying to imagine what the ideal solution would be, but nothing comes to mind. In any case, the post-revprop-change script solves the problem at present.

p.s. At some point in my testing, I ran into the "Failed to get lock on destination repos" error after an aborted sync. I followed the advice in the "using svnsync" article at Paul's Journal and ran:

$ svn propdelete svn:sync-lock --revprop -r 0 file:///home/svn/fbo-sync

That worked for me as it did for him. Many thanks.

keywords: Google Code, Subversion, svn, Trac, mirroring, svnsync, post-commit hook created 2007-12-10 last modified 2010-09-13