How to convert a CVS repo to GIT using cvs-fast-export?

As another attempt, I'm trying to convert a CVS repository to GIT. cvs-fast-export was recommended. The only problem is, I have no idea how to actually do so. I have it built, but how do I call it? There's tons of flags explained here but that's more confusing than it is helping.

Is there a short explanation somewhere of the actual process of conversion?

The repository is remote, and I normally go in via SSH. I'm not sure if the folder structure of this project is normal. Here's roughly what it looks like:

  • (on the remote server)/somefolder/cvs/anotherfolder/

  • Contains: CVSROOT repo-I-want-to-clone-name

  • repo-I-want-to-clone-name contains an Attic, and all the source code files with ,v after them. (No CVSROOT)

  • CVSROOT contains an Attic, cleanlog.sh, cvsignore, checkoutlist, checkoutlist,v, editinfo, editinfo,v, commmitinfo, loginfo, taginfo, etc. (No source-code like files here)


You need to have a local copy of the CVS repository directory (containing all the RCS ,v files with history logs). If you have SSH access, use it to download the files via sftp/rsync/tar. If you only have a pserver URL, you need to use something like cvssuck to generate a local repository. In case the repository is hosted at Source­Forge, you can download the whole thing using rsync.

Once you have the RCS files, feed a list of the filenames to cvs-fast-export, and it will output a repository in the intermediate "Git fast-export" format

cd ~/cvsfiles
find . -name '*,v' | cvs-fast-export [some options] > ~/converted.fe

Note: Make sure to include any Attic directories, as they contain files which existed in old commits but were eventually "deleted".

(Besides that, however, there are no additional metadata files needed – each ,v file is completely self-contained, as it uses the same single-file history format as RCS does. The job of cvs-fast-export is to mingle those individual file histories into multi-file commits somehow.)

You can then make edits to the dump using reposurgeon (e.g. assign authors, squash split commits), and finally import it into Git using:

git init ~/result
cd ~/result
git fast-import < ~/converted.fe

The import will generate branches and commits, and will update the working-tree index, but apparently doesn't extract the working-tree files themselves: use git reset --hard or git checkout -f to do that.

(In theory, the same "fast-export" dump can also be imported by various other SCMs such as Mercurial, Plastic, or Bzr.)


Here is how I converted and verified my CVS repository named repos to using cvs-fast-export on Ubuntu 18.04.

Perform the conversion

First, create a "fast export" file from CVS:

sudo apt install cvs-fast-export

cd repos/    # The CVS repository
find . | cvs-fast-export -kb > ../repos.fe

Import the fast export file to Git:

cd ..
git init repos.git
cd repos.git
git fast-import < ../repos.fe

git fast-import creates a bare Git repo, so check out the working files:

git checkout -f

Verify the conversion

Create a CVS working copy with keywords not expanded:

cd ..    # Directory in which you started
export CVSROOT=$PWD/repos
mkdir checkout-cvs
cd checkout-cvs
cvs co -kb `(cd ../repos; ls | grep -v CVSROOT)`

Recursively prune empty directories, which do not appear in the Git version:

find . -type d | sort -r | xargs rmdir

The above gives an error for every non-empty directory, but that's fine.

Compare the CVS latest version to the Git latest version:

cd ..
diff -r  checkout-cvs repos.git