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.)

  • Added what the structure of the repository roughly is on the remote server on the original post. Just wanted to clarify so I know for sure that I'll be correct in just copying CVSROOT. Jun 21 '19 at 21:12
  • Whoops, forgot to add a mention so you get notified. Jun 21 '19 at 21:25
  • Ah, I think I used the wrong term – I didn't mean the "CVSROOT" directory actually, but rather the one that contains your repository's files specifically (or even the root directory that contains all repositories).
    – user1686
    Jun 21 '19 at 21:39
  • So the folder that has all the source files with ,v after them is the correct one to copy? The CVSROOT folder with stuff like checkoutlist,v and editinfo,v can be safely ignored? Or should I grab both? Jun 21 '19 at 21:47
  • When I try this, I get cvs-fast-export: no commitids before 2019-06-21T16:39:24Z. (Which is actually about a half hour from now?) Jun 21 '19 at 22:12

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

Here is an effective tool I just used for a long delayed migration:


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.