On some revision control systems

So I’ve long wondered about the advantages of those shiny, modern (distributed) revision control systems that have seemingly become quite fashionable these days. I started with CVS and liked it, but once I moved to Subversion I have started to feel a dissatisfaction with my version control system, the kind that would not go away if I went back to CVS. It’s like driving a car. Once you drive a faster car, you realize the faster car is not fast enough. Obviously, going back to the original car is a step in the wrong direction.


The first revision control system I used was CVS. I liked it when I got used to it. It let me keep track of my files, was easy to back up and was fast enough for my little projects. There were good workarounds for CVS’ limitations such as “renaming” files by performing an explicit add and then remove operation or by doing a “repo-copy” (i.e. copying the files in the repository itself). Empty directories could be pruned on updates. What else would one want?


Well, I have long wondered why I should be using Subversion instead of CVS. After all, CVS has worked well for me in the past and simply "because it can rename files" hasn’t been too convincing. In fact, I have heard that argument so often and from so many people, it started to become a reason not to switch to Subversion. Well, then I gave Subversion a shot and I have to say, I like it – with some limitations.

But let me first say what I think is good about Subversion. I like Subversion’s speed advantage over CVS. The reason I started using Subversion was that I wanted a way to track my own changes to a rather large source tree that is kept in CVS. I wanted to make periodic imports of snapshots and merge my local changes – similar to how vendor branches work in CVS. When trying to accomplish this with CVS, it became apparent that it would be very time consuming: An import and merge session could take several hours. Doing the same thing with Subversion accellerated the process quite a bit – an update session would still take about an hour, but mostly because I had to download the updated source tree from the net.

Ok, that’s about it when it comes to subversion’s advantages. What I don’t like is subversion’s poor handling of branches. I don’t think a branch is another directory, I think a branch is a branch. The same holds true for a tag. Also, merging branches is a major pain – while simple at first, it will get to the point where keeping track of what has been merged and what needs merging is a complex task . Granted, CVS isn’t a whole lot better at that.

To set things straight: I’m not saying Subversion is bad. All I’m saying is that it isn’t a lot better than CVS for my purposes.


So now on to the reason I started this post. I realize, it has become a lot longer than anticipated, but who cares?! I’ve read about the advantages that distributed revision controls offer some time ago. Having all the history available in with every working copy is one of them. The ability to keep track of merges between branches is another one. It’s the latter that got me interested in Mercurial. While I realize that upcoming versions of Subversion will support merge tracking as well, the version(s) installed on my various computers don’t support it – and I don’t want to compile some development branch of a critical (to me) piece of software.

So I looked at other choices, e.g. git and Mercurial. To be honest, I haven’t looked at git because I heard it is supposed to be hard to use with more than 100 commands. So I started to look at Mercurial and played around with it. I like it, so I don’t think I’ll look at git anytime soon.

Mercurial has (almost) everythig I need: It’s fast, it’s easy to use and it handles merges between branches well. I’m the "almost&quot can be erased as soon as I dig further. What’s still missing is an easy setup for a permanent, central "master" repository (short of the "hg serve" command which starts a minimal HTTP server). I’m also missing a web frontent similar to CVSWeb and SVNWeb – I’m sure such thing does exist, but I haven’t found it yet. The third thing I haven’t figured out yet is how to do backups.

I’d like to write a bit about the differences between Mercurial and the other systems I’ve used. First, a revision of a file has up to two parents. Actually, it always has two parents, but one may be the null parent, and that doesn’t count. You start out with a file that has two null parents. Once you commit a change to the file, the file’s parent becomes the previous revision, and so on. If a revision has only one parent, and no other revision uses it as it’s parent, then that revision is called a head.

The second parent comes in the play, when you have multiple branches. Creating a branch is also different from other systems. You first switch a working copy to a new branch by issuing hg branch <name<. The default branch, i.e. what’s called "HEAD" in CVS and "trunk" in Subversion is called "default" in Mercurial. The default branch always exists. Switching a working copy to a new branch does not create the branch, yet. Only commiting a first change in that working copy does. Note that the first revision’s parent will be the branchpoint revision.

So what happens when you merge between branches? When you merge and commit, the current branch will contain all changes of the original branch since the last merge (or the branchpoint). You don’t need to remember which changes you need to merge into the current branch – Mercurial does that automatically for you. This is possible because when merging, a file’s second parent is filled in with the head revision of the originating branch. That also means, that when you merge changes from branch A into branch B, the head revision of branch A is no longer a head. Don’t worry, though, once you commit something on branch A again, it will have a head again.

Now on to the distributed part. The concept of branches is taken further in a distributed revision control system. Multiple instances of the repository can have dependencies between each other. A new copy of a repository is created by running the "hg clone" command. Then, changes to that repository copy can be made as if the original never existed. But, if you want to, you can also pull any changes the original repository has incorporated. Do this by running "hg pull" – it’s really just merging all changes from your master repository into your copy. It also works the other way around: You can push your changes upstream by running "hg push" (if the upstream repository allows it).

All in all, I find Mercurial very easy to use once the basic concepts have been understood. I’m not sure yet whether I’ll convert any of my Subversion repositories to Mercurial or if I’ll use seriously at all. But for future reference, here’s a link to a free book on Mercurial.

Setting up a Subversion Server

Ok, there’s probably a million Blog posts, tutorials and HowTos on how to do this already. Yet I still find it hard to find short instructions on how to set up a subversion server quickly. I just had to do it again, and it took me longer than expected, so here it goes.

My requirements are pretty basic:

  • WebDAV so I can access the repository over HTTP.
  • I don’t need too much security, so SSL won’t be needed.
  • I want SVN:Web, a web front end to the repositories.

Here’s what I did for the Subversion+WebDAV part. Note that I’m currently on IBM’s OpenClient Linux Distribution which is based on RedHat.

  • I made sure that the httpd package is installed. I didn’t have to install it, so I guess it’s installed by default.
  • I had to install the mod_dav_svn package with yum:
    $ sudo yum install mod_dav_svn
  • The package installs a config file at /etc/httpd/conf.d/subversion.conf. By default, the <Location> tag is commented out. I just copied it, removed the comment signs and adjusted the values to my needs. This is what it now looks like:
    LoadModule dav_svn_module     modules/mod_dav_svn.so
    LoadModule authz_svn_module   modules/mod_authz_svn.so
    <Location /repos>
       DAV svn
       SVNParentPath /var/www/svn
          AuthType Basic
          AuthName "Authorization Realm"
          AuthUserFile /var/www/passwd
          Require valid-user
  • Now the Apache HTTP Server will serve the contents of /var/www/svn via WebDAV (I think), but it will query for a valid user entry. The entry must be created as follows:
    # cd /var/www
    # htpasswd -c passwd <username>
  • Next, I created the direcories for the repositories. As root, I ran these commands:
    # cd /var/www
    # mkdir svn
    # cd svn
    # svnadmin create <name of repository>
    # chown -R apache:apache <name of repository>
  • I then activated the HTTP service so the Apache Web Server would be started at boot time. I did this using a GUI tool called system-config-services. I had to check the box next to the httpd entry. I didn’t want to reboot right aways, so I clicked Start in the toolbar. The tool told me that the service would now be running and I could verify this by going to http://localhost/. Testing the Subversion part was easy, too. Navigating to http://localhost/repos/example did the trick. Note that the actual repository name must be used instead of "example".
  • Oh, and I want at least minimal security. That is, I want the HTTP Server to serve pages only to the local machine. Therefore, I changed the Listen directive in the server config uration file to this:

The second part was installing the SVN::Web CGI Script. Here’s what I did to do it.

  • First, I had to install the subversion-perl package with yum. As root, I ran
    # yum install subversion-perl
  • Second, I installed the actuall SVN::Web script through CPAN. Again as root, I did
    # cpan install SVN::Web
  • I then created a directory that would hold all the SVN::Web files:
    #cd /var/www
    #mkdir svnweb
  • In that directory, I let the Perl script set itself up using default values:
    # cd /var/www/svnweb
    # svnweb-install
  • The last step created a file called config.yaml. It must be edited so the CGI script finds the repositories. Near the end, I edited the reposparent value:
    reposparent: '/var/www/svn'
  • Now, as the final step, the script needs to be introduced to the Apache Server. I created a file svnweb.conf in /etc/httpd/conf.d with the following contents:
    Alias /svnweb /var/www/svnweb
    AddHandler cgi-script .cgi
    <Directory /var/www/svnweb>
            Options All ExecCGI
            DirectoryIndex index.cgi

After restarting the Apache HTTP Server, I could access http://localhost/svnweb and see the repositories.

Vendor Branch Management with Subversion

So, I sometimes hack on FreeBSD and want version control for my local changes. Working with CVS and vendor branches seemed to much of a pain and everyone was talking about how nowadays you’d use Subversion anyways. So I decided to give Subversion a try.

What helped me a lot in getting started with Subversion was the Book “Version Control with Subversion“, which is available online.

So I set up a subversion server and import snapshots of the FreeBSD source tree on a more or less regular basis. Here’s what I do to update the vendor branch.

  • First, I fetch the latest and greatest of the FreeBSD sources, logged into svn.dmz.local.deadc0.de as user svn. I use csup, the CVSup replacement in the base system. I don’t use CVS since it would leave the CVS directories all over the place. It would also be slower and I don’t need its functionality anyways. This is the supfile I use:
    *default host=cvsup.dmz.local.deadc0.de
    *default base=/var/db
    *default prefix=/usr
    *default release=cvs tag=.
    *default delete use-rel-suffix
    *default compress

    The actual command I run is:

    # csup -L 2 -g ~/supfile
  • Then, I use the Script svn_load_dirs, which can be installed through the devel/svn_load_dirs port. I invoke it as follows:
    $ svn_load_dirs -t tag http://.../repos/freebsd/vendor current /usr/src

    This causes the repository location /repos/freebsd/vendor/current to be updated with the contents of /usr/src. After that, a tag is created at /repos/freebsd/vendor/tag, caused by the parameter -t.

    This whole process takes a while, and every once in a while, something needs to be acknowledged. So unfortunately, it can’t be let run over night. One thing to remember: The command is run as the currently logged in user “svn”, but the repository username is “phs”. So when the script asks for the password, I hit enter. Then a blank line appears where I enter the repository user name. After hitting enter, the scripts asks for the password again, this time for the right user.

  • The next step is to SSH into knecht.dmz.local.deadc0.de and go to /usr/devel/src. There, I run

    $ svn up

    After that, I merge the new vendor code with:

    $ svn merge http://…/repos/vendor/freebsd/ http://…/repos/vendor/current .

  • The last step may have produced some conflicts, so I use svn status to detect conflicts.
  • After all conflicts have been resolved, I commit the changes with svn ci and voilĂ , that’s it.