I’ve just shipped a new version of cvs-fast-export, 1.26. It speeds the tool up more, more, more – cranking through 25 years and 113300 commits of Emacs CVS history, for example in 2:48. That’s 672 commits a second, for those of you in the cheap seats.
But the real news this time is a Python wrapper called ‘cvsconvert’ that takes a CVS repository, runs a conversion to Git using cvs-fast-export, and then – using CVS for checkouts – examines the CVS and git repositories side by side looking for translation glitches. It checks every branch tip and every tag.
Running this on several of my test repos I’ve discovered some interesting things. One such discovery is of a bug in CVS. (Yeah, I know, what a shock…)
CVS uses the RCS state field value of “dead” to mark files that have been deleted. I found a case in the CVS repo of a project called “timidity” where a file had somehow ended up with state dead at rev 1.2, Exp (the default live state) at 1.3, and dead again at its final revision of 1.4. This confused CVS badly; a checkout keyed to a tag made after 1.3 but before 1.4 should have included the file but did not.
This showed up as a defect (mismatched file manifests) in cvsconvert. I spent half a day looking for where cvs-fast-export had gone wrong before I figured out that cvs-fast-export was doing what the metadata in the master said it should – it was CVS that had screwed the pooch. Annoying, but not very surprising.
This was an example of the most common kind of defect – files that had been deleted in CVS showing up under tags in gitspace when they didn’t under the corresponding CVS tags. Maybe eventually I’ll figure out how to perfectly match CVS’s behavior here, but it’s not really a big deal – there tend to be only a few of these per CVS repository and a few minutes’ work with reposurgeon will snip them off nicely.
Reassuringly, I found no cases anywhere of manifest mismatches or file differences at master or any other branch tip. Well, other than some trivial file differences due to CVS keyword expansion, and those can be suppressed.
The design approach of cvsconvert seems quite successful. I may try writing something parallel to it to sanity-check Subversion lifts.