A git-svn tutorial

Introduction

I have been using git-svn successfully for some time now, and I am generally pleased about the productivity improvements I get. I am documenting my workflow here, to serve as an example to anybody else that could be struggling with the admittedly complicated git commands.

Prerequisites and background

I'm using the MSYS version of Git, v1.5.5, on Windows Vista. This packaging is a good Windows citizen, giving you shell integration (if you want to), context menus in directories and two extremely useful GUI tools. Download from here (direct link).

Unfortunately, in v1.5.6 git-svn is broken, and the maintainers are waiting for someone to provide patches. This is one risk you're taking when using open-source software I guess. 1.5.5 works good enough for me, so I don't mind staying there for a while.

Setup

You start your adventures with git-svn by cloning an existing Subversion repository:

git svn clone url://path/to/repo -s

The -s flag assumes that your repository uses the "trunk, branches, tags" convention. If not, you have to specify manually which directories represent branches and tags, if you want Git to know about them.

This will take a long time, as it will fetch every single revision from SVN and commit locally. If for any reason it stops, you can resume with git svn fetch.

I always setup Git to not translate line endings for me, using git config core.autocrlf false. We don't have a strict policy about line endings, so I don't want to complicate matters.

When everything is finished, you should have the default master branch setup, and pointing to your SVN head. Verify that by running gitk --all. You should see a commit tree, with the SVN head tagged as remotes/trunk and with the master branch label next to it.

Day to day usage

Branching

In your day to day usage, you will be working against a local branch. There are two ways to setup one:

  1. The graphical way. Fire up gitk, select the revision you want to branch from (most of the time it would be remotes/trunk), right click, select 'Create branch here...'. You then have to "checkout" that branch, which you can do by right clicking its label.

  2. The command line way: git checkout -b a_branch_name. Note that the branch will happen from the where your current working copy is based on. I'm sure there is a way to specify a revision, but I'm using gitk all the time, since I like to see visually what's going on.

Normal usage

Once you have a branch, you can start editing code, and committing to Git as needed. I use Git GUI for that, as it gives a vastly better UI to review changes.

There are many ways to use Git: I usually commit when I have finished a "unit" of coding, amending to add small changes as I see fit. Git GUI is tremendous help, showing at a glance what's going on.

Interacting with Subversion

There are two things you want to do with Subversion: update your working copy with remote changes and committing your changes to the SVN repository. Here's how to do them:

Updating

To update your working copy, you use git svn rebase. This will make your history seem as you've really branched from the current SVN head, instead of your actual branching point. This means that your changes are still grouped together, and you don't have other people's changes mixed up with yours.

Dealing with conflicts

Occasionally, you may have a merge problem. Things here get tricky. You can setup a mergetool that will show you the correct files, but it's a bit of voodoo. I find that editing the offending file in a text editor gives enough information. The on-screen (command-line, actually) instructions are very helpful, and you can always undo everything by issuing git rebase --abort.

Committing changes

This is very simple: git svn dcommit will send every commit as a separate checkin into Subversion, complete with message. If you realize in horror that a commit message 3 commits back is wrong, or if you don't want to pollute your Subversion tree with 15 commits, you can use git rebase remotes/trunk --interactive which allows you to revisit every local commit you've made, changing messages, squashing commits together and more.

Various other useful bits

  • git gui blame somefile will give you a nice clickable blame, so you know where to point the finger.
  • git svn fetch will fetch svn revisions (branches included!) without updating your current branch.
  • git-svn doesn't handle SVN externals. I just checkout the external using plain SVN, and add the directory in .gitignore.
  • Speaking of .gitignore, you can generate one by running a git svn command which eludes me right now.

Wrap-up

Git has turned out to be very useful, if only as a glorified backup and undo system. It's also very fast and very space efficient, so you can have lots and lots of branches, and a complete history of your project, in almost the same space as an SVN checkout. Using the remote mechanism, sharing trees between two machines is very fast an easy.

Whenever I have to use plain Subversion, even with the very helpful TortoiseSVN front, I feel very inefficient. Truth be told, for some operations like tagging and branching in Subversion, I can't be bothered to learn the Git equivalent, so I will never completely drop Subversion!

Missed anything? Got suggestions? Let me know in the comments!