In a previous article, we saw how to install and use Subversion, in the context of a single user, and use it on a single system. This article will take you one step beyond and show how to install a Subversion server, accessible via the network and to a team of users. We'll see also how Subversion can help to manage a project to which several developers contribute.
Subversion uses Apache as its HTTP transport layer. The command-line client communicates with remote servers with a protocol built on the WebDAV/DeltaV HTTP 1.1 extensions. This means that a Subversion server, while not completely implementing WebDAV, can respond to common HTTP and WebDAV (read-only) clients such as Web browsers and file explorers.
You'll need a recent version of Apache 2 to build a Subversion server. The
build process is obviously a bit more complex than for a stand alone client, as
you should build an Apache with the specific module
That's why I'll detail the necessary steps.
As in my previous article, I'll assume you're building from a source
snapshot on an Unix platform and that you will install everything under
XXXX is the
revision number of the snapshot you're using, this directory being linked from
/usr/local/subversion). This directory layout makes it easier to
upgrade Subversion, an important point when using a software that has not
reached a stable version yet. In this case, make sure your system can find
shared libraries in
/usr/local/subversion/lib. (To build
Subversion from the latest sources, refer to the INSTALL file, which explains
how to checkout and compile from the development server.) At the time of this writing, the current stable version is 0.16, revision number 3987.
The first step is to set up the subversion directory and build the database. As of subversion 0.15, you will need the Berkeley DB, version 4.0.14. If it's not installed on your system, install it in the same directory as Subversion.
# mkdir /usr/local/subversion-rXXXX # ln -s /usr/local/subversion-rXXXX /usr/local/subversion $ gunzip -c db-4.0.14.tar.gz | tar xf - $ cd db-4.0.14/build_unix $ ../dist/configure --prefix=/usr/local/subversion-rXXXX $ make # make install
Then, install Apache. You should get at least version 2.0.42. The
--enable-dav option will build and include mod_dav and
--enable-so will enable DSO (Dynamic Shared Object) support. The
other options specify that Apache will be installed with Subversion and will
use the Berkeley DB you've just built, an important point.
$ gunzip -c httpd-2.0.43.tar.gz | tar xf - $ cd httpd-2.0.43 $ ./configure --prefix=/usr/local/subversion-rXXXX \ --enable-dav --enable-so \ --with-dbm=db4 --with-berkeley-db=/usr/local/subversion-rXXXX $ make # make install
Finally, compile and install Subversion. The important option here is
--with-apxs, giving the path to the
used to build and install Apache extension modules. In this case, the
installation process should install
mod_dav_svn, the Apache
module that handles requests from Subversion clients.
$ cd .. $ gunzip -c subversion-rXXXX.tar.gz | tar xf - $ cd subversion-rXXXX $ ./configure --prefix=/usr/local/subversion-rXXXX \ --with-berkeley-db=/usr/local/subversion-rXXXX \ --with-apxs=/usr/local/subversion-rXXXX/bin/apxs $ make $ make check # optional : runs the tests # make install
As Apache provides the network layer, a Subversion server gets all the
Apache features for free: robustness, HTTP authentication, data compression via
mod_deflate, encryption via
To enable some URLs to be handled by
mod_dav_svn, you only have
to add in the
<Location /svn> DAV svn SVNPath /home/rafael/svn </Location>
This block tells Apache to serve paths that begin with
mod_dav_svn, and that they map to the Subversion repository kept
at the physical location
/home/rafael/svn. To check out a working
copy of the trunk of the
frobnizer project from this repository,
the appropriate command becomes:
$ svn checkout http://my.host/svn/frobnizer/trunk work-dir
By default, Apache listens on port 80, and this means that it must be
started by the root user. You can change this if you want. Note, however, that
the Apache processes must be able to write to the Subversion repository. One of
the clean ways to do this is to create an account
svn, on your
system, to own the Subversion repository. Configure Apache to run as this user
User configuration directive. Once you've done this,
you're ready to start the server:
$ /usr/local/subversion/bin/apachectl start
To restrict read or write access to your repository, you can use Apache's
access control features. You can limit the hosts that are allowed to access the
repository and require a username and a password for some operations (via HTTP
authentication). For example, the following lines, inserted in the
<Location> block, limit write access to a specific group of users:
<LimitExcept GET PROPFIND OPTIONS REPORT> Require group commiters </LimitExcept>
This specifies that all requests but those using the listed HTTP methods should be issued by a authenticated user, a member of the commiters group. (See http://httpd.apache.org/docs-2.0/mod/core.html for a detailed description of access control and of the related configuration directives.) When the commit has been done via HTTP, the Subversion-generated change logs will record the HTTP username.
Note that the Subversion client caches your username and password in your
working copy by default, so you won't have to enter it each time you access the
repository. If you're worried about security, you can disable this behavior
via the user configuration file
$HOME/.subversion/config or in the
global configuration file
/etc/subversion/config on the client
If you want to deploy Subversion in your organization, you may find it
easier to compile a statically linked
svn client. This way, you
only have to copy this binary file to the users machines, without having to
install the required libraries. (You'll need, however, to compile this client
separately using the
--enable-all-static configure option. It's
not possible to build a statically linked
mod_dav_svn at the same time. The Apache module needs to be built
as a shared library.)
When several users have commit access to the Subversion repository, they
need, at some point, to integrate in their working copies the changes committed
by others. The
svn update command performs this task, updating its
targets (by default, the current directory and its contents, recursively) to
the latest revision.
What happens when you update a file that you've edited locally, but on which another user has committed his own edits? If you've read the first article, you already know that Subversion doesn't require that you lock a file when you want to modify it. This behavior is different from some other source control software that prevent you from editing a file that has been reserved by another user. Subversion attempts to merge the changes you've made with the ones made by others.
While performing an update,
svn will print a list of updated
files, marking each line with a letter to indicate the action taken on the
corresponding file. It was (A)dded, (D)eleted, (U)pdated, and, when your local
copy has changes, they have been mer(G)ed, or the file remains in a
(C)onflicted state. For example, the following output:
U foo.c G bar.c C hot.c
foo.c was updated, and that it didn't have any
local change. Your local edits to
bar.c were successfully merged.
Subversion didn't know how to merge on
hot.c, because the
server's changes overlapped with yours.
In this last case, Subversion will create three temporary files containing
your local version, the repository version you used as a basis for your edits,
and the latest version from the repository. Your original file will be modified
as well: mergeable changes will be merged, and other changes will be indicated
with conflict markers similar to those inserted by the
You will then have to resolve those conflicts manually and delete the
temporary files. (To protect against unwanted commits, Subversion will not let
you commit a file when it detects this temporary file lying around.) For
svn resolve command deletes those temporary files
Note also that Subversion will not let you commit files for which a more recent revision exists. You'll need to update them first.
An interesting feature of Subversion is that it allows an arbitrary amount of metadata to be attached to any versioned resource (files and directories). Metadata, like files, are also versioned. This metadata is a set of key/value pairs, known as properties.
You can use properties to annotate files (let's say,
review-status to this file is being reviewed by Joe), or
directories. For example, on a tag directory
can add a note beta release, for internal use only.
Properties whose names begin with
svn: are reserved by
Subversion. Some of them are specifically recognized and handled. For example,
svn:executable marks a file to be checked-out with the executable
flag set on filesystems that support it. Other interesting properties
svn:keywords controls keyword expansion. Like CVS, Subversion
can replace strings of the form
$Keyword:...$ embedded in text files. The keywords that you want
to be replaced should be listed in the value of the
property. Subversion provides keywords for the last user that modified the
file, the last revision number that affected the file, the date of this last
revision, and an absolute URL for the file in the repository. For example, if
the standard headers for some of your files contain the keywords
$Author$ , you can enable their automatic
substitution with the following commands:
$ svn propset svn:keywords 'Rev Author' *.c $ svn commit -m "Enable keywords Rev and Author on C files" *.c
svn:mime-type attaches a MIME type to a file to be used when
delivering the file via HTTP. It's also used to determine whether the file
should be stored internally as text or as binary. No MIME type, or a MIME type
that begins with
text/, indicates a text file. All other types
indicate a binary file. Note that the
svn import and
add commands try to recognize binary files and tag them as such, with
the default MIME type
application/octet-stream. Subversion does
not attempt to make diffs between different versions of a binary file.
svn:eol-style is very useful when several developers work on
different platforms. It's used to force the line endings to
to check-out always the file using the native line ending used on the
I presented an overview of tags and branches in the first article. Branches prove to be useful mainly on multiuser projects. With branches, a developer or a team of developers, can work on an experimental feature without interfering with the main development. Branches can also be used to keep track of changes in a maintenance version of a product. They can be used to handle beta releases and the returns of beta-testers. Subversion makes it easy and cheap to create branches, so why shouldn't you use them?
A branch is typically created outside a working copy to spare disk space
with the form of the
copy command that operates directly on URLs.
The following creates a maintenance branch from the current project trunk (by
convention, the root directory of your project files):
$ svn copy http://my.host/svn/frobnizer/trunk \ http://my.host/svn/frobnizer/maint-1.0
To start working on a branch, you can of course check out a whole new
working copy corresponding to it, but it's easier to switch your
working copy (or parts of it) to the branch. Assuming that your working copy
already contains the trunk, you can make it point at the
branch with the following command, issued at the root of your working copy:
$ svn switch http://my.host/svn/frobnizer/maint-1.0 .
svn switch works a bit like
update, except that it
doesn't conceptually move your working copy through time (revisions), but
through space (branches). Consistently, the output of
similar to that of
Finally, to integrate changes from another branch in your working copy, you
can use the powerful
svn merge command.
merge can be
diff, but instead of outputting changes as a unified
diff to standard output, it applies them to your working copy (as you would
apply a patch).
For example, the following commands integrate a batch of changes from the trunk into your maintenance working copy:
$ svn merge -r149:155 http://my.host/svn/frobnizer/trunk U foo.c U foo.h A bar.c $ svn status M foo.c M foo.h A + bar.c $ svn commit -m 'Integrate revisions 150 to 155 from the trunk'
The output of
svn merge indicates which files have been
affected by the changes, just like
svn update. The
status command reports the status of files in your working copy. In this
foo.h have local modifications,
bar.c is scheduled for addition in the next commit. The
+ sign in the status line indicates that Subversion knows that
this file has been branched from elsewhere, and will retain this information
when committing it.
Note that you may have to resolve manually potential conflicts between the merge and the commit.
An interesting use of
merge is to fetch changes from the
current branch. This way you can roll back a change. The following command
applies the changes of revision 200 to your working copy, in
$ svn merge -r200:199 .
Once you're getting used to Subversion, you'll find that it can help you in ways you didn't expect.
A first example would be dealing with runtime configuration files during upgrades. Working on a software project where some global configuration variables (IP addresses, environment variables or trace levels) are read in a file, I have to modify this file almost every time the application is installed on another machine. A default configuration file is provided with the sources, and it's kept under version control because the set of configuration variables changes over versions. What to do when I want to upgrade a snapshot installed on a test machine, but without losing the local changes to the config file?
The solution is simple. Instead of installing the application on the test
machine from a source tarball, I copied my statically linked
client there and performed a check-out. Then, I adjusted the configuration
files to my needs, without commiting the changes. Upgrading the software to
another development snapshot is as easy as an
svn update followed
make all . The update process integrates the common changes
to the configuration file (e.g., new variables) with the local changes (e.g.,
user/password to access a local database). This can be applied to any evolving
configuration file you need to deploy: an
.vimrc, or an
Branches can also be used in creative ways. I work on an intranet application that ships with several sets of style sheets. For example, some are designed for low resolution screens and provide different font sizes and background images.
I set up two directories,
/html/css/1024, to hold those CSS. The
directory, for style sheets adapted to a higher screen resolution, is a branch
of the other. When a CSS is augmented or modified, it's easy to incorporate its
changes into the other branch while leaving the specific font or image
I'm sure Subversion will be used in ways its designers didn't imagine. That's what makes the success of a tool.
Thanks to Karl Fogel for having kindly reviewed this article.
Rafael Garcia-Suarez is a French software engineer and Unix system administrator.
Return to the Apache DevCenter.
Copyright © 2009 O'Reilly Media, Inc.