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.

  1. 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. 

  2. 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