Saturday, October 31, 2009

Using git with a cvs repository

Edited 11/29/2009: Even better than the below, use git-cvs.

My situation: Developing for a software project that uses a CVS repository, but I really want to use git because I do a lot of development off-line and hate waiting until I get back online to commit (it's a pain to separate different commits and I forget what I did and why).

Poking around the web I found a couple of good blog posts (linked to at points in this post where I found them useful) on how to use git locally for a cvs repository. I weaved them together and here's what worked for me.

Prerequisite Software

First you need to have the appropriate software installed:

Make sure you have git-core package with cvsimport functionality:
% git help -a | grep cvsimport
cvsimport merge-recursive status

Make sure cvsps is installed, which it wasn't in my case:
% cvsps
cvsps: Command not found.
I installed on my Mac using port:

% sudo port install cvsps


And then I found it:
% rehash
% cvsps
Can't open CVS/Repository: No such file or directory

Overview

The image at right shows how the structure works. You start with your main cvs repository. You import this into a git staging repository. You don't do any work in the staging repository, it exists solely for importing from git.

You will then clone the staging repository into a working git repository where you will do all your development.

Then you'll have another cvs checkout of the main cvs repository. This is were you will integrate your changes made in your git repository back into cvs.

Kudos to Takis Blog for much of the mechanisms here, but note that this blog post is somewhat dated and the command syntax has changed (e.g. 'git-cvs-import' is now 'git cvs-import').

Set up

Make the git staging repository ("staging.git") and import the CVS repository (which for the sake of this post I'll call 'repo'):

% mkdir staging.git
% cd staging.git
% git cvsimport -p -x -v -d $CVSROOT repo
Add a tag to indicate current synch point with CVS:

% git tag CVS-LAST-EXPORT
Make your git working directory ("repo.git"):
% cd ..
% git clone -l staging.git repo.git
And check out your cvs staging area:
% cd ..
% cvs checkout -d staging.cvs repo
Pulling changes from cvs into git

If changes are checked into CVS, pull them into your git staging area by doing the following (the same command you used to create it in the first place).
% cd staging.git
% git cvsimport -a -p -x -v -d $CVSROOT foo
Then go to your working git directory and pull them in:
% cd ../repo.git
% git pull
Resolve any conflicts and commit them:
# edit files
% git commit -a
Pushing changes from git to cvs

Start by making sure your cvs staging repository is up to date:
% cd ../staging.cvs
% cvs update
Now examine the list of commits back to last CVS-LAST-EXPORT tag and commit all that you want to commit:
% setenv GIT_DIR ../repo.git/.git
% git log CVS-LAST-EXPORT..
# For each git commit id, execute the following
% git cvsexportcommit 547f3018a8f951dcc4187e5f171b337bfbad1883
# The last line of the above comment, will be a 'cvs commit' which you will execute:
% cvs commit -F .msg 'README'

Optionally you can add '-c' after "cvsexportcommit" and it will automatically commit for you if the change looks good.

When you are done, mark the last commit you exported with a tag:

% git tag CVS-LAST-EXPORT

Don't forget to unset the GITDIR environment variable when you are done:
% unsetenv GIT_DIR
After committing to cvs, go back and import the changes into your git staging and working repositories.

That should do it. I'm sure I'll update this post as I refine the process.

Update 11/1: Fixed typo with 'setenv GITDIR' command. Added CVS-LAST-EXPORT tag. Fixed '-p x' to '-p -x' options with 'git cvsimport'.
Update 11/3: Added '-a' to 'git cvsimport' and note about '-c' to cvsexportcommit.