Setting up a subversion mirror repository using svnsync

With the new version of subversion 1.4 you have a new tool called svnsync with which you can maintain mirror repositories quite easily. I’ve been working on one at work and would like to share with you my findings in case it interests anyone :-)

Understanding the problem

In order to have a mirror repository, it is important that commits only happen on the master and then they are synchronized to the mirror using the svnsync program. Then, the mirror repository can be used for whatever you may think of but for committing: backup, high speed checkouts, web front-ends, etc.

So, svnsync must be the only “one” able to commit to the repository. If we use the apache integration, there are various ways to do this. Let’s say we are using svn+ssh for authentication, in which case it is more complicated as ssh access usually grants writing access to the file system. So creating a different user is going to be handy.

Creating and populating the repository

So, let’s say that we created a user called svnsync on the target machine and that we are going to create a new subversion repository in its home directory:

[sourcecode lang=” bash”] svnadmin create /home/svnsync/svn [/sourcecode]

Now, we need to set up a hook to let svnsync change the properties. For this, we create /home/svnsync/svn/hooks/pre-revprop-change with:

#!/bin/sh
USER="$3"

if [ "$USER" = "svnsync" ]; then exit 0; fi

echo "Only the svnsync user can change revprops" >&2
exit 1

We will grant access to the user running svnsync on the main machine by copying its ssh key to .ssh/authorized_keys. And now, we only need to initialize the remote repository. Note that we can run svnsync from where ever we want, but for the sake of simplicity, we will run it on the main machine, where the original repository resides.

$ svnsync init --username svnsync \
      svn+ssh://svnsync@remote/home/svnsync/svn \
      file:///source-svn

Note:

  • The syntax is svnsync init DEST SOURCE

That’s it, the destination repository goes before the source repository.

  • There is no “:” between the host name and the path to the remote repository.

With this command, we will have initialized the destination repository and now we are ready to populate the destination repository. We can do it with this command:

svnsync synchronize --username svnsync \
       svn+ssh://svnsync@remote/home/svnsync/svn

And, as we already initialized the repository, there is no need to specify the source repository. This will take more or less time depending on how big you repository is and how fast your network connection is. Hopefully it will have finished after you take a coffee :-)

Creating another user to let users access the repository

So, we will now create an user called svn which will be used to access the repository using the subversion client. As we are using svn+ssh, all we need is to grant access to such user to all the users that have access to the main repository. If we are using ssh keys it’s as easy as copying all the allowed keys to the /home/svn/.ssh/authorized_keys file. Also, if we change the permissions to the repository at /home/svnsync/svn (and its parent) to be something like

drwxr-xr-x  7 svnsync users 4096 2006-11-28 17:30 svn/

we will let svnsync (and the svnsync user) be the owner and have write permissions to the repository and let svn (and all the users ssh’ing) have read access only to the repository (provided both belong to the users group).

$ svn co svn+ssh://svn@remote/home/svnsync/svn/test
A    test/trunk
[...]
A    test/trunk/readme
Checked out revision 2.
$ echo "test" >> test/trunk/readme
$ cd test/
$ svn -m "test commit" ci
Sending        trunk/readme
Transmitting file data .svn: Commit failed (details follow):
svn: Can't create directory '/home/svnsync/svn/db/transactions/2-1.txn':
Permission denied

And that’s all.

Commiting to the master respository

In case you want to commit back to the master respository, you need to do a “svn switch –relocate” to point to the master repository, but for that to work, it needs to have the same UUID if we don’t want it to fail.

  1. To get the UUID on the main machine:
svnadmin dump -r0 /source-svn | head -n 3 > saved-uuid
  1. Copy the file saved-uuid to the remote machine and do a
svnadmin load --force-uuid /home/svnsynd/svn < saved-uuid

So, thins to take into account:

  1. When populating the repository, we use the svnsync user who has write permissions to the repository (svn+ssh://svnsync@…)

  2. When checking out, we use the svn user (svn+ssh://svn@…)

Automatization

If we want to keep both repositories synchronized, we need to set up a couple of hooks on the source repository.

post-commit

Add this to such hook (it needs to be executable and not have the .tmpl extension)

# Propagate the data to the remote repository
/usr/local/bin/svnsync synchronize --username svnsync
        svn+ssh://svnsync@remote/home/svnsync/svn &

post-rev-changes

We also want the properties changes to be present at the remote repository:

# Propagating changes to the remote repository.
/usr/local/bin/svnsync copy-revprops --username svnsync
       svn+ssh://svnsync@remote/home/svnsync/svn $REV  &

Note that we put them to the background to let the user go on with what he was doing.

Final notes

This is a quick guide on how to set things to have a remote repository. There is much more than this and I recommend you to read the documentation and, obviously, do a backup. Doing a “svnadmin dump” only takes a while a it´s really worth it.

In any case, just let me know if you find any errors or typos.

comments powered by Disqus