GitTrac Migration

So you want to migrate from CVSTrac to GitTrac?

You're crazy, of course, but that's never stopped anyone.


The first and most obvious thing about this is that you're implicitly migrating from CVS to GIT. This is the point where you may want to rethink the whole idea.

Still with us?

Prep Your Code

You've got a pile of code under your CVS respository, in a bunch of different modules. In CVS, these modules are treated all as independent trees. With GIT, a single repository is one tree.

Single Repository Mode

What this means is that git-cvsimport expects to be handed a single CVS module, and if you want everything in one place, you need to create that single CVS module (no, you can't do it with "-a" or "&" tricks in CVSROOT/modules). That's what I wanted, so it needed a little fancy footwork to make it happen:

  mkdir /tmp/repo && cd /tmp/repo
  mkdir code
  cp -R <path/to/cvsrepository> code
  mv code/CVSROOT .

Note that this is the place to do any other repository cleanups you need. Also note that we can get away with this only because git-cvsimport ignores the contents of CVSROOT/history.

Multiple Repository Mode

If, on the other hand, you want to keep the concept of individual modules, you would git-cvsimport each module into its own distinct GIT repository, then point GitTrac at the directory containing all those repositories.

You could also have a separate GitTrac database instance for each GIT repository/CVS module.

Move Your Code

This should be the easy part.

  cd ~
  git-cvsimport -d /tmp/repo -k -m -v -i code

At the end of this, if all goes well, you've got a directory called .git. That's now your GIT repository.

Check Your Code

We're experienced software developers, so we're obviously not going to trust git-cvsimport to not completely destroy our codebase. We need to check it out and have a look at things.

  mkdir working
  cd working
  git-clone <path/to/.git> code

You've just checked out the equivalent to the last CVS HEAD into code/. Poke around. Run things like git-log on files and make sure you've got a history. Make sure your tags and branches are all there (git-show-branch helps here). See if all your code builds.

Fix Your CVS Dependencies

When you use CVS for too long, you inevitably start to make a lot of assumptions. You distribute ChangeLog's generated from CVS commit messages. You assume magic keywords in code (like $Id$) mean something. You may even assume you know which branch you're working on by looking at CVS/Tag. Really hardcore CVS users might even put those assumptions into Makefiles and build scripts.

By "you", I really mean "me". But I didn't come up with all those ideas myself, and if you read the same web sites I do then you're probably in the same amount of trouble that I am.

Unfortunately, I don't have the answers here. You need to evaluate whether you really need the same functions, and come up with your own solution.

Other things you may have done with CVS include exploiting some of the server-based scripting capabilities such as CVSROOT/commitinto, CVSROOT/loginfo, etc. These sorts of things will obviously not work the same way, if at all, in a distributed SCM like GIT.

Publish Your Repository

The GIT repository you've created is what's called a private repository. Normally, GIT projects are published to the world as public (or bare) repositories.

  git clone --bare working/code code.git

You need to place this code.git tree somewhere other users can access it. You also need to put it somewhere GitTrac can access it directly (through the filesystem). You were probably planning on doing this anyways, right?

There's more than one way to publish a GIT project. I chose to use git-deamon and the GIT protocol for publishing, but there's more than one way to do it.

  touch code.git/git-daemon-export-ok

git-daemon was configured to run from inetd.conf using:

  git stream tcp nowait cpb  /usr/bin/git-daemon
         git-daemon --export-all --inetd --verbose
         --export-all --base-path=/home/cpb/git /home/cpb/git

and we fetch code.git into a working copy via:

  git clone git://localhost/code.git

Of course, git-daemon doesn't allow you to publish changes that way.

  git push ssh://localhost/home/cpb/git/code.git master

GIT For CVS Users

If you're the only user of your codebase, you can do whatever you want. However, larger organizations may have to cater to less "nimble" developers and other users.

There's some useful documentation for migrating CVS users to GIT.

For the truly backwards users, a CVS server emulator is available which should allow existing users to continue using CVS with minimal disruption and retraining.

CVSTrac To GitTrac

Setting Up GitTrac

First, you need a GitTrac configuration. We assume you've already got a gittrac executable. In that case, we start by initializing the database:

  gittrac init /path/to/database code

Start the gittrac server. Just temporarily, we've got work to do later:

  gittrac server 8008 /path/to/database code

Browse to http://localhost:8008/, login as setup, and configure the GIT repository. In my example, we're pointing it at /home/cpb/git/code.git. Then import the repository. I normally like to drop back to the command-line and use:

  gittrac update /path/to/database code

but you may also just browse to the timeline or something. Have a look around, browse the file tree, look at checkins, and generally make sure you're happy with what you're seeing. If something got broken, you really want to fix it before going any further.

Import CVSTrac Data

If you were starting from scratch, you're already finished. The rest of us, however, have years of CVSTrac history which we don't want to lose, and ideally we'd like to have it all in the same place.

Ergo, we need to move all that CVSTrac data to GitTrac.

Many things won't have any issues. Wiki pages and attachments, for the most part, can be transferred directly. The user database and things like custom markups and tools are no problem.

Tickets and check-ins are going to give you trouble.

The trick is that CVS and GIT have entirely different concepts of what a commit looks like. Then, when we run this stuff through CVSTrac and GitTrac, we try to number everything as discrete checkins. CVSTrac tries to merge distinct commits into seemingly atomic operations, but if you've used it in a non-trivial environment you know that falls apart periodically.

So we've got two instances of *Trac containing more or less the same data, but the numbering scheme for the check-ins may be wildly different. And since things like tickets are cross-referenced with specific check-in numbers, a naive database copy from CVSTrac to GitTrac simple will not work.

Fortunately, this is something we can tackle with just a decent understanding of how the CVSTrac database works and an unreadable perl script. is my attempt at an automatic CVSTrac to GitTrac conversion tool. There's nothing too magical about it and you could certainly adjust for a SvnTrac migration.

The idea is roughly as follows:

  1. create a (many-to-one) mapping of CVSTrac checkin's to GitTrac checkin's.
  2. do the same thing with milestones. This isn't so easy since GitTrac and CVSTrac have different ideas of how to represent tags and such, so it may not be entirely complete.
  3. copy over the database tables as-is, except the ones GitTrac would have created from the repository (CHNG, FILE, FILECHNG).
  4. fix check-in cross-references. Some tables are easy, such as INSPECT and XREF. Some, such as the contents of Wiki markup, are really hard since we need to use regular expressions on text.

Fix Your CVS Dependencies

If your CVSTrac installation was relying on CVS-specific tools, you'll need to remove them or switch to GIT equivalents.

You'll also need to update reports. For example, if you've got a report which lists CVS branches, it's probably not going to work the same way with GitTrac. GIT revision numbers are longer, so if you're listing CVS file revisions in reports you may have layout issues.

Fix This Wiki Page

There's no way I've documented every possible problem with this process. If there's something you've encountered which doesn't work or if you've seen better shortcuts, please fix the documentation.