Using Multiple Remote SVN Repositories with Apache

Subversion is a version control system, modeled after CVS, but has many useful features that make it a more improved form of CVS. I wanted to see how useful subverision would be for my purposes and so I decided on setting it up on my server.

Preliminaries

I chose to set it up so that it could be accessed remotely and for this type of approach there are two possibilities. The first uses svnserve which is a subversion server that speaks a custom protocol. The other option is to set up Apache to use WebDAV to provide access to the subversion repositories.

An important aspect of subversion repositories is that all projects under a given repository are simultaneously versioned. This means that if a repository has two projects A and B, then if some updates are made to the project A and then comitted the version of the repository itself goes up by 1. And this means that the version of B has also gone up by one, even if no modifications have been made to project B. One solution to this is that we make a repository contain only one project. This leads to a little bit more administration but it suits my purposes better. It does seem a little clunky, since I would like all my projects relating to papers be under the paper repository. But the repository wide versioning makes it a little unwieldy. One way to achieve this is to make the repository a subdirectory of the papers directory. So if your subversion repositories are stored at /home/svn then the project called paper1 would be stored in the repository called /home/svb/papers/paper1. That is, papers is not a repository, the repository is papers/paper1. I'm not sure if there is a more elegent alternative.

I set it up on a Fedora Core 3 server running Apache 2.0.52. The stock install doesn't include the mod_dav_svn module, so I obtained it using yum. Also the repositories need to have read/write permissions by the user that the web server runs under (apache in the case of FC3). As a result the initial setup of the repositories is done by root and then ownership is changed to apache.

Setting up Apache

The first step is to setup the URL mapping. My repositories are installed under /home/svn. So one of my repositories is /home/svn/papers/cnn. Thus I would need Apache to map a URL like http://blue.chem.psu.edu/svn/papers/cnn to /home/svn/papers/cnn. This achieved by editing etc/httpd/conf.d/subversion.conf as below
<Location /svn/papers/cnn>
   DAV svn
   SVNPath /home/svn/papers/cnn

   <LimitExcept GET PROPFIND OPTIONS REPORT>
      AuthType Basic
      AuthName "Authorization Realm"
      AuthUserFile /home/svnauthusers.txt
      Require valid-user
   </LimitExcept>
</Location> 
On a publicly accesible server security is important. Thus the LimitExcept clause specifies that only authorized users should be able to access this repository. The authorized users are specified in the password file whose path is provided. Make sure that this file is not present in a directory that is served by Apache and that it is readable by the apache user. Users can be added to the pass word file by usng htpasswd on the command line as
$: htpasswd -c /home/svnauthusers.txt rajarshi 
An important point is that if you have multiple repositories the above example requires that you need to to specify the above URL mapping for each repository that should be served. However if you all your repositories are under a specific directory in the filesystem (such as /home/svn/papers in the above example) there is an easier way:
<Location /svn/papers>
   DAV svn
   SVNParentPath /home/svn/papers

   <LimitExcept GET PROPFIND OPTIONS REPORT>
      AuthType Basic
      AuthName "Authorization Realm"
      AuthUserFile /home/svnauthusers.txt
      Require valid-user
   </LimitExcept>
</Location> 
By specifying SVNParentPath we shift the job of mapping repository URL's to filesystem paths onto the DAV modules. (Thanks to Jean Christophe Andre for pointing this out)

Creating repositories

At this point you'll need to be root. We first need to create the svn directory and then the repository directories. Finally, the repository should be created by subversion. The last step results in the setup of various files and directories for use by subversion.
$: su                
$: mkdir /home/svn
$: mkdir /home/svn/papers 
$: mkdir /home/svn/papers/cnn
$: svnadmin create /home/svn/papers/cnn
$: chown -R apache.apache /home/svn 
The last line is important since the web server must be able to read and write these directories. This means that whenever you add a new repository (usually as root since the apache user has no login shell on FC3) you'll need to change ownership.

An alternative to this approach is to have a seperate instance of Apache running under a different user, say, svn. Then any exploits that allow a person to become the original apache user would not affect the svn user. In addition by setting group membership, this would allow you to setup the directories without having to be root.

Importing a project

Since we've decided to set up subversion so that a single repository corresponds to a single project we can proceed as follows. Say your project is currently under a directory called ~/cnn. So we would do
$: cd ~/cnn
$: mkdir trunk tags branches 
and then move all the files for your project into trunk. Once thats done we can import the project by doing
$: svn import -m "First import of the project" . \
        http://blue.chem.psu.edu/svn/papers/cnn
You'll see some messages and then you've created the repository. A simple check would be to delete your current project directory and then do a check out
$: cd ~ && rm -rf ~/cnn
$: svn co http://blue.chem.psu.edu/svn/papers/cnn
The project will be created in a directory called cnn in your current working directory.

Ignoring files

The equivilant of the .cvsignore file, which lists filename patterns that should be ignored, is the global-ignores option that can be set in the users config file, located at ~/.subversion/config. By default this list contains the following patterns
*.o *.lo *.la #*# .*.rej *.rej .*~ *~ .#* .DS_Store
and other patterns that should be ignored can simply be appended. However, in case a specifc project requires that a certain type of file be ignored, this can be set for that project (in fact repository, but since we are working with 1 repository = 1 project this is the same thing) only by doing
$: svn propset svn:ignore "*.aux\n*.bbl" .
The pattern list is supposed to be a new line delimited list of patterns, hence the '\n'. Its also important to note that this sets the ignore property for the directory specified and is not recursive by default. The ignore property can be set recursively by adding -R to the command line.