Advent Day 24: The Reflog

December 24, 2018

This is day 24 of my Git Tips and Tricks Advent Calendar. If you want to see the whole list of tips as they're published, see the index.

There are precious few things in Git that save my bacon more often than the reflog. And knowing it's there encourages me to craft my commits, often rebasing them, into a manner that's easy to read for my collaborators.

I've mentioned before that on the libgit2 project, some of my collaborators like to review each individual commit in a pull request. This lets them focus on a single idea or expression, before moving on to the next idea, in the next commit. It's hard to code review a huge pull request, and breaking it down into smaller pieces helps with that significantly.

But it's not always easy to craft an easy-to-read pull request. It often means taking their feedback and going back to fix up prior commits, rewriting and rebasing.

But what happens if you get this wrong? If you've rewritten a bunch of changes, and you made some errors, how do you get back to the ones that you rewrote?

The reflog - running git reflog will tell you all the commits that you've had checked out, in reverse chronological order, and how you got to that commit.

For example, I've made some fixup commits for a pull request and I've decided to rebase them. Once my rebase finishes, my reflog looks like:

cab2a8240 (HEAD -> newfeature) HEAD@{0}: rebase -i (finish): returning to refs/heads/newfeature
cab2a8240 (HEAD -> newfeature) HEAD@{1}: rebase -i (continue): new feature for widget
3649929fa HEAD@{2}: rebase -i (continue): some new code
ff7d3d7c3 HEAD@{3}: rebase -i (pick): some new code
8a3a999bd HEAD@{4}: rebase -i (pick): refactor before new code
90345d266 HEAD@{5}: rebase -i (pick): update widget
77f1460fc (origin/master, origin/HEAD, master) HEAD@{6}: rebase -i (start): checkout origin/master
e43efed03 HEAD@{7}: commit: fixup! some new code

Looking at this, you can see where the rebase started, on the 7th line. It checked out origin/master to start rebasing the commits on top of that. Then it replayed (cherry-picked) my commits on top of that.

But the line before that, the 8th line, is where I was before I started the rebase. So if I got myself into trouble, and want to get back, I can just:

git reset --hard e43efed03

And now I can start my rebase over again, or make more changes, etc.

One of the great things about git is that it's very hard to get into a state where you've lost data. Even when you rebase changes away, those changes still exist, and the reflog will always show the changes that you've made throughout history.