Friday, September 19, 2008

CVS Is Dead, Long Live Subversion

Let's take a breather from C++ coding standards, and give a thought to version control. For years the best choice for a source code repository that anyone could make was CVS, which had these things going for it:
  1. It's free.
  2. You don't have to lock a file before working on it.
  3. It does pretty good merging.
  4. You can branch your repository.
  5. It doesn't require one full-time employee to manage it, like commercial systems do.
There were a few flies in the ointment. You could start a checkin and get some directories checked in, only to find that some other ones failed because of out-of-date files. Moving files essentially amounted to a delete and an add, losing the change history. You had to remember to check in binary files a special way. And tagging, branching, and locking were loaded with gotchas -- one wrong step and you were in the drink.

As luck would have it, the CVS developers didn't like flies in their own ointment, and some of them went out and developed a CVS-like revision-control system which takes care of a lot of the shortcomings of CVS: subversion. All the things that you like about CVS are there; many things that are tedious in CVS are simply no-ops in subversion.

The best example is branching. In CVS a branch tag will go on every file in the repository. When you make the tag, you have to be careful that you're tagging file versions that belong together -- which pretty much necessitates that you either branch off of -rHEAD or an exact date like -D2008-09-19 20:05. Hopefully that specification doesn't hit in the middle of someone's check-in and give you a mismatched set of files. There's some weirdness with adding files on a branch and later moving them to HEAD. And the version numbers of files on the branch are like 1.187.4.3 -- if you branch from a branch you get monstrosities like 1.148.24.1.2.2.

In subversion, branching is painless for the developer, and an extremely fast, no-gotcha operation on the repository. You just choose the numbered repository version that you want to branch from, and do an svn copy command to a different directory of your repository. Under the hood, the copy command is just a symbolic link, so it takes up almost no space. There's no danger of mismatched files, thanks to subversion's atomic checkins -- a commit either fails or succeeds, and the files aren't tagged with individual version numbers, there's just a single revision number for the entire repository.

Another beneficial side-effect of atomic commits is that when you're investigating why a change was made to a file sometime in the forgotten past, often you would like to see what other files changed at the same time -- or even just what other files looked like at that time. Of course both of those things can be discovered in CVS-land with a certain amount of effort. In SVN-land, they come for free: you can know what any file in the same revision looked like by checking out the file with that revision number; you can know what files were checked in at the same time as your file by running svn log -v on it.

Everytime there's some kind of forehead-slapping or head-scratching at work due to CVS/RCS, I have to say, "In the future, when we're using subversion, we'll look back at this and laugh". Ditch CVS as soon as you can and move up to subversion.

No comments: