Spot the culprit commit with Git Bisect
It happened on Friday (a build day), my team was working on final touch ups to deliver that iteration’s build. One of my team member was keenly looking at the code.
“What are you looking for? “, I asked him.
“Someone has made a change that affected a small part of my work leading to a bug. I’m looking for that change”, he replied.
“Well. How much have you progressed? Are you in a logical state to figure out that commit? “, I asked him.
“I don’t have any clue till now”, he replied.
“Git provides a way to trace back a bug from where it has occurred. Let’s explore together”, I replied.
In a few minutes, we were able to find the commit where the bug has originated. That’s because of one of the powerful command in git called “Git Bisect”.
Let’s learn about Git Bisect below
What is Git Bisect?
git bisect
uses binary search to find the commit that introduced a bug. It can also be used to find the commit that changed any property of your project. You define a bad commit (broken code) and a good commit (working code) and git bisect
helps you to quickly find that culprit commit either manually or automatically. But, you have to build and verify your project each time git
asks you to do.
Hope you’re bit confused. Let’s understand it with an example.
Let’s create a new shell script that adds two numbers and prints the result on console
I have created a file named sum.sh
and added the above code
Running the above file prints the result on the console as Total = 30
Let’s make some dirty commits before breaking the code
For each of the above line, I’ve made a commit. (Just to understand git bisect
clearly)
Let’s break the code now
I made the code to always print the result as Total = 0
and added the commit message as Broke the code in this commit
Let’s ensure our code is really broken by running the script
We broken our code successfully. Let’s add 10 more commits after our breaking code
Again for each above line, I’ve made a commit.
So, my final list of commits are as follows,
Finally, our broken code is nested heavily before and after 10 commits.
We’re ready to find that culprit commit. Let’s see git bisect
in action.
To begin git bisect
, we need to ensure that there's no changes left in staging area and working copy of our repo.
To start bisecting, we need to run
git bisect start
, which will not show anything on the console. But we can find the status of bisect by runninggit status
command
We need to let git know which are good and bad commits. I’m setting 09bb72c
(Latest commit) as bad commit
and 9e07971
(Commit where I added sum.sh file) as good commit
(Please refer above log screenshot to identify those commits).
My HEAD is currently at my latest commit. So, let’s set this as bad
commit.
Let’s checkout to 9e07971
and set that as good
commit,
git bisect
has started to work. Look at the message from git bisect good
command. From the message, we can clearly infer that git has checked out to a random middle commit ([Arunachalam] 1st commit before breaking code). It's also show that there are 10 revisions left and roughly 3 steps to figure out that culprit commit.
Let’s run the script and verify.
As it’s working properly, we can classify this commit as good commit
by running git bisect good
.
Immediately, after I consider that commit as good commit
, git has switched to the next random commit within that range (The commit where we were before till the commit which we marked as bad).
Let’s run the script again and verify,
That’s a bad commit (The output of total was not 30). After classifying it as a bad commit, git has checked out to next random commit.
Let’s run the script and verify again,
That was a bad commit too. So, classified it as bad commit
. Git has checkout to next closer commit.
Let’s run the script and verify again,
The result wrong again. So, let’s classify it as bad commit
.
“BOOM!!! I caught that culprit commit”, says Git.
It also adds
- Author of the commit
- Date and time of commit
- Commit message
- Files changed in that commit
Really Great. Isn’t it?
That’s the same feeling me and my colleague experienced on the day, we were stuck on that issue.
Run
git bisect reset
to return to your actual code.
As usual, few of my team mates raised questions about git bisect
to me on some occasions. Let's summarize them.
Kumar asked a good question, “What if I don’t know if a commit in the middle was good or bad? “.
That’s a good question, some people may face such scenario. Git Bisect provides a command to handle such scenario too.
git bisect skip
command helps you to skip the particular commit and move on to the next commit
“How can I get out of bisect if I messed up with it? Or I found the actual reason for the bug in the middle of bisect process? “, question raised by Udhaya.
Yes. Thanks for that question Udhaya. I feel that’s the most important thing everyone should know.
git bisect reset
command will reset thebisect
process. If you want to start again, you have to start fromgit bisect start
. Remember, we have to run thereset
command at the end of thebisect
process too.
“Can I see the list of commits which I’ve marked as good / bad in the middle of bisection process? “, beautiful question raised by Raman.
“Yes. You can”
git bisect log
command lists all the commits that we have marked as good / bad in the bisection process. Here's the list for the above scenario.
Curious Naras asked a crazy question.
“Remember, at the beginning of your session while defining about git bisect
you said, ' git bisect
helps you to quickly find that culprit commit either manually or automatically. '. How can a git bisect automatically find a culprit commit? ".
That resembles his sharp listening skills. Let me answer this extraordinary question.
There exists a powerful command called
git bisect run
. This helps to automatically find out the culprit commit.
If you have a script that can tell if the current source code is good or bad, you can bisect by issuing the command:
git bisect run my_script arguments
The script ( my_script
) should exit with code 0 if the current source code is good, and exit with a code between 1 and 127 (inclusive), except 125, if the current source code is bad. Exiting with code 125 will skip the bisect.
Let’s automate the above scenario.
I’ve created a new file named test.sh
and added the above code. The above code runs the script file ( sum.sh
) and get the output and verifies if the output is the expected or wrong. It returns 0 ( good code
) if the output is 30 or returns 1 ( bad code
) if the output is 0 and returns 125 ( skip
) if the output is neither of that.
Let’s try to find the same culprit commit with this automated script
You can notice that I’ve given two hashes after bisect start
command. Those are bad ( 09bb72c
) and good ( 9e07971
) commits. From the message you can see the bisect has started.
Let’s automatically find the culprit commit by running bisect run
“OooooooooH!!!”, Look at how crazy it runs.
The culprit commit was found in fraction of second. Yes. It found the commit in less than a second. But this may not happen for all. Because, when you’re working on a project, the actual verification scenario is to build the project or run test cases. Definitely a build / test command takes a lot of time to complete (Eg. yarn build / gradlew build).
That’s all about git bisect
. Hope you enjoyed reading this article.
Give a clap 👏 if you like this article. Subscribe to our newsletter to receive more such insightful articles that get delivered straight to your inbox.