Traversing a git tree

Apr 27, 2018

When using git, going back in history to examine the state of things
at some previous commit is a really standard task. And as such, this
is fairly easy to do, since in git each commit has a link to its
parent, and the syntax has been optimised for precisely this sort of
thing.

But after doing this, going back to where you were, going forward
in history, is much more difficult. Because while git commits have
links to their parents, they know nothing of their children.

I’m sure there is a perfectly good reason for all of this somewhere,
but I often feel like being able to move back and forth between some
points in a project’s history. So I made myself a set of aliases1
to do just that.

Setting a reference point

In order to move forward in git, you need to be able to specify
a direction. This is done with git save, which will store the
current position (the HEAD) into a persistent storage2. You can
then see what you’ve saved with git saved-head.

Once you’ve saved a position, you can clear it with git forget,
or jump back to it with git load. The trick here is that git save
can detect whether HEAD was on a branch (in which case it stores the
name of the branch) or detached at some other point (in which case
it stores the commit SHA). So using git load means you’ll go back
to being on the branch if that’s where you were when you used
git save.

Moving back and forth

With a reference point set, you can move back with git back, which
will move you to the previous commit, or give it a number to move back
the specified number of steps (as in git back 5).

Moving forward is simply a case of using git next, which will go one
step towards the saved position (or git next 5, which will do what you
expect).

Making things a little more automatic

When I started using this, I kept forgetting to save a position before
using git back, which would leave me stranded at some older commit.
Since I’m too lazy for this, git back now detects whether a position
has been saved, and if none has, it saves the current one before moving.

Likewise, when moving forward, git next will detect when you’ve reached
the target position, and automatically use git forget. This means that
you can use git back and git next without ever having to manually
save or forget anything.

Needless to say, there are probably countless pitfalls that I haven’t
considered when writing this, and I’m sure that there are others who
have written similar tools that do this much better. But this scratches
a personal itch, and was a lot of fun to write.

Time will tell whether I keep using it or not. But for now, at least,
it’s just what I wanted.

If you haven’t used aliases yet, you’re in for a treat: you can
assign global or project-specific aliases to specific commands,
which makes customising git to fit your own particular workflow
much easier. ↩

The storage I’ve mentioned is nothing but a file. By default,
this file is .git/MY_SAVED_HEAD, but it can be modified by
changing the value of the MY_GIT_SAVED_HEAD_FILE environment
variable. You can see what this resolves to with
git saved-head-file. ↩