HOWTO: smooth CVS to SVN migration (and back again)
This page explains how I migrated the VideoLAN CVS repositories to Subversion while still allowing anonymous CVS access for users who did not want to move to Subversion. If you are a CVS user and have not yet fallen in love with Subversion, I suggest you have a look at this excellent project. In fact, I recommend to be familiar with Subversion before reading this document, because I may have missed important things.
The idea is to migrate the CVS repository to a Subversion repository
using cvs2svn
, disable CVS accounts (except read-only
accounts such as anonymous) and set up post-commit hooks to replicate
SVN commits back to the CVS repository.
First step: cvs2svn
Here are the preparatory steps to migrate a CVS module
wooyay
from the CVS root /cvs/stuff
to a new
SVN repository /svn/wooyay
:
$ svnadmin create /svn/wooyay
|
That’s all! Your SVN repository is created. The default layout is a
bit special but quite handy: tags are in tags/
, branches
are in branches/
, and HEAD is trunk
.
Don’t forget to backup your old CVS tree! It might be useful if something ever gets wrong.
Repository cleaning
Now that your repository is created, you can use Subversion’s magical powers to do whatever you want to the repository, such as removing and renaming branches or tags. These steps are not mandatory but you might find them convenient.
CVS branch names cannot start with a digit or contain periods,
and you end up with branches called v1_2_3
instead of
1.2.3
. And I don’t like that. Here is an example of what I
would do:
$ svn ls file:///svn/wooyay/branches
|
I also like to import .cvsignore files to Subversion properties and
set the "Id" keyword properties for files containing the "$Id:" special
string. If your repository is big, you might want to do this change only
for trunk/
and the still active branches.
$ svn checkout file:///svn/wooyay/trunk workingdir
|
The post-commit hook
This is the important part. My svn_cvsinject
script can be
used to reinject SVN commits into the old CVS directory. Use option
-r
to specify the revision to reinject, and -a
to do branch aliases. In our example, this would be the contents of the
/svn/wooyay/hooks/post-commit
file:
#!/bin/sh
|
It is advisable to run svn_cvsinject
in the background
because it can take a long time to finish. Also, make sure that all
users with commit rights (including the user svnserve might run as) have
write permissions on the CVS repository.
Here are the current svn_cvsinject
features:
- support for file creation and removal
- support for directory creation and removal
- support for simultaneous commits in different branches
- support for branch aliases
However, it also has the following current limitations:
- no support for new branches
- no user mapping when run from
svnserve
(but the user is mentioned in the commit log) - concurrent calls may break things (use locks)
- poor error handling
Conclusion
It works for me (tm), but I’d be happy to learn of other successful installations. And please tell me of failures as well, so that I can fix bugs!
If your CVS repository ever gets corrupted, you can reinject
every SVN commit by restoring your backuped CVS tree and calling
svn_cvsinject
again for every revision since you used
cvs2svn
.