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 running git 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

  1. Author of the commit
  2. Date and time of commit
  3. Commit message
  4. 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 the bisect process. If you want to start again, you have to start from git bisect start. Remember, we have to run the reset command at the end of the bisect 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.

--

--

Learn any concept under 5 minutes. Subscribe to newsletter at https://5minslearn.gogosoon.com

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store