Some time ago, I wrote that I needed to be less of a scaredy-cat about git (in particular so as to get back into KDE development, and the rat's-nest of git repositories there was scaring me off -- in that sense I'm a data point in what Paul Adams is writing about). The best way of learning is by doing, so I looked for something to do with git that would basically force me to use it regularly.

That really means "find a way to use git at work-work", since most of my development hours happen there now (largely administrative number crunching in Python).

A bit of background: at work-work we have a central SVN repository. It has a non-standard naming scheme: trunk is called development, and branches are in the releases subdirectory. The only branches are for actual releases. For various reasons we are also using SVN 1.5, which means that we don't have any of the more-modern merge and branch features that SVN has grown. So feature work by the developers happens in trunk directly, not in feature branches, and we end up with some pretty confusing history of interleaved commits.

I have 38 minutes on the train between Arnhem and Utrecht that I could use effectively for development of small things: typo-fixes, message improvements, adding unittests, that kind of thing. But I don't want to end up with one big set of changes for all the little things I do on the train; I want sensible commits of one logical change after another.

So basically I want simple feature branches and offline commits. It needs to linearize history and integrate with a weird SVN setup. Previously I used Mercurial and its hgsubversion extension to get this effect; now I wanted to do the same with git, for learning purposes (and then I can futz with the KDE repositories again).

Most of what I eventually built to give me a nice git-based workflow that meshes with the central SVN repository is based on a series of blog posts
from TF Nicolaisen. They were really useful.

Anyway, my workflow now looks like this:
  • Pick a ticket N from our bug tracker (it's TRAC, with some customized statistics modules I wrote and a handful of third-party TRAC-hacks for planning purposes),
  • Update my git repo from SVN with git pull,
  • Start a git branch for my work on the ticket with git checkout -b ticket-N,
  • Do my thing, with as many commits and experimental branches as needed, and then clean up (remove debug-commits, maybe merge some small steps), with git rebase -i,
  • Rebase onto the updated upstream SVN with git svn rebase,
  • Push the whole thing into SVN with git svn dcommit,
  • Drop the branch, since it's in SVN now; the detached commits will get garbage collected eventually.
That's a cromulent git workflow, and except for excessive rebasing and the push to SVN at the end, usable for regular git work as well.

The setup I've ended up with is illustrated here; on the server side, there are three repositories: first is the central SVN repository. This is the official and canonical source and other developers commit to SVN normally. Then there's the git-fetch repository, which pulls revisions from SVN and puts them into git commits. This one has only the SVN commits in it. Third is a bare git repository, which is where I pull from and where the fetcher repository pushes things. This bare repository also has a few things that are not from SVN commits -- I push branches here if I want to share them over git with other machines I work on, and there are a few tags in it marking some events in the history of the repository.