Introduction
Git is a powerful and essential tool for anyone that writes code. It can also cause you great despair if you run the wrong command at the wrong time, so many of us keep our heads down and try not to get too fancy with Git. I actually find Git to be quite a joyful tool to use — it makes source control fun. Perhaps you feel a bit more neutral about it. At the very least, though, here are two git commands that will save you both time and heartache.
git reflog
We’ve all done it. Whether it was an ill-advised git reset --hard
, git commit --amend
with later regret, or a git rebase
gone awry, at some point or another we’ve all run some destructive Git command that we later wished we hadn’t. Fortunately, the reflog is here to the rescue!
Git tracks changes made to branch tips and other references in the “reflog”. Lets see it in action. Create a Git repository and run these commands in a bash shell:
echo "one" >> ./foo git add ./ git commit -m "first commit" echo "two" >> ./foo git add ./ git commit -m "second commit" echo "three" >> ./foo git add ./ git commit -m "third commit" echo "oops" > ./foo git add ./ git commit --amend -m "fourth commit"
Maybe that last command was a mistake — you didn’t mean to amend. Check the commit log for master:
git log
The output should look like this:
commit 5f35ac5ddedfdd432bb6f66b13907ddd1ff58423 (HEAD -> master) Author: Raymond Lam <[email protected]> Date: Sun May 22 23:31:34 2022 -0400 fourth commit commit 102175066a4df49ca9a29d17b0053d6f9bb61734 Author: Raymond Lam <[email protected]> Date: Sun May 22 23:31:34 2022 -0400 second commit commit 4d2a2d880f69726ca3f61c68a5f9ec33ec63340d Author: Raymond Lam <[email protected]> Date: Sun May 22 23:31:34 2022 -0400 first commit
It seems like that “third commit” is gone, along with hours of work! Never fear, though — run this command:
git reflog show master
The output from the last command should look like this:
5f35ac5 (HEAD -> master) [email protected]{0}: commit (amend): fourth commit 0e1655b [email protected]{1}: commit: third commit 1021750 [email protected]{2}: commit: second commit 4d2a2d8 [email protected]{3}: commit (initial): first commit
This is a log of every update to the master branch tip. (Your actual commit hashes may be different.) You can see when master was updated to “second commit”, and then updated to “third commit”, which you thought you lost, and then updated to “fourth commit” (and notice the indication that “fourth commit” is commit amendment). That “third commit” is the one you want back, so to put master back on that commit, go ahead and run:
git checkout 0e1655b git checkout -b recovered-branch
(Your commit hash will be different.)
From here, you can integrate recovered-branch
back into master however you wish.
Interestingly, the reflog tracks other references besides branch tips, including HEAD
. Run:
git reflog
which is shorthand for:
git reflog show HEAD
The output should look like this:
0e1655b (HEAD -> recovered-branch) [email protected]{0}: checkout: moving from 0e1655be8fe3cbaab32dc25f96c242927ba6677f to recovered-branch 0e1655b (HEAD -> recovered-branch) [email protected]{1}: checkout: moving from master to 0e1655b 5f35ac5 (master) [email protected]{2}: commit (amend): fourth commit 0e1655b (HEAD -> recovered-branch) [email protected]{3}: commit: third commit 1021750 [email protected]{4}: commit: second commit 4d2a2d8 [email protected]{5}: commit (initial): first commit
As you can see, every update to HEAD
is tracked, including not only commits but branch checkouts. This is a pretty comprehensive log of Git activity in a repository and could come in handy.
git bisect
A customer calls customer support to report a bug that is blocking a critical workflow. Minutes later, another customer calls reporting that same bug. And then another, and another, and soon the support team finds itself inundated with calls from unhappy customers who suddenly cannot use the app that your team maintains. You are on call, and you get paged.
The pressure is on — you have to find and fix the bug before your service’s uptime SLA is breached. Debugging and fixing forward might take too long, and for whatever reason rolling back to yesterday’s deployment is impractical. The best thing to do would be to find the commit that broke the app and revert, but there have been a lot of commits between yesterday’s and today’s deployments. You start to panic a little…
We’ve all been there. The task is daunting, and the pressure is high. Fortunately Computer Science saves us. Don’t go through every commit — use a binary search to find the bad commit and save the day in logarithmic time! Take a known good commit, a known bad commit, checkout the commit in the middle, and test. If the bug is there, then that’s the new bad commit; otherwise, that’s the new good commit. Find the commit in the middle, test, and repeat, until you zero in on the bad commit.
This still sounds like a lot of work. Fortunately, git bisect
will do a lot of it for you! Let’s give it a try. Initialize a Git repository and run these commands in bash to set up our scenario:
echo "print('doing something useful 1')" > useful_program.py git add . git commit -m "Commit 1" echo "print('doing something useful 2')" >> useful_program.py git add . git commit -m "Commit 2" git tag "v1" echo "print('doing something useful 3')" >> useful_program.py git add . git commit -m "Commit 3" echo "print('doing something useful 4')" >> useful_program.py git add . git commit -m "Commit 4" echo "raise Exception('Something bad happened.')" >> useful_program.py git add . git commit -m "Commit 5" echo "print('doing something useful 6')" >> useful_program.py git add . git commit -m "Commit 6" echo "print('doing something useful 7')" >> useful_program.py git add . git commit -m "Commit 7" git tag "v2"
The v1
and v2
tags represent the last good deployment and a bad deployment, respectively. The exception is obviously the bug. To get started, run:
git bisect start
Next, we must give Git a good commit and a bad commit. Run:
git bisect good v1 git bisect bad v2
You can indicate any kind of reference (tags, branches, commit hashes, HEAD
, etc.) as the good and bad commits. The output should look like this:
Bisecting: 2 revisions left to test after this (roughly 1 step) [5082142d6835ac7af2186d717c3c9da29049bd55] Commit 4
This means that “Commit 4” is currently checked out. Test this commit (which is to say, run useful_program.py). If the bug manifests, run:
git bisect bad
If the bug is not present, run:
git bisect good
Each time, Git will checkout another commit for you to test. (It will also tell you roughly how many commits are left to test.) Repeat this process until the output looks like:
commit edaf593d26877117dcee190fd3f554ebe28f23d2 Author: Raymond Lam <[email protected]> Date: Sun May 22 22:26:17 2022 -0400 Commit 5 :100644 100644 f01bb32ee5cd9e62a8572f9977f60f2c8d9a1acb 2007f74b3a0017f04780284afc0190445346e99b M useful_program.py
Lo and behold, “Commit 5” is the commit where we introduced the exception! Although there were 6 commits since the last good deploy, we only had to test 3 of them. Revert the bad commit, deploy, and celebrate the elegance with which you brought an end to a crisis.
Other Resources
There are plenty of good resources out there to help you use, understand, and get the most out of Git.
A couple of years ago, Taylor wrote a Tutorial on Git and Gitlab for GinkgoBits. It has been a great resource for our software engineers just starting out at Ginkgo Bioworks.
The Atlassian Git Tutorials are quite good. My favorite of these is the Git Reset tutorial — git reset
is one of those commands that is really powerful and dangerous (though not that dangerous thanks to git reflog!), and everyone uses it, and almost nobody really understands it. This tutorial does a great job of dispelling the mystery of this command.
Finally, there is a popular cheatsheet for getting yourself out of a jam when you run the wrong Git command. Dangit, Git!?! is the PG version of it.
I meant it when I said that Git makes source control fun, and I hope that after reading this you come closer to agreeing with me.
(Feature photo by Keila Hötzel on Unsplash)