git-svn, and thoughts on Subversion
We use Subversion for our revision control system, and it’s great. It’s certainly not the most advanced thing out there, but it has perhaps the best client support on every platform out there, and when you need to work with non-coders on Windows, Linux and Mac OS X, there’s a lot of better things to do than explain how to use the command-line to people who’ve never heard of it before.
However, I also really need to work offline. My usual modus operandi is working at a café without Internet access (thanks for still being in the stone-age when it comes to data access, Australia), which pretty rules out using Subversion, because I can’t do commits when I do the majority of my work. So, I used svk for quite a long time, and everything was good.
Then, about a month ago, I got annoyed with svk screwing up relatively simple pushes and pulls for the last time. svk seems to work fine if you only track one branch and all you ever do is use its capabilities to commit offline, but the moment you start doing anything vaguely complicated like merges, or track both the trunk and a branch or two, it’ll explode. Workmates generally don’t like it when they see 20 commits arrive the next morning that totally FUBAR your repository.
So, I started using git-svn instead.
People who know me will understand that I have a
hatred of crap user interfaces, and I have a special
hatred of UIs that are different “just because”,
which applies to git rather well. I absolutely
refused to use tla for
that reason—which thankfully never seems to be
mentioned in distributed revision control circles
anymore—and I stayed away from git for a long time
because of its refusal to use conventional revision
control terminology. git-svn in particular suffered
more much from (ab)using different terminology than
git, because you were intermixing Subversion jargon
with git jargon. Sorry, you use checkout
to revert a commit? And checkout also
switches between branches? revert is
like a merge? WTF? The five
or ten tutorials that I found on the ‘net helped
quite a lot, but since a lot of them told me to do
things in different ways and I didn’t know what the
subtle differences between the commands were, I went
back to tolerating svk until it screwed up a commit
for the very last time. I also tried really hard to
use
bzr-svn since I really like Bazaar (and the guys
behind Bazaar), but it was clear that git-svn was
stable and ready to use right now, whereas bzr-svn
still had some very rough edges around it and isn’t
quite ready for production yet.
However, now that I’ve got my head wrapped around git’s jargon, I’m very happy to say that it was well worth the time for my brain to learn it. Linus elevated himself from “bloody good” to “true genius” in my eyes for writing that thing in a week, and I now have a very happy workflow using git to integrate with svn.
So, just to pollute the Intertubes more, here’s my own git-svn cheatsheet. I don’t know if this is the most correct way to do things (is there any “correct” way to do things in git?), but it certainly works for me:
* initial repository import (svk sync):
git-svn init https://foo.com/svn -T trunk -b branches -t tags
git checkout -b work trunk
* pulling from upstream (svk pull):
git-svn rebase
* pulling from upstream when there's new branches/tags/etc added:
git-svn fetch
* switching between branches:
git checkout branchname
* svk/svn diff -c NNNN:
git diff NNNN^!
* commiting a change:
git add
git commit
* reverting a change:
git checkout path
* pushing changes upstream (svk push):
git-svn dcommit
* importing svn:ignore:
(echo; git-svn show-ignore) >> .git/info/exclude
* uncommit:
git reset <SHA1 to reset to>
Drop me an email if you have suggestions to
improve those. About the only thing that I miss from
svk was the great feature of being able to delete a
filename from the commit message, which would unstage
it from the commit. That was tremendously useful; it
meant that you could git commit -a all
your changes except one little file, which was simply
deleting one line. It’s much easier than tediously
trying to git add thirteen files in
different directories just you can omit one file.
One tip for git: if your repository has top-level
trunk/branches/tags
directories, like this:
trunk/
foo/
bar/
branches/
foo-experimental/
bar-experimental/
tags/
foo-1.0/
bar-0.5/
That layout makes switching between the trunk and
a branch of a project quite annoying, because while
you can “switch” to (checkout)
branches/foo-experimental/, git won’t
let you checkout trunk/foo; it’ll only
let you checkout trunk. This isn’t a big
problem, but it does mean that your overall directory
structure keeps changing because switching to
trunk means that you have
foo/ and bar/ directories,
while switching to a foo-experimental or
bar-experimental omits those
directories. This ruins your git excludes and tends
to cause general confusion with old files being left
behind when switching branches.
Since many of us will only want to track one
particular project in a Subversion repository rather
than an entire tree (i.e. switch between
trunk/foo and
branches/foo-experimental), change your
.git/config file from this:
[svn-remote "svn"]
url = https://mysillyserver.com/svn
fetch = trunk:refs/remotes/trunk
branches = branches/*:refs/remotes/*
tags = tags/*:refs/remotes/tags/*
to this:
[svn-remote "svn"]
url = https://mysillyserver.com/svn
fetch = trunk/foo:refs/remotes/trunk
; ^ change "trunk" to "trunk/foo" as the first part of the fetch
branches = branches/*:refs/remotes/*
tags = tags/*:refs/remotes/tags/*
Doing that will make git’s “trunk” branch track
trunk/foo/ on your server rather than
just trunk/, which is probably what you
want. If you want to track other projects in the
tree, it’s probably better to git-svn
init another directory.
Update: Oops, I forgot to thank
Mark Rowe for help
with this. Thanks Mark!
As an aside, while I believe that distributed version control systems look like a great future for open-source projects, it’s interesting that DVCS clients are now starting to support Subversion, which now forms some form of lowest common denominator. (I’d call it the FAT32 of revision control systems, but that’d be a bit unkind… worse-is-better, perhaps?) Apart from the more “official” clients such as command-line svn and TortoiseSVN, it’s also supported by svk, Git, Bazaar, Mercurial, and some great other GUI clients on Mac OS X and Windows. Perhaps Subversion will become a de-facto repository format that everyone else can push and pull between, since it has the widest range of client choice.