Introduction to Git Bisect

In this article, we will explore a mostly ignored Git Feature - the Git Bisect. Git Bisect allows you to search through the commit history of your project to find the commit that introduced the bug.

Git Bisect

Consider the situation where you introduced a bug in your project and have no idea where to look for it in the vast codebase. Git Bisect guides you by iterating over your previous commit until the last known commit and helps in finding the commit that introduced the bug using a process of elimination. Git Bisect employs binary search to iterate over the project commit history, marking each item as good or bad.

Let us create a sample project and knowingly introduce a bug. Consider the following code commit history.

# Commit 1
for(int i=0;i<10;i++)
{
    await Task.Delay(500);
    Console.Write($"{i} ");
}
# end
# Commit 2
for(int i=0;i<20;i++)
{
    await Task.Delay(500);
    Console.Write($"{i} ");
}
# end
# Commit 3
for(int i=0;i<30;i++)
{
    await Task.Delay(500);
    Console.Write($"{i} ");
}
# end
# Commit 4
for(int i=0;i<40;i++)
{
    await Task.Delay(500);
    Console.Write($"i ");
}
# end
# Commit 5
for(int i=0;i<50;i++)
{
    await Task.Delay(500);
    Console.Write($"i ");
}
# end

The application itself is a simple one, which prints numbers in ascending order with a delay. We are making changes in the code with each commit, and accidentally introduced a bug in commit 4. Since commit 4, the application doesn't work as desired. The developer is given a codebase with commit 5, and he has to figure out the erroneous commit and fix the bug.

He first looks into the commit history of the project using git log

commit 465a196ade5987b4735fce97065c2367e53171ea (HEAD -> master, origin/master, origin/HEAD)
Author: Anu Viswan <[email protected]>
Date:   Sat Oct 7 07:37:35 2023 +0530

    commit 5

commit 3d4b659530b237f1bd28a6552e8a7e3766e5354b
Author: Anu Viswan <[email protected]>
Date:   Sat Oct 7 07:37:22 2023 +0530

    Commit 4

commit b173307d4d55ab4adf000758feb4d280b7622183
Author: Anu Viswan <[email protected]>
Date:   Sat Oct 7 07:36:48 2023 +0530

    Commit 3

commit 73a8b3f69dd2616bc0a1367eac41f2ab5042fdb5
Author: Anu Viswan <[email protected]>
Date:   Sat Oct 7 07:36:33 2023 +0530

    Commit 2

commit 6ad42e6a2ebd16149fb8e10855c9da9ebd03bb99
Author: Anu Viswan <[email protected]>
Date:   Sat Oct 7 07:36:19 2023 +0530

    Commit 1

He has a head-start though. He is fully aware that "commit 1" (or any known good working commits) does not have the particular bug and hence the bug was introduced in one of the subsequent commits.

git bisect

Let us now start the process of elimination to figure out the faulty commit using the Git Bisect. Since we know the current commit, "commit 5" has the bug, we will consider it as bad. To begin the Git Bisect, use the git bisect start command.

> git bisect start
status: waiting for both good and bad commits

Git Bisect now expects us to mark a commit as good and another as bad to mark the range of commits to be examined. Since we know the latest commit commit 5 is bad, let us go ahead and mark it.

> git bisect bad
status: waiting for good commit(s), bad commit known

As shown in the status message, it now waits for a marked good commit, which we know is the first one commit 1 (for simplicity). We retrieve the ID of commit 1 from the git log results we got earlier.

> git bisect good 6ad42e6a2ebd16149fb8e10855c9da9ebd03bb99
Bisecting: 1 revision left to test after this (roughly 1 step)
[b173307d4d55ab4adf000758feb4d280b7622183] Commit 3

At this point, the Git Bisect has moved to a commit that is around the middle point of good and bad (oh yes, it is binary search) and has selected Commit 3 as the current one. We need to mark whether the current commit is good or bad. If we run the application now, we realize that the commit is still good and works as expected. So let us mark the current commit (commit 3) as a good one.

> git bisect good
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[3d4b659530b237f1bd28a6552e8a7e3766e5354b] Commit 4

Now the Git Bisect has moved to Commit 4. We will again check our application, and at this point, we realize it is faulty. Hence we will mark the current as bad.

> git bisect bad
3d4b659530b237f1bd28a6552e8a7e3766e5354b is the first bad commit
commit 3d4b659530b237f1bd28a6552e8a7e3766e5354b
Author: Anu Viswan <[email protected]>
Date:   Sat Oct 7 07:37:22 2023 +0530

    Commit 4

 Git/DemoApp/Program.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

As soon as we have marked commit 4 as bad, Git Bisect detects it as the first one with a bad commit or, rather, in other words faulty one. We can check the log to get details of the commit.

> git show  3d4b659530b237f1bd28a6552e8a7e3766e5354b
commit 3d4b659530b237f1bd28a6552e8a7e3766e5354b
Author: Anu Viswan <[email protected]>
Date:   Sat Oct 7 07:37:22 2023 +0530

    Commit 4

diff --git a/Git/DemoApp/Program.cs b/Git/DemoApp/Program.cs
index eb9d677..9573de8 100644
--- a/Git/DemoApp/Program.cs
+++ b/Git/DemoApp/Program.cs
@@ -1,5 +1,5 @@
-for(int i=0;i<30;i++)
+for(int i=0;i<40;i++)
 {
     await Task.Delay(500);
-    Console.Write($"{i} ");
+    Console.Write($"i ");
 }
\ No newline at end of file

As we observe the code above, we realize there was a mistake by the developer, and a bug was introduced as he accidentally modified the console write.

Since we had found our faulty commit, we can stop the git bisect by using the git bisect reset command.

> git bisect reset
Previous HEAD position was 3d4b659 Commit 4
Switched to branch 'master'
Your branch is up to date with 'origin/master'

The command switches the branch to the original head.

Git Bisect might NOT be an everyday tool that you might use, but when a situation arises, its significance cannot be underestimated.