Version control system migrations are a fact of life for developers in any longer lived codebase. In fact, I’ve had a hand in quite a few migrations as newer, more workable version control systems became available. Also, like a lot of developers, I’ve got fragments of source code dating back quite some years floating around on various servers and development machines of mine. Not necessarily code that is still being used, but still code that I don’t want to just delete forever. Some of the oldest code I have uses RCS for source control and hasn’t been touched for a long, long time. As my machines generally don’t have anything as old as RCS installed for version control, I decided this might be a good time to migrate the code to my version control system of choice, Mercurial.
Unfortunately Mercurial can’t directly import source code with version control history from RCS, so the code and I had to make a detour via CVS.
Getting the code into CVS
CVS used RCS version files and comes with a script to import an RCS controlled source tree into CVS (rcs-to-cvs). First, I had to install both RCS and CVS. The conversion script requires both as it will automatically check in files that are not under RCS version control yet. Oh, and some of the files that were under RCS also needed checking back in anyway. On my FreeBSD server, this was as easy as using portmaster
to install the two ports: portmaster devel/rcs devel/cvs
.
Next, I had to ensure that all of the files that were under version control actually were checked in. That’s when I noticed that some of the files had been checked out since 1999, so it was probably about time to check in the changes anyway.
I did already have a CVS repository lying around, so at this point it was a simple matter of creating another project directory under CVS and running env CVSROOT=<directory containing CVSROOT> rcs-to-cvs <new project directory>
. Hey presto, everything was now imported into CVS. This process was quick any easy, other than identifying files that were checked out and checking them in before the migration.
Using Mercurial’s convert extension to import the source code from CVS
Mercurial has an extension called convert that is used for importing data from other source control repositories, with CVS being one of them. The first step I had to take was to enable the extension as per the documentation.
Pointing mercurial at the CVS repository itself did not result in a successful migration:
[107] gzip-mbox>env CVSROOT=$HOME/cvs hg convert -s cvs $HOME/cvs/gzip-mbox
assuming destination gzip-mbox-hg
initializing destination gzip-mbox-hg repository
/export/home/timo/cvs/gzip-mbox does not look like a CVS checkout
abort: /export/home/timo/cvs/gzip-mbox: missing or unsupported repository
Hmm. Looks like the extension wants a checkout, so I’ll create a temporary checkout. I was amazed that I even still remembered the command to do so (cvs checkout
, after setting the CVSROOT environment variable).
The second run against the temporary CVS checkout was much more successful:
next-cube% hg convert -s cvs ~/tmp/faxhelper
assuming destination faxhelper-hg
initializing destination faxhelper-hg repository
connecting to /export/home/timo/cvs
scanning source...
collecting CVS rlog
8 log entries
creating changesets
4 changeset entries
sorting...
converting...
3 Initial revision
2 Initial revision
1 Latest checkin before CVS conversion
0 .
Running hg log
in the resulting directory confirms that we have a valid Mercurial repository:
next-cube% hg log
changeset: 3:1cc67108e9d3
tag: tip
user: timo
date: Sun Mar 13 01:16:30 2022 +0000
summary: .
changeset: 2:3e9bbe195278
user: timo
date: Sun Mar 13 01:14:06 2022 +0000
summary: Latest checkin before CVS conversion
changeset: 1:88ac0082214a
user: timo
date: Sun Mar 13 01:12:32 2022 +0000
summary: Initial revision
changeset: 0:e8414eeb62be
user: timo
date: Fri Apr 02 16:31:16 1999 +0000
summary: Initial revision
What I didn’t realise on my first attempt was that hg convert
creates the repository directory automatically, so it wasn’t even necessary to create it beforehand. I did end up with a nested repository directory as a result, but that was quickly remedied.
A quick hg clone
later and I did get to see how well C++ source code that sat untouched since 1999 is holding up:
[140] gzip-mbox>gmake
c++ -g -Wall -c -pipe MBox.cc
In file included from MBox.cc:26:
./Files.hh:4:16: error: unknown type name 'string'; did you mean 'std::string'?
Line(const string &Input, const off_t Offs) : Line(Input), Offset(Offs) {}
^~~~~~
std::string
/usr/include/c++/v1/iosfwd:210:65: note: 'std::string' declared here
typedef basic_string<char, char_traits<char>, allocator<char> > string;
^
In file included from MBox.cc:26:
./Files.hh:5:5: error: unknown type name 'string'; did you mean 'std::string'?
string Line;
^~~~~~
std::string
Yeah, not so well.
Fortunately the repositories are relatively small, so the conversion doesn’t take a lot of time. At least with the codebase in the version control system I currently use, I now don’t have to remember - or rather rely on other websites to remember - archaic version control system incantations that I forgot because I haven’t used these systems for a long, long time.
Fun and completely irrelevant factiod - one of my professors in my first or second year of Computer Science studies was Walter F. Tichy, the creator of RCS. Also, it was interesting to see that GNU RCS is still being maintained - the latest release (5.10.1) is a bit over a month old.