Projects - Git Cvs Sync

How to commit changes to a CVS repo but secretly use git to do most of the real work. Or: how to limit your interaction with CVS to the absolute minimum.

We will use the Emacs CVS repository as an example. It is assumed that you have commit access to the CVS repo.

Initial setup
Interacting with the git repo
Syncing to CVS
Special scenarios

Initial setup

So you've decided that you would much rather be developing Emacs using git. Good choice. Here is one way to set it up.

The directory tree will look like this:

emacs/
  git-emacs/       | This is the unofficial git repo for Emacs.
  cvs-emacs/       | This is the official CVS repo for Emacs.
  export-from-git  | This is a handy script for syncing changes.

First, make your top-level directory, which I have called emacs here.

cd /path/to/my/projects
mkdir emacs
cd emacs

Check out either git://git.sv.gnu.org/emacs.git or git://repo.or.cz/emacs.git. I use the former.

git clone $REPO
mv emacs git-emacs

Get the CVS repo as well.

cvs -z3 -d:ext:[email protected]:/sources/emacs co emacs
mv emacs cvs-emacs

Save the following script to the file export-from-git (name it whatever you like).

#!/bin/sh
#
# Export a commit from git to CVS.

if test -z "$1" || test -n "$2"; then
    echo Incorrect number of arguments
    exit 1
fi

export GIT_DIR=../git-emacs/.git
git cvsexportcommit -c -p -v $1

Interacting with the git repo

If you want to update the git repo, and you have not committed any local changes to the current branch, do git pull origin master.

If you have committed some changes that you'd like to keep, then do git fetch and git rebase origin/master, assuming you are in the master branch (or whatever branch in which your changes lie).

If you want to just throw away the local changes (for example, on a branch where you stage changes before committing them to CVS), do git reset --hard origin/master and git pull origin master. This allows you to see a pretty diffstat when the pull is done. To replicate that same diffstat, use git diff --stat master origin/master, replacing "master" and "origin/master" as appropriate to indicate the two markers to compare.

Branches can be set up however you like. Just be sure to use the --no-track option to the git branch command if a branch by the same name exists on the remote repo, and you do not want to automatically update it with git pull. If you are careful like this, you can omit the arguments to git pull to save typing.

Syncing to CVS

First, bring up the output from git log so that you know which commit IDs you want to sync to CVS.

Then update CVS using cd cvs-emacs; cvs up -dPR.

Now commit each change from oldest to most recent using the ../export-from-git script. The current directory should be cvs-emacs. The first and only argument should be a single commit ID. This script will try out the change, and then commit it if things are up-to-date.

Be sure to separate changes to ChangeLog files from other changes, because they will undoubtedly have to be merged manually. If a commit doesn't go through, remove the .dotest directory, fix up the file manually, and do cvs commit -F .msg -- file1 file2 ... if you want to keep the same commit message that is present in the git repo.

Special scenarios

If the remote git repo you are using happens to lag behind for some reason, and there is a commit that you want to check in, there is a way to deal with it.

First, make a new git branch in the git-emacs repo called something like fakesync and check it out. Then switch to the cvs-emacs directory and run the following:

export GIT_DIR=../git-emacs/.git
# If you need to remove files that should not be tracked in git,
# do the next command, otherwise skip it.
make maintainer-clean
git add .
git add -u .
git commit -m "Fake sync from Emacs"
unset GIT_DIR

Now rebase your changes against this new branch, run git log, and use ../export-to-git on any commits that you want to sync back to CVS. Afterward, you can either ignore this branch or remove it.