Subversion

This page has details about the server-side installation of Subversion, and a few examples how to use Subversion on the command line. Another page on this wiki lists Subversion clients on Mac OS X.


Debian package

The following Debian packages need to be installed:

subversion-tools
cvs2svn
libapache2-svn
websvn


References

About the only thing needed is the excellent (!!!) reference called the "Subversion Book"

http://svnbook.red-bean.com/


Configuration

Command line

Create the base directory for all Subversion repositories:

mkdir /var/lib/subversion

Set the following permissions to allow WebDAV access through the Apache web server:

chown -R www-data /var/lib/subversion
chgrp -R www-data /var/lib/subversion


Run Subversion as daemon

The default port that the Subversion daemon listens on is TCP port 3690.

The daemon should be run with its own user, therefore

  • create a subversion user and group
  • make sure that the user is not allowed to log in

To run the daemon through inetd, add the following line to /etc/inetd.conf:

svn stream tcp nowait subversion /usr/bin/svnserve svnserve -i -R -r /var/lib/subversion

Notes:

  • make sure that the argument after nowait is the name of the system user created earlier
  • the -R option makes sure that the stand-alone Subversion server only has read access to repositories; this is necessary because the directory /var/lib/subversion is setup for WebDAV access


Integrate Subversion with Apache

Overview

The preferred way to access Subversion repositories is WebDAV, through the regular HTTP/HTTPS protocols running on ports 80/443. Although this may seem kludgy at first (as opposed to the regular approach of running a dedicated server daemon), it is soon revealed that the idea of using WebDAV was a stroke of genius by the Subversion designers because it circumvents so many problems caused by restrictive firewalls and proxies that would block the non-standard port used by a dedicated service daemon.

The drawback for those that still use Apache 1.3 is that they will have to upgrade to Apache 2 because the required Subversion module is only available for that newer version of Apache. The upgrading topic is covered (somewhat cursorily) elsewhere.


Modules

Both Apache2 modules mod_dav and mod_dav_svn need to be enabled

a2enmod mod_dav
a2enmod mod_dav_svn

Check which modules are enabled by looking at the contents of the directory

/etc/apache2/mods-enabled


General configuration

Basic configuration looks like this:

<Location /svn/>
  DAV svn
  # any "/svn/foo" URL will map to a repository /var/lib/subversion/foo
  SVNParentPath /var/lib/subversion
</Location>

These directives tell Apache that it should delegate all access to the location /svn to the DAV provider named "svn". Apache does not care how the DAV provider achieves this goal (the provider knows because of the SVNParentPath option, which is understood by the mod_dav_svn module), therefore it is not necessary to give Apache any information about where it can find the location /svn. The option Especially you must not say

Alias /svn/ /var/lib/subversion/

as would be necessary for a regular non-DAV access. Such an alias only causes an HTTP error 301 to be issued. For instance:

tharbad:~/Desktop/svn --> svn checkout http://osgiliath/svn/acexpander/
svn: PROPFIND Anfrage fehlgeschlagen auf '/svn/acexpander'
svn: PROPFIND von '/svn/acexpander': 301 Moved Permanently (http://osgiliath)

For more information, have a look at

http://subversion.tigris.org/faq.html#http-301-error


Access control: Authentication

The basic configuration presented above allows read and write access to all repositories, by all clients, without requiring any authentification. Most of the time this is not what you want. This chapter tells you how to force clients to authenticate so that they may gain write access to repositories. The next chapter talks about authorization, i.e. how to define what a client may (or may not) do once it has proven its identity.

Because access is by WebDAV and not regular HTTP, the usual approach of controlling access by saying "Require all granted" or "Require all denied" does not work.

Instead, we use the Apache directives <Limit> and <LimitExcept> to limit access to certain HTTP methods only. For instance, if the following directives are added to the Location block from the previous chapter, non-authenticated clients are forced to use only read-only HTTP methods (i.e. authentication is required for all HTTP methods except the listed ones):

<LimitExcept GET PROPFIND OPTIONS REPORT>
  Require valid-user
</LimitExcept>

Note: These statements are already present by default in /etc/apache2/mods-enabled/dav_svn.conf


Now that we know that authentication is required, we still need to define how authentication should occur. Our friend is the Apache directive <AuthType>. This directive tells Apache to issue a challenge when a client (i.e. a Web browser, or in our case any Subversion client such as TortoiseSVN or RapidSVN) tries to access a certain resource. We add the following directives to the Location block from the previous chapter:

AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /etc/apache2/dav_svn.htpasswd

This requires authentication in the "Basic" style, i.e. the client transmits the password as clear text over the network, and Apache, upon receiving it, generates a hash from the password and compares the hash to the user's entry in the file /etc/apache2/dav_svn.htpasswd. If the hashes match, authentication was successful.

Note: Because of the clear text transmission of the password, clients should always use HTTPS so that the entire HTTP traffic is encrypted by SSL!

Now the only thing that remains is to create the first user. For instance, the following will create the user "patrick" and prompt for a password:

rm -f /etc/apache2/dav_svn.htpasswd
htpasswd -c /etc/apache2/dav_svn.htpasswd patrick

Note 1: The -c option requires that the password file does not exist; for the second and all future users, leave out the -c option.

Note 2: The password file stores its passwords as encrypted hashes. The encryption algorithm depends on the options you specify for htpasswd. On my system the default for htpasswd is to use the DES algorithm.


Access control: Authorization

Once a client has been authenticated (i.e. it has proven its identity by providing a correct user/password combination), the configuration presented above authorizes the client to have write access to all repositories on the server. Again this may not be exactly what you want. For instance, now that you know who the client is, you may wish to exactly define what the client may (or may not) access. This procedure is called authorization.

The Apache module authz_svn allows you to define your authorization rules. On my system, the module is already loaded by default in

/etc/apache2/mods-enabled/dav_svn.load

However, the module is not used. Some example directives that are commented out can be found in

/etc/apache2/mods-enabled/dav_svn.conf


How does it work?

  • first you have to define your authorization rules in a rules file (/etc/apache2/dav_svn.authz in the example below)
  • then you tell authz_svn where that file is (the AuthzSVNAccessFile directive in the example below)
  • whenever a client tries to access a repository path, the module checks the authorization rules
  • when the module finds out that authentication is necessary to access the repository path, it hands control back to Apache for the authentication procedure
  • Apache then makes sure that the client authenticates correctly according to the AuthType configuration
  • if the client cannot authenticate, access is denied
  • if the client can authenticate, control is again passed back to the module
  • the module now checks the client identity (i.e. the user name resulting from the authentication procedure) and the type of access (i.e. read, write) against the authorization rules
  • access is denied if the rules state that the user/access type combination is not allowed on the given repository path
  • access is allowed if the opposite is true


What follows is the complete Location block from the previous chapters, now updated with the directives required for authz_svn:

<Location /svn>
  DAV svn
  # any "/svn/foo" URL will map to a repository /var/lib/subversion/foo
  SVNParentPath /var/lib/subversion

  # How to authorize a user
  AuthzSVNAccessFile /etc/apache2/dav_svn.authz

  # Try anonymous access first, resort to real
  # authentication if necessary.
  Satisfy Any
  Require valid-user

  # How to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /etc/apache2/dav_svn.htpasswd
</Location>

As can be seen, the <LimitExcept> directive has been removed. Its task is now performed by the authz_svn module.

"Satisfy Any" means that Apache allows access only if the client

  • either authenticates (required because of Require valid-user)
  • or if an Allow directive explicitly allows access (e.g. Allow from 192.168.1)

Since our Location block does not contain an Allow directive, I assume that the authz_svn module somehow fabricates the directive "in the background" when it determines that, according to the rules file, no authentication is necessary.


For a definition of the options you can use in the authorization rules file, please refer to

http://svnbook.red-bean.com/nightly/en/svn.serverconfig.httpd.html#svn.serverconfig.httpd.authz


Access control: LDAP

The following block in the Apache configuration enables access control over LDAP:

<Location /svn>
  DAV svn
  # any "/svn/foo" URL will map to a repository /var/lib/subversion/foo
  SVNParentPath /var/lib/subversion

  AuthName "Subversion Repository"
  Include pelargir-ldap.conf
  <LimitExcept GET PROPFIND OPTIONS REPORT>
    Require ldap-group cn=svnusers,ou=groups,dc=herzbube,dc=ch
  </LimitExcept>
</Location>


websvn

Add the following block to the Apache configuration (/etc/apache2/conf.d/osgiliath.conf in my case):

# The websvn package creates a symlink /var/www/websvn, which
# it obviously expects to be used to make websvn accessible.
# This may work in "normal" setups where /var/www is the
# DocumentRoot of the server
# -> Because we don't use this directory as the DocumentRoot
#    we have to manually create an Alias here, but we let it
#    point directly to /usr/share/websvn and ignore the
#    /var/www/websvn symlink
Alias /websvn/ /usr/share/websvn/
# For some unknown reason we have to use <Location> here
# instead of the usual <Directory>. Maybe because websvn.conf
# uses <Location> for some other options???
<Location /websvn>
  <IfModule mod_php5.c>
    php_admin_flag engine on
  </IfModule>
</Location>


Administration of repositories

Basics

Create a repository:

svnadmin create --fs-type fsfs /var/lib/subversion/acexpander
chown -R www-data:www-data /var/lib/subversion/acexpander

Import content into an existing repository (note: this immediately changes the repository, you will not have the chance to change your mind and not commit):

svn import --message "ldap stuff" ldap http://herzbube.ch/svn/tools/ldap

(this imports into the "ldap" directory, creating it if it does not exist yet)


Backup and restore

Dump a repository (resulting file can be used for backup):

svnadmin dump /var/lib/subversion/yasna >yasna.svndump

Restore a repository dump:

svnadmin create /var/lib/subversion/yasna
svnadmin load /var/lib/subversion/yasna <yasna.svndump


Remove files from a repository

The basic sequence is:

  • create a repository dump file
  • cat the dump file into the tool svndumpfilter
  • the tool now modifies the dump file on the fly and outputs a modified dump file
  • restore the modified dump file to a repository


The following example removes the file "Archive/unace.tgz" from the repository trunk and all of the explicitly named tags:

cd /tmp
svnadmin dump /var/lib/subversion/aceexpander >ae.dump
cat ae.dump | svndumpfilter exclude trunk/Archive/unace.tgz >ae-mod.dump
for tag in start v0-5 v0-6 v0-7 v0-8 v0-9-1 v0-9 v1-0; do cat ae-mod.dump | svndumpfilter exclude tags/$tag/Archive/unace.tgz >x; mv x ae-mod.dump; done
svnadmin create /var/lib/subversion/acexpander-modified
svnadmin load /var/lib/subversion/acexpander-modified <ae-mod.dump
chown -R www-data:www-data /var/lib/subversion/acexpander-modified


Converting a CVS repository

Converting a CVS repository to SVN is done using the tool

cvs2svn

The tool automatically creates the directories "branches", "tags" and "trunk". If the CVS repository contains binary files (e.g. .pdf, .xls, .doc), cvs2svn must be run with some additional options to prevent those files to be imported with errors.


To let cvs2svn recognize binary file types according to their extensions, specify the option

--mime-types=/etc/mime.types


To prevent the property svn:eol-style from being automatically set to "native", specify the option

--no-default-eol

(if this option is not specified, the property will be set on all files whose so-called "Expansion Mode" in the CVS repository is not set to "kb")


To allow the property svn:eol-style to be automatically set according to the file extensions, specify the option

--eol-from-mime-type

(this option has no effect unless --no-default-eol is also specified; files that have a MIME type "text/*" are given the property value "native")


An example command sequence to convert a CVS repository:

---------- <on my Mac where I have my CVS repositories> ----------
cd /Users/Shared/Development
tar cfp cvsroot.tar cvsroot/*
gzip cvsroot.tar
scp cvsroot.tar.gz osgiliath:/tmp
---------- <login on osgiliath> ----------
cd /tmp
tar xfpvz cvsroot.tar.gz
cd cvsroot
for i in acexpander herzbube.ch ndsi yasna; do cvs2svn --mime-types=/etc/mime.types --no-default-eol --eol-from-mime-type --fs-type=fsfs -s "/var/lib/subversion/$i" "$i"; done
cd /var/lib/subversion
chown -R www-data:www-data acexpander herzbube.ch ndsi yasna


Access to repositories

The following is a simple and short command reference that is probably of no great use. More details can be found in the "SVN Book" and on the command line ("svn help").

Checkout (= create) a working copy:

svn checkout svn://osgiliath/acexpander/trunk
svn checkout http://osgiliath/svn/acexpander/trunk

Update working copy:

svn update

Add a file:

svn add xxx

Add a directory:

svn mkdir xxx

Remove a file or directory:

svn delete xxx

Overview of the changes made to the working copy:

svn status

Comparison of a file in the working copy and the version that was checked out (not the version in the repository HEAD!!!):

svn diff xxx

Undo changes in the working directory

svn revert xxx

(a file that was added again becomes unmanaged by svn; a file that was changed is overwritten (!!!) by the file's version that is in the working copy cache)

Transmit working copy changes to the repository:

svn commit --username patrick --password <secret>

(after the first login, svn stores the given credentials as clear text (!!!) in ~/.subversion

View a file's history:

svn log xxx