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