How I wound up using git

jdavidb on 2008-04-21T19:37:11

Based on this and this, I worked out a complicated process to use git and integrate with our team's CVS repository. I could use gitk to browse the entire CVS history; when I was ready to push to CVS every intermediary change I had made in git would go into CVS (albeit with inaccurate timestamps).

And it promptly broke. I had a catastrophic failure with cvsexportcommit (well, it probably wouldn't have been catastrophic if I'd been more experienced and had a bare clue what to do next), and at the same time I broke the build of our software (this wasn't git's fault) and spent way too long trying to figure out how far back to revert.

Way, way too complicated, too fragile, and too hard to even explain what I was doing.

Then while reading this blog entry, specifically the section on just using git to save state in a directory, I had a brainstorm and worked out a new, simpler procedure. Interestingly enough, it's not too much different from how I was trying to make SVK work with CVS. Except for the fact that it works. :)

The new method does not turn every single git commit into a CVS commit, but that's probably okay since:

  1. People probably didn't want to see that many commits from me
  2. The timestamps on the commits were wrong and deceptive, anyway.
  3. It scared me to death to push 20 commits and worry that everything was going to fail on commit 16. Which it did, once.

The new method also doesn't turn every CVS commit into a commit in my local git repository. Oh, well; I probably didn't need that, anyway, although it was super cool. Instead, I just get daily (or so) CVS snapshots on the branch that tracks CVS. Which is not bad, at all. Maybe by the time I need something more (if I ever do) the tools will be less fragile, or we'll be using something better than CVS here.

Here's the method I worked out:

  • Make myself a fresh CVS checkout:
    $ cvs -d $CVSROOT checkout -d /path/to/wc $module
    $ cd /path/to/wc
    $ cvs update -AdP  # prune the numerous useless empty directories
  • Turn this checkout into a git repository:
    $ git init
    $ echo CVS > .gitignore  # Ignore all CVS subdirectories
    $ git add .
    $ git commit -m'Initial import'
  • Teach git to ignore build artifacts:
    $ ant [build...]
    $ git status | grep '^#   ' | awk '{print $2}' | sed 's/\/$//' >> .gitignore
    $ git add .gitignore
    $ git commit -m'Ignore build artifacts

From there I made a branch named local with local changes that I want to work with but which the team will probably never want (hence, changes which will never be committed to CVS). I do work on branches off of the local branch; when I'm ready to commit I use git-rebase to pluck those branches off of local and rebase them onto master; i.e., subtracting my local changes. Then I commit from there and delete the branch. Meanwhile, whenever I want to update from CVS (including after committing my own changes) I checkout the master branch and perform the update, and use git merge to pull changes down into local and then again into work branches as needed (and if desired).

Seems to be working great so far. I've got two separate small work projects going right now on separate branches and the changes don't conflict with each other a bit, and I'm a lot happier and more willing to try things like this than I was a few weeks ago.