Once upon a time, there was a young developer who worked on a small side project, after a few hours he created a working version and started to refactor the code to make it nice, tidy, and clean. He refactored the code for a couple of hours and kept working neatly in small and meaningful commits. When he was pleased with his work he ran the code again but oh no, something terrible happened… the code didn’t work.
We have all been there, for me it happened a week ago, I sat in my bed and stared at my IDE rat me out that it was I who wrote that buggy code, “I got only myself to git blame that’s for sure” I grinned with myself but after a second a light bulb popped with a new idea — 💡 what if I can use Git to traverse through all of my commits and hunt down my bug? fortunately, after a short search on Google I discovered that it is possible - using the simple git bisect command.
In this tutorial, we will explore the git bisect
command. How it works, why, and how to use it so hopefully you will find it useful and powerful as I do!
🔮Debugging with Git
As you may know for git everything is about commits, a branch for that matter is nothing but a pointer to a single commit. This allowed git to traverse through the commits and provide a couple of debugging tools to help us debug our code: File Annotation(a.k.a ‘git blame’) and Binary Search.
If you’d like to learn more about how git works under the hood and stores its data check out my recent article and git documentation for further reading.
File Annotation — git blame
File annotation shows the last commit to modify each line of any file and done using the git blame
command, this is often the most useful tool for tracking down a bug and know who is the one to blame. If you are not familiar with git’s file annotation I highly recommend you read about it form git documentation.
Binary Search — git bisect
A way to locate which commit introduced an issue using binary search through all the commits history of a particular branch. It’s done with the git bisect
command which checks out each commit allowing you to run a test to see if the issue exists in this commit. When you’re hunting down a bug but you don’t know where to look for it and there were hundreds of commits since the last good commit you remember - the git bisect
command is your answer.
📽 Simple Scenario
Let’s say you worked on a new branch…
🥳 You worked for some time until you finally reach a good point with a good working product.
😌 You are also a responsible developer who wishes to keep his tree clean so you probably added a few commits along the way and had the following commits history:
*-----*--*---271cb1c (current)
💎 You decided it’s time to start refactoring the code, make it nicer and clean, eventually you ended up with the following commits history:
*-----*--*---271cb1c-*-66e0075-*-...--*---9c07a95 (current)
😰 Unfortunately, your code isn’t working properly at the current commit, apparently somewhere along the way a part of the code got broken.
🧐 You know that it had definitely worked before the refactor — when you added commit 271cb1c
— meaning there is a bug in the code between commit 271cb1c
and 9c07a95
, the only question is…. How do you find it?
👑 Enter git bisect
.
🛠 How to use it
Inspired by Clint Eastwood I like to think about this part as the story of ‘The Good, the Bad and the Wizard’:
🧙🏼♂️ The Wizard
Run the git bisect start
command to startup the git bisect
wizard.
😈 The Bad
In git
the binary search works by repeatedly dividing in half the commits that could contain the bug and narrowing down the possible commits to one, this makes the scope of the search essential. The way to define the git bisect
wizard what is the searching scope is by running the git bisect bad
command which tells git
that the current
commit is broken and contains a contagious bug.
😇 The Good
The bad commit is only one end of the scope now we need to tell git
when the last known good commit was, we do that by running git bisect <good_commit>
command, for example in our case, the good commit was commit 271cb1c
so we’ll run the following command:
git bisect 271cb1c
After running this command git
will start checkout commits using binary search and allow at each commit to build the app and check whether or not the bug exists, all left for you to do is to make sure you tell git
the status of the last git bisect
command with either git bisect bad
or git bisect good
so it will continue to narrow down the possible commits, repeat until it finds the commit where the bug was introduced.
Let’s assume we checked out on commit 66e0075
and that we found out that the bug occurred before it so we run:
git bisect bad
🙌🏻 Git is kind
Now git
has all it needs to determine that the commit between commit 66e0075
and commit 271cb1cis
the commit where the bug introduced. When git
finally finds the commit with the bug it prints all the information it has about it to help you figure out what happened that may have introduced this bug, for instance:
$ git bisect bad
66e00756e887030abcbde19ac3e10a7c2942036a is the first bad commit
commit 66e00756e887030abcbde19ac3e10a7c2942036a
Author: Noaa Barki <noaa@datree.io>
Date: Sun Apr 26 12:10:56 2020 +0300
fix: added client
base/client.go
base/client_test.go
1 files changed, ......
create mode 100644 base/client_test.go
🧙🏼♂️The Wizard
Like every time-travel journey its important to go back to where it all began otherwise we might be checked out on a commit from the past and end up in a weird branch state. We go back to the present by running the simple git bisect reset
command which shuts down the git bisect
wizard and reset the HEAD
to where we were before we started.
🤖 Automation
This is the part when git bisect
really shines - one of git bisect
subcommand is the git bisect run
which accepts a command as an argument and allows to fully automate git bisect
!
git bisect run <cmd>...
After running this command git
automatically runs cmd
on each checked-out commit and uses the return value of that call to decide if the commit is good or bad until it will find the first broken commit.
How to use it
First, you tell git
the scope of the bisect by providing the bad and good commits:
$ git bisect start <bad_commit> <good_commit>
Then you git bisect run
with your command and let git
do all the work for you, for example:
$ git bisect start HEAD 991debbc8b9e05ddc217023a898109822a70dd60
$ git bisect run npm run test
The command can be any script that will exit 0 for “good commit” or non-0 “bad-commit”.
🌸 Summary
In this tutorial, you learned how to track down a bug using the git bisect
command. Moreover, you learned how to automate it and by that reduce debugging time significantly. I hope you’ll try git bisect
in the future when you’ll look for a nice solution for hunting down a bug.
Special thanks for my teammates at datreeio 🙏🏻
Top comments (6)
Hmmm Your Article and Idea Seems Very Nice, But Why We have to use something else if we can use the Git only, and Reverse the Commit that is causing the error? It is very simple to go to a specific commit and look for the error at that commit, instead of learning a new thing and then implementing that thing in the project.
And how do you know what commit is causing the error? If you don't find the error as soon as it was caused, you will need to check commits one by one or do some binary search. That's where git bisect helps you. It automates the binary search work.
First, thank you for your feedback😊
When you have a broken version of your code and you want to locate when(the specific commit) it got broken you will probably need to check-out each commit, build your app and check if the bug still exists, this called a linear search and while it’s very simple and easy to understand it can take a lot of time and effort when there are hundreds of commits. That is exactly what the git bisect command does only with binary search which is more efficient then a linear search.
As Elcio said the git bisect command helps you find the commit where the bug interduced. However, if you already know on which commit it happened then you don't need the git bisect at all. 😎
That's Very Nice To Mention Here and For Sure Looking Forward to Give it a try as well.
Git bisect is built into git, so it's not "something else".
That's really cool, I didn't know about it.
Thanks