Save the day with git bisect

Git is probably the one single skill/technology keyword that can be found on CV of almost all people working in the Software industry. It is one of the first things everyone learn even before starting to write the code. This is mostly because of the fact that it is the most widely used version control system, it is easy to learn, and it is very powerful. Here in this article, I don’t intend to discuss about complex or very advanced features, but to talk about a real scenario that I faced recently and how I used one of these features to solve the problem, even though I should simplify the case. But to start, have you ever heard of git bisect?

The Case

If you have ever worked on a repository in collaboration with at least one other developer, you should be familiar with cases like conflicts when more than one person works on the same content. In these cases, git asks you to choose either of the versions to continue. Sounds like it can avoid the disaster before it happens. But what if you don’t touch the same content, but yet manage to conflict with each other’s work? Let me explain the case with a simple example.

I have a very simple git repository with a single text file. The file content is just some simple strings that are appended by each developer depending on the situation. (Think of the file as a CHANGELOG file). This is the content of the file:

By Hassan - line 1
By Hassan - line 2
By John - line 1
By Hassan - line 3

And we have a bash script that reads the file and prints number of lines per each developer. This is the content of the script. It basically fails if the given developer name has no lines in the file.

#! /bin/bash
if [[ $(grep "$1" file.txt | wc -l) -eq 0 ]]; then
    exit 1
fi

So our repository looks like this:

$ ls -lha
total 48K
drwxr-xr-x   3 USER GROUP 4.0K May 10 09:56 ./
drwxrwxrwt 411 USER GROUP  36K May 10 09:55 ../
-rw-r--r--   1 USER GROUP    0 May 10 09:55 file.txt
-rwxr-xr-x   1 USER GROUP  122 May 10 10:16 script.sh*
drwxr-xr-x   7 USER GROUP 4.0K May 10 09:56 .git/



The Problem

Some weeks ago, our small team added a new junior to the team who has to deal with some code, and then update the file.txt with their content. Due to miscommunication, the new member assumed that they should change the style so the content of the file doesn’t contain names anymore. This change was not seen during the PR review and went up directly to the production. Looking at the git log, you see that the faulty commit is somewhere in the history, but no-one yet knows why tests are failing now.

$ git log --pretty=format:"%h%x09%an%x09%s
3ca5ac0 John   Add new line
113c956 Hassan Add new line
6838abf John   Add new line
176606b Junior Update format
0ddf3cf Hassan Add new line
e71809e Hassan Add new line
ae217e2 John   Add new line
237198d Hassan Add Script and File



Troubleshooting

I, as a senior developer, now have to find when the issue introduced. I don’t have any initial idea when and how this happened. As far as I know, everything had gone according to the plan until the problem started to show up. I may have a rough estimate about the healthy commits where the code was working just fine, but I don’t know when things failed and how many commits are made on top of them. So here comes the git bisect to the rescue.

Step 1: Start the bisect

First thing to do is to start the bisect for the range starting from the point we are sure that is working fine and ending to the point we know things are broken. I start from commit e71809e as the place I am sure things were working, and end to HEAD which is the last commit in the history and is broken for sure.

$ git bisect start 3ca5ac0 e71809e~
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[176606bcdecd06e7aef5719bc50401b9af818a30] Update format

Step 2: Run test for each commit

And now I can start checking which commit is the one that breaks the script. I do that by git bisect run as below:

$ git bisect run ./script.sh "Hassan"

running  './script.sh' 'Hassan'
Bisecting: 0 revisions left to test after this (roughly 1 step)
[0ddf3cfe17e37a640a9f2dda7f406e46f279000d] Add new line
running  './script.sh' 'Hassan'
176606bcdecd06e7aef5719bc50401b9af818a30 is the first bad commit
commit 176606bcdecd06e7aef5719bc50401b9af818a30
Author: Junior <junior@our-team.com>
Date:   Fri May 10 11:04:35 2024 +0300

    Update format

    file.txt | 5 +----
    1 file changed, 1 insertion(+), 4 deletions(-)

bisect found first bad commit%

Step 3: Check findings

While we can already see the first problematic commit, we can see more by checking logs

$ git bisect log

# bad: [3ca5ac00780b0bad6bc1769af47450cb45ce9136] Add new line
# good: [ae217e2d5d19e86f84b399a5cf73be9510dd9c0e] Add new line
git bisect start '3ca5ac0' 'e71809e~'
# bad: [176606bcdecd06e7aef5719bc50401b9af818a30] Update format
git bisect bad 176606bcdecd06e7aef5719bc50401b9af818a30
# good: [0ddf3cfe17e37a640a9f2dda7f406e46f279000d] Add new line
git bisect good 0ddf3cfe17e37a640a9f2dda7f406e46f279000d
# first bad commit: [176606bcdecd06e7aef5719bc50401b9af818a30] Update format

Step 4: End the bisect

Now that we are in the faulty commit, we should fix our code. We can now update our script to match the new style in the file.txt, to avoid the failure in the system.

#! /bin/bash -e
if [[ $(grep "line" file.txt | wc -l) -eq 0 ]]; then
    exit 1
fi

This fix would be then committed once git bisect reset that would end the bisect.

$ git bisect reset
$ git commit -am "Fix the script to match the new format"

Conclusion

git bisect can be a life-saver when you have to dig into a long history or testing your changes can be timely. Just to imagine, you can think of the above example with 100 commits where each execution of ./script.sh could have taken 10 minutes. In this case, you definitely don’t want to do all the checks manually.

And at the end and as I mentioned in the beginning, git can be a tricky technology for many of us, as despite its simplicity, there are some features of that are not used very often despite their magical power in certain situations. I encourage you to watch the 2 following videos if you believe you know git, even though they may not immediately be useful to you.