Traversing a git tree
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.
# Go back one commit
git checkout HEAD~
# Go back 5 commits
git checkout HEAD~5
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.
You can find the definition of these aliases on GitLab.
Here’s how I use them:
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 theMY_GIT_SAVED_HEAD_FILE
environment variable. You can see what this resolves to withgit saved-head-file
. ↩