Two Git Commands That Will Save You Time and Heartache


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) master@{0}: commit (amend): fourth commit
0e1655b master@{1}: commit: third commit
1021750 master@{2}: commit: second commit
4d2a2d8 master@{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) HEAD@{0}: checkout: moving from 0e1655be8fe3cbaab32dc25f96c242927ba6677f to recovered-branch
0e1655b (HEAD -> recovered-branch) HEAD@{1}: checkout: moving from master to 0e1655b
5f35ac5 (master) HEAD@{2}: commit (amend): fourth commit
0e1655b (HEAD -> recovered-branch) HEAD@{3}: commit: third commit
1021750 HEAD@{4}: commit: second commit
4d2a2d8 HEAD@{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')" >
git add .
git commit -m "Commit 1"
echo "print('doing something useful 2')" >>
git add .
git commit -m "Commit 2"
git tag "v1"
echo "print('doing something useful 3')" >>
git add .
git commit -m "Commit 3"
echo "print('doing something useful 4')" >>
git add .
git commit -m "Commit 4"
echo "raise Exception('Something bad happened.')" >>
git add .
git commit -m "Commit 5"
echo "print('doing something useful 6')" >>
git add .
git commit -m "Commit 6"
echo "print('doing something useful 7')" >>
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 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

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 tutorialgit 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)

Posted By