Friday, November 07, 2008

Recovering history with git reflog

I use git-svn quite a bit at work. One of the nice things about this is that I can create perfect patch sets - instead of commit a whole raft of random chunks, I can polish a change until it works. Quite often I'll reset the HEAD to a few commits back, to get rid of changes that add crap like "printf" or were just bug-fix dead ends. You know, the usual cargo cult "I'll change this to see what happens", that once you understand the problem realise had nothing really to do with the root cause of the problem.

One of the side effects of this is that, after a while I tend to end up with lots of "dead" commits. While using gitk I can see these as little commit balls that aren't joined to anything, like the headless branchy thing in the image. If I close gitk, or click the "Reload" option then the commit tree is cleaned up to show only commits that have a head.

Occasionally, once I've finished a change, squashed all my commits and committed them to subversion, it turns out that one of those lost commits really should have been part of the patch set. Oops! I've closed gitk by now, so all those lost commits can't be seen. How can I get them back? Here's where git reflog comes to the rescue.

I didn't discover the wonders of git reflog until quite recently. Its man page is not too helpful in describing what it does: "Manage reflog information". What it actually does is print out a list of commits that are in a branch, or are in limbo. It doesn't distinguish between the two. So you can use it, together with gitk, to show your lost commits. For example I use this:
gitk --all `git reflog | cut -c1-7`
That's it. It shows all branches (--all) and all the commits in the reflog, so all those lost commits are reachable, cherry-pickable and branchable again. I just wanted to share that with the internet, because I know I'll forget it by next week :-)