Git is a powerful tool that allows you to go back in time to any previous versions of your project. Using the git checkout
command, you’re able to go back to any reference (or ref for short) in the version history. A ref can be:
- A commit, identified by its hash:
3f37c5c6
- A branch:
feat/add-login-button
- A tag:
v1.0
- HEAD: A pointer to the tip of the current branch
But what if you don’t know the name of the reference you’re looking for?
Or worse, what if you don’t know what you are looking for?
In this post, we go through 3 Git commands you can use to efficiently navigate version history with practical use cases.
1. Git reflog shortnames
If the ref you’re looking for is a ref that you had previously checked out, then the reflog may help you. As HEAD jumps from commits, tags, branches through your repository, Git logs all the references it has pointed into the reflog.
git reflog
is similar to git log
, except that instead of displaying a list of commits, it will show a list of previous references, whether they are branches, commits, tags, and tell you from and to where you moved at each checkout command.
HEAD@{0}
is called a reflog shorthand, and it allows you to reference commits in relations to HEAD (or any other ref), instead of referencing them directly.
Here are a few examples of useful reflog shorthands to use:
git checkout HEAD@{1}
: Jump back to the previous value ofHEAD
(equivalent togit checkout -
)git checkout HEAD@{5}
: Jump back to the 5th previous value ofHEAD
Note that this is different fromgit checkout HEAD~5
which jump back to the 5th parent of the commitHEAD
is pointing to.git checkout HEAD@{1.month.ago}
: You guessed it, jump back to the ref that was checked out a month ago.git checkout feat/add-login-button@{2.weeks.ago}
: reflog shortnames work from any ref, not just HEAD
Note: As opposed to the version history, the reflog cache does expire. Refs older than 90 days will get cleaned up by Git’s internal pruning mechanism. To increase the expiration date of the reflog cache, set thegc.reflogExpire
property in your git config
2. Git bisect for binary search
If the ref you are looking for is the solution to a question similar to: “What is the first commit in my version history where X is true” then the ref can be efficiently found with git-bisect
Let’s say you want to find the first commit where a bug was introduced. You would do
git bisect start
git bisect bad # Flag HEAD as a bad commit containing the bug
git bisect good v1.0 # Can be any ref before the bug was introduced
Then Git will successively checkout a number of commits (no greater than log2 of the number of refs between HEAD and v1.0) and ask you to flag them as good
or bad
until it zeros in one the first commit where the bug was introduced.
See the git bisect documentation for more details, and a full example on how to use it.
3. Git Log Searching
If the ref you’re looking for is the answer to “What is the first commit that introduced this function?” or “What commit deleted this constant?”, then you can use the -S
flag (aka the pickaxe flag) of the git log command to find it.
git log -S myAwesomeFunction --oneline
This command will only list commits that changed the number of occurrences of the string myAwesomeFunction
- The commit where that function was created will be last in that list
- The commit where that function was deleted will be first in that list
However, commits where the body of this function was changed without modifying the number of references to it will not show up in that list.
For that use case, you can use the -L
flag of the git log command:
git log -L :myAwesomeFunction:./src/importantFile.js
This command will do two things:
- Try to figure out what the bounds of that function are
- Look through the history and show you every change that was made to the function as a series of patches back to when the function was first created.
Final Thoughts
Navigating version history, and finding exactly the commit that you want can be hard, but expanding your toolbox of commands to use, and knowing when to use them can help to improve your workflow.
What about you? What commands do you like to use to navigate version control?
Git’s version history is an incredibly useful tool and even more so the smaller and more atomic your commits are. Find out more from my other Git post: Improve your commit hygiene with `git add — patch`