A Novices Tutorial on Subversion

Sean Russell


Table of Contents

Why choose Subversion?
Installing Subversion
Directory structures
General usage
Caveats
Summary

Abstract

Subversion (SVN hereafter) is a version managament system designed as a replacement for, and general improvement on, CVS. Subversion is designed to be a distributed, concurrent, expandable version control system.

This is a walkthrough on setting up and running the pre-alpha release of Subversion, aimed at people perhaps familiar with CVS, but not experts at SVN.

Why choose Subversion?

Subversion has some aspects which can be confusing for people coming from a CVS background. Currently, SVN is pre-alpha software, and should be treated as such: back up your database. However, that said, Subversion in its current state offers immediate benefits over CVS.

  • Better control over directories. Directories, like any other file, are versioned in Subversion. This means that you can track, copy, and delete them as you would any other file. This sort of directory management was an administrative nightmare in CVS.

  • Offline mode. Subversion, being designed with the network in mind, allows many functions to be performed off-line. This includes "tagging" and "branching", as well as copying, status, and diffs. CVS had no off-line commands.

  • Atomic commits. A commit is completed as a single operation. This means that if something should happen in the middle of a commit, the repository will be left in a valid state. This was not the case with CVS.

  • Subversion has a WebDAV interface to the repository. While I haven't played around with it, this should replace several add-ons to CVS.

Installing Subversion

I'm not going to go into great detail about how to install Subversion, but I will point out a few things which I either had trouble with, or which you should watch out for. Nearly all of these pertain to the SVN server, so if you aren't installing that, you can ignore them. Installing the SVN client is pretty painless.

The Subversion server requires Apache 2. Follow the instructions in subversion/notes/dav_setup.txt before you do anything else! I installed Apache 2 with no problem, and set it to run on a different port than :80 so that it wouldn't conflict with my Apache 1 server. Eventually, I'll migrate to Apache 2, so this is a Good Thing. The server also requires neon; make sure you download that, unpack it into the subversion directory, and then rename the directory to "neon".

After you get Apache installed, go ahead and build and install Subversion, but don't use the recommended --disable-shared option; if you do, WebDAV won't work, and neither will your server. Make sure that you do use the --with-apxs=/usr/local/apache2/bin/apxs option mentioned in the dav_setup.txt file, but not mentioned in the INSTALL file.

If you build Subversion from the self-hosted repository, it requires bleeding-edge autoconf, automake, and libtool installations. I had problems installing the Subversion server on a couple of machines because of this. If you do as well, don't call me. I still have one older server that Subversion refuses to compile on. If this is the case for you, just use the tarball distribution from the subversion webpage.

After you get Apache and Subversion installed, create a subversion user. Then make a subdirectory named repos in ~subversion and run svnadmin ~subversion/repos[1]. You'll also need to add some lines into Apache2's httpd.conf to make the repository accessible. This is documented in the dav_setup.txt file. The installation instructions mention creating the repository after they talk about testing the tools, which is confusing. Make sure that you run svnadmin before you attempt to test accessing the repository.

Security is not handled by subversion, but by Apache. Secure your repository using the normal Apache configs.

I set my subversion server with the Apache config file:

<Location /svn/repos>
   DAV svn
   SVNPath /home/subversion/repos
</Location>

This allows me to access my repository at http://localhost:8080/svn/repos, with, for example: svn co http://localhost:8080/svn/repos myproject/trunk -d myproject (I'll explain this below).

Directory structures

When you import a project into Subversion, or start one there, it is recommended that you change your directory structure around a little to accomodate how Subversion handles "tags" and "branches". There are three things to note at this point: the first is that this restructuring isn't neccessary, but recommended, to make your life easier. The second is that Subversion doesn't have "tags" and "branches" per se, as CVS does, but only has copies. Some SVN people you talk to will use the words "tags" and "branches", but others will insist on only talking about "copies". The third is that "version" means something different in SVN than in CVS.

Last thing first: versions. This is actually a pretty simple concept, unless you're coming from CVS. SVN doesn't store a version number for each file, it stores a global version number per repository. Therefore, it doesn't make sense to talk about "version 5 of file X". You should think about it like "file X from version 5 of the repository".

I'll outline the recommended directory structure for reference; we'll discuss it below. Here's an example directory structure for "myproject"

myproject/
   trunk/
   tags/
   branches/

When you make a copy with SVN (via svn copy), SVN makes as light a copy as possible. For example, if you svn copy a directory, the you'll get a copy on your client side, but on the server, SVN will only store a delta, telling it that a copy has been made (I'm simplifying here). This is similar to what happens when you edit a file; just like CVS, the server only stores the differences between the two versions, not a whole new file.

SVN doesn't know anything about tags or branches; it only knows about deltas. Therefore, a tag is just a copy of the main directory. Consider our example directory structure above. In CVS, all of my project files would normally be in myproject/, and various subdirectories. When we import a project to or start a new project in SVN, we put all of the files and subdirectories in trunk/. When we want to make a snapshot (tag, in CVS) of a given version, say "r1.0", we simply do an svn copy: svn cp trunk http://localhost:8080/svn/repos/myproject/tags/r1.0. This makes a cheap copy, and the name of the new directory is now the tag. The only difference between a tag and a branch, then, is that you don't ever change any of the files or directories in a tag.

I'll go into more detail about the commands, but this explains why you want to set up your directory structure this way. Make sure that you do this restructuring before you import your project into SVN.

General usage

We'll start from the top: importing a project. We'll assume we're working with a CVS project; if you aren't you can ignore the CVS-related items.

To import a new project, clean up your project directory and prepare it for SVN. For the example below, assume that we start in the parent directory of your project directory.

$ find myproject/ -name CVS -exec rm -rf {} \;
$ mkdir myproject_svn myproject_svn/tags myproject_svn/branches
$ mv myproject myproject_svn/trunk
$ svn import http://localhost:8080/svn/repos svn_myproject myproject

The "myproject" in the last command is very important. Don't forget this!! If you do, all of the files in svn_myproject will be imported directly into the root directory of the repository, and you'll have a big mess. If you're using a different repository per project, you don't have to bother with this, but if you want multiple projects per repository, this is neccessary.

To check the project out, you would execute: svn checkout http://localhost:8080/svn/repos/myproject/trunk -d myproject. This checks out the main trunk into a subdirectory called "myproject". The argument to -d can be any name. This is where SVN is really cool, and just how cool sort of creeps up on you. You can do things like: svn checkout http://localhost:8080/svn/repos/myproject/tags/r1.0 -d myproject_release_1_0 to check out a given tag. Tags and branches always were a difficult concept to work with in CVS, and after you get used to it, they're much easier with SVN.

Commits and updates are performed almost the same as with CVS via svn commit and svn update, with a minor difference; you must pass 'svn commit' a -m or -e argument if you want to supply a log message, because it try to force you to give one. The reason for this is given in the SVN documentation, and I agree with them. SVN updates do conflict resolution differently than CVS. CVS embeds the conflicts in the source file. SVN puts conflicts into a .rej file, rather than in the source directly. This is nice, because it allows more orthoganal difference management techniques on heterogeneous file types; CVS conflicts only worked on text files. It also means that you can ignore conflicts until you have to commit (commit won't let you commit if you have rejects laying around). The SVN status command, unlike the CVS status command, is actually useful, and gives you decent, human-readable output. The status command can also be performed "off-line".

As I mentioned earlier, files and directories can be renamed and deleted. Renames are performed with the svn copy command; remember that all copies start out as cheap copies. Deletes are performed with the svn delete command. The documentation states that the delete on the client side occurs when you commit the change, but I've noticed that you have to update to have the files removed from your local copy. For example: svn delete file && svn commit && svn update, to have svn remove the file from your directory.

Caveats

A current gotcha to watch for is when making copies of branches. Before you svn copy any branch or other directory copy that has changed, make sure that you perform an svn update to make sure the version is current. I'm not sure what happens if you don't, but whatever it is, it probably isn't good.

Summary

Subversion is already very usable, and functionality is added daily. In my opinion, SVN already surpasses CVS in usefulness; being able to delete and rename directories alone would justify the switch. My only disagreement with SVN is that the versions are repository-wide, not project-wide, neccessitating (in my opinion) multiple repositories, which equates to additional administrative work. However, this is an issue which will be resolved, one way or another, and is not a reason to not use SVN.



[1] There is a discussion about how to use repositories, and it requires some background. My recommendation is an opinion shared by myself and at least one Subversion developer, but it is by no means the rule. Subversion repositories share a single version. Therefore, you may have multiple projects in one repository, but all projects in that repository will share a version. If you have project A and project B in a repository, project B's version will change every time project A has something committed to it, even if no work is being done in project B. The way to have separate project versions is to have multiple repositories. This is somewhat tedious. Therefore, I recommend that you try to lump similar or related projects into one repository, but create different repositories for very different projects.