Comparison of Configs/Aliases in Bazaar, CVS and Subversion

When a project grows to a certain size, it will probably need a way to share code between multiple software packages they release. In the context of Gnome, one example is the sharing of the libbackground code between Nautilus and gnome-control-center. The simplest way to do this is to just copy over the files in question and manually synchronise them. This is a pain to do, and can lead to problems if changes are made to both copies, so you’d want to avoid it if possible. So most version control systems provide some way to share code in this way. As with the previous articles, I’ll focus on Bazaar, CVS and Subversion

Unlike the common operations each system implements this feature in a different way, so I’ll go over each one in turn and then compare them.

CVS

When you run the “cvs checkout module” command, CVS will look in the CVSROOT/modules file for the repository. For example, the file might contain the following:

module foobar

This would tell CVS to check out the foobar directory from the repository into a directory named module when the user asks for module. If no entry is found for a particular name, the directory by that name is checked out from the repository.

To compose multiple modules into a single working copy, the ampersand syntax can be used:

module foo &bar &baz
bar othermodule/bar

With this modules file, “cvs checkout module” would give the following working copy:

Working Copy Repository
module foo
module/bar othermodule/bar
module/baz baz

Operations like tag, commit, update, etc will descend into included modules, so for the most part a user can treat the resulting working copy as a single tree. If a particular branch tag exists on all the included modules, you can even check out a branch of the combined working copy. There are some problems with the support though:

  • While “cvs update” will update the working copy, it won’t take into account any changes in CVSROOT/modules.
  • If you’ve only got write access to part of the repository, and can’t write to CVSROOT/modules, then you can’t change configurations.
  • While CVS lets you check out old versions of code, you still use the latest version of CVSROOT/modules. This can make it difficult to check out historical versions of the tree.
  • Since “cvs tag” descends into included modules, you can end up with many branch tags on some modules. For instance, the gnome-common/macros directory in Gnome CVS has 282 branch tags, which makes it almost impossible to feed fixes to all those branches.

Subversion

Rather than a single repository-wide file describing the module configuration for checkouts, Subversion makes use of the svn:externals property on directories.

Any directory can have such a property attached. Each line in the property is of the form:

subdir [-rrevnum] absolute-uri-of-tree-to-include

This will check out each the given tree at the given sub dir when ever “svn checkout” or “svn update” are used. However unlike CVS, “svn commit” will not descend into the included modules.

Some of the benefits of this approach include:

  • Inclusions can be placed close to the location they are included.
  • It reduces the permissions problems: if you can commit to the directory where the inclusion will occur, you can add the inclusion.
  • Can include modules from other repositories. In this case, it is actually useful that “svn commit” doesn’t descend into the included module because it is likely that the user won’t have write access to the external modules.
  • When checking out a historic version of the module, the historic version of the svn:externals properties get used.

Some of the down sides to the approach include:

  • Module inclusion directives can be scattered throughout the tree. There isn’t a single place to look for such directives.
  • When including something from the same repository, you still need to use an absolute URI to identify the module. It is not uncommon for committers to use a different URI to access the repository to those who only have read access (e.g. svn+ssh://hostname for committers, svn://hostname or http://hostname for read-only users). So which URI do you use in the svn:externals property? You’ll need to choose between a tree that read-only users can’t check out or a tree that committers can’t commit to …
  • If you want to branch a set of related modules in the repository, you’ll need to alter the svn:externals properties to point at the branched versions of the modules. When performing merges back to the mainline, you need to make sure you don’t merge the svn:externals property changes.
  • When checking out historic versions, although historic svn:externals definitions get used, you will get the up-to-date versions of the included modules unless a particular revision of the included module was specified in the property.
  • If the hosting arrangements for an included module change, the historical values of svn:externals properties will be invalid.

Bazaar

The module inclusion system in Bazaar is handled through “configurations”. These are simple files stored in a branch with lines of the form:

subdirectory archivename/branchname[--patch-NNNN]

After checking out a branch, you can check out the various included modules by running the following command from the base of the working copy:

baz build-config file-name

To update a working copy and all the included modules, you need two commands:

baz update
baz build-config -u file-name

(the -u flag is only available in the 1.5 prereleases. Previously you needed a command like “baz cat-config file-name | xargs -n2 baz update -d“).

The name of the configuration file is not special, and it is possible to have multiple configurations stored in a single branch. In fact it is common to have a branch that stores nothing but configurations, and assemble the source tree in a subdirectory.

One common use of multiple configs is similar to the use of non-branch tags in CVS: recording a particular configuration used for a particular release. This can be done by taking a snapshot of the configuration, which adds fixed revision numbers to the branches checked out:

baz cat-config --snap development.config > release-0.42.config

If anyone builds this configuration, they will see the tree as it was when that snapshot was taken. Some benefits of this system include:

  • It is easy to maintain multiple configurations for a set of branches.
  • Since configurations are stored in the same way as other files on the branch, anyone can modify them (either by committing to the branch, or by creating a new branch and making the change there).
  • Use of the arch namespace to identify branches, so is somewhat immune to branch location changes (it is still vulnerable to referenced branches disappearing altogether).

Some of the down sides of the approach include:

  • Requires the user to run a second command after checking out the branch containing the configuration.
  • No standard name for configurations, so the user needs to know the config file name in addition to the branch name when checking things out.

Summary

Here is a summary of how the three systems stand up against each other in this respect:

  CVS Subversion Bazaar
Who can change configs? Committers to CVSROOT Committers Anyone
Build historic configs? No Yes Sort of (snapshot configs)
Supports multiple parallel configurations of same code? Yes Yes Yes
commit command crosses module inclusion boundaries? Yes No No
Configs built by checkout command? Yes Yes No
Configs built by update command? No Yes No
Resistant to project hosting changes? Yes No Yes
Same config usable for committers and read-only users? Yes Yes for DAV access
No for svn+ssh:// access
Yes

Each system is slightly different with its benefits and problems. It isn’t particularly surprising then that configs are not handled well by the various version control migration scripts. For example, the cvs2svn script doesn’t handle them at all (e.g. the KDE Subversion repository doesn’t contain any svn:externals properties in historic versions migrated from CVS).

End Of Fashion

I went to see End of Fashion upstairs at 78s today for their in-store appearance. The tickets were included with the band’s single which was pretty nice.

They played for about 40 minutes, starting off with some of their well known songs, and then mixed in a few of the new ones off the album. Towards the end, they did a cover of The Red Sun Band’s “Devil Song”.

After the set the band were doing autographs, so I got the liner notes from my copy of the new album signed by all the band members. I like what I’ve listened to on the album so far.

Version control discussion on the Python list

The Python developers have been discussing a migration off CVS on the python-dev mailing list. During the discussion, Bazaar-NG was mentioned. A few posts of note:

I’m going to have to play around with bzr a bit more, but it looks very nice (and should require less typing than baz …)

Version Control Workflow

Havoc: we are looking at ways to better integrate version control in Launchpad. There are many areas that could benefit from better use of version control, but I’ll focus on bug tracking since you mentioned it.

Take the attachment handling in Bugzilla, for instance. In non-ancient versions, you can attach statuses to attachments such as “obsolete” (which has some special handling in the UI — striking out obsolete attachments and making it easy to mark attachments as obsolete when uploading a new attachment). This makes it easy to track and manage a sequence of patches as a fix for a bug is developed (bug 118372 is a metacity bug with such a chain of patches).

If you look at this from a version control perspective, this sequence of patches forms a branch off the mainline of the software, where each newly attached patch is a new revision. The main differences being:

  • No explicit indication of what the patch was made against (code base or revision), or what options were used to create the patch.
  • No linkage between successive patches (can be a bit confusing if multiple patch series are attached to the same bug report).

So why not just use real version control to manage patches in the bug tracker? The big reason for projects using CVS or Subversion is that only authenticated users can create branches in the repository, and you don’t want to require contributors to ask permission before submitting fixes.

So this is an area where a distributed version control system can help: anyone can make a branch, so potential contributors don’t need permission to begin working on a bug. This also has the benefit that the contributors get access to the same tools as the developers (which is also helpful if they ever become a regular developer).

Now if you combine this with history sensitive merging and tell the bug tracker what the mainline branches of the products are, you can do some useful things:

  • Try and merge the changes from the bug fix branch onto the mainline, and see if it merges cleanly. This can tell a developer at a glance whether the patch has bitrotted. This could also be used to produce an up to date diff to the mainline, which can aid review of the changes.
  • Check if the bug fix branch has been merged into the mainline. No need for developers to manually flag the attachment as such.

We discussed some of these features in the context of Launchpad at the recent Brazil meeting.

Back from Brazil

I got back from the Launchpad sprint in São Carlos on Tuesday afternoon. It was hard work, but a lot of work got done. Launchpad is really coming together now, and will become even better as some of the things discussed at the sprint get implemented.

One of the things discussed was to formalise some of the development workflow we’ve been using to develop Launchpad inside Launchpad itself so that it will be usable by other projects.

I really enjoyed the time in Brazil. The food and fruit juices were great (especially the ones made from native fruits like Açaí).

At the end of the second week, Mark flew us up to Rio de Janeiro for the weekend on his jet:

Canonical One
Inside Canonical One

We took a cable car up Corcovado mountain to see the Cristo Redentor statue. There was a great view from the top.

Cristo Redentor
Rio de Janeiro from Corcovado

Since I was leaving that weekend, I didn’t fly back to São Carlos with everyone else. Instead Kiko got a local travel agent to book me a flight directly to São Paulo, so that I could catch my international flight.

Unfortunately, when I went to the Varig ticket counter to pay for the ticket there was no record of the booking, which was bad.

However, I was able to buy a ticket on the flight anyway (which was due to leave in an hour), which was good. I even ended up paying less for the ticket than I expected.

Once I got through security, I found the flight had been delayed, which was bad. After the departure time changing about 3 times, we ended up boarding about an hour after the original listed departure time. This happened to coincide with the listed departure time of the next Varig flight to São Paulo (which had the same gate listed too), causing some confusion.

They served chocolate fondue on the flight, which was nice.

When I reached São Paulo, it turned out that my checked luggage hadn’t, which was bad. I filled out a lost luggage form, and the staff said they’d try to get my bag to the international airport in time for my next flight if it turned up.

At the Buenos Aires and Auckland airports, I tried to find out whether my bags had made it onto the flight. The conversations would go something like this:

me: Hi, my bag got lost on a previous flight and I want to know whether it made it onto my current flight. Here is the lost luggage form with the bag tag number.
them: do you have a bag tag?
me: no, they took the tag when processing the lost luggage report.
them: well, I can’t track the bag without the bag tag. You should have kept the tag.
me:

It was almost the same in Sydney, except the guy at the desk took a look at the form and realised that it had a bag tag number on it (the thing they wanted the bag tag for), and found that the bag had been put on my flight. Sure enough, it turned up on the carousel.

The rest of the trip was fairly uneventful, which was nice.