Git

Git Bisect Tutorial

Git Bisect Tutorial
Commenting your commits is an essential part of maintaining traceable code. It helps you track problems. However, finding a bug based on comments alone is a tedious task. It can take a long time to sort through all the history and figure out which commit is the culprit.

The git bisect command provides a way to speed up the bug detection process. It lets you pinpoint the problem faster. With git bisect, you can define a range of commits that you suspect have the problematic code and then use binary elimination methods to find the start of the problem. Finding bugs become faster and easier.

Let's set up an example and run a few test cases to see how it works.

Example Setup

In our example, we will create a test.txt file and add a new line to the file with each commit. After 16 commits, the final state of the file will look like this:

Here is my good code 1
Here is my good code 2
Here is my good code 3
Here is my good code 4
Here is my good code 5
Here is my good code 6
Here is my good code 7
Here is my good code 8
Here is my bad code 1 <-- BUG INTRODUCED HERE
Here is my bad code 2
Here is my bad code 3
Here is my bad code 4
Here is my bad code 5
Here is my bad code 6
Here is my bad code 7
Here is my bad code 8
Here is my bad code 9

In the above example, the bug got into the code after 8 commits. We kept developing the code even after introducing the bug.

You can create a folder called my_bisect_test and use the following commands from inside the folder to create the example situation:

git init
echo "Here is my good code 1" > test.txt
git add -A && git commit -m "My commit 1"
echo "Here is my good code 2" >> test.txt
git add -A && git commit -m "My commit 2 (v1.0.0)"
echo "Here is my good code 3" >> test.txt
git add -A && git commit -m "My commit 3"
echo "Here is my good code 4" >> test.txt
git add -A && git commit -m "My commit 4"
echo "Here is my good code 5" >> test.txt
git add -A && git commit -m "My commit 5 (v1.0.1)"
echo "Here is my good code 6" >> test.txt
git add -A && git commit -m "My commit 6"
echo "Here is my good code 7" >> test.txt
git add -A && git commit -m "My commit 7 (v1.0.2)"
echo "Here is my good code 8" >> test.txt
git add -A && git commit -m "My commit 8"
echo "Here is my bad code 1" > test.txt
git add -A && git commit -m "My commit 9"
echo "Here is my bad code 2" >> test.txt
git add -A && git commit -m "My commit 10"
echo "Here is my bad code 3" >> test.txt
git add -A && git commit -m "My commit 11"
echo "Here is my bad code 4" >> test.txt
git add -A && git commit -m "My commit 12 (v1.0.3)"
echo "Here is my bad code 5" >> test.txt
git add -A && git commit -m "My commit 13"
echo "Here is my bad code 6" >> test.txt
git add -A && git commit -m "My commit 14"
echo "Here is my bad code 7" >> test.txt
git add -A && git commit -m "My commit 15 (v1.0.4)"
echo "Here is my bad code 8" >> test.txt
git add -A && git commit -m "My commit 16"

Checking History

If you look at the history of the commits, you see the following:

$ git log
commit 3023b63eb42c7fadc93c2dd18b532a44a0a6888a
Author: Zak H
Date: Sun Dec 31 23:07:27 2017 -0800
My commit 17
commit 10ef0286d6459cd5dea5038a54edf36fc9bfe4c3
Author: Zak H
Date: Sun Dec 31 23:07:25 2017 -0800
My commit 16
commit 598d4c4acaeb14cda0552b6a92aa975c436d337a
Author: Zak H
Date: Sun Dec 31 23:07:23 2017 -0800
My commit 15 (v1.0.4)
commit b9678b75ac93d532eed22ec2c6617e5a9d70fe7b
Author: Zak H
Date: Sun Dec 31 23:07:21 2017 -0800
My commit 14
commit eb3f2f7b0ebedb732ecb5f18bee786cd3cbbb521
Author: Zak H
Date: Sun Dec 31 23:07:19 2017 -0800
My commit 13
commit 3cb475a4693b704793946a878007b40a1ff67cd1
Author: Zak H
Date: Sun Dec 31 23:07:17 2017 -0800
My commit 12 (v1.0.3)
commit 0419a38d898e28c4db69064478ecab7736700310
Author: Zak H
Date: Sun Dec 31 23:07:15 2017 -0800
My commit 11
commit 15bc59201ac1f16aeaa233eb485e81fad48fe35f
Author: Zak H
Date: Sun Dec 31 23:07:13 2017 -0800
My commit 10
commit a33e366ad9f6004a61a468b48b36e0c0c802a815
Author: Zak H
Date: Sun Dec 31 23:07:11 2017 -0800
My commit 9
commit ead472d61f516067983d7e29d548fc856d6e6868
Author: Zak H
Date: Sun Dec 31 23:07:09 2017 -0800
My commit 8
commit 8995d427668768af88266f1e78213506586b0157
Author: Zak H
Date: Sun Dec 31 23:07:07 2017 -0800
My commit 7 (v1.0.2)
commit be3b341559752e733c6392a16d6e87b5af52e701
Author: Zak H
Date: Sun Dec 31 23:07:05 2017 -0800
My commit 6
commit c54b58ba8f73fb464222f30c90aa72f60b99bda9
Author: Zak H
Date: Sun Dec 31 23:07:03 2017 -0800
My commit 5 (v1.0.1)
commit 264267111643ef5014e92e23fd2f306a10e93a64
Author: Zak H
Date: Sun Dec 31 23:07:01 2017 -0800
My commit 4
commit cfd7127cd35f3c1a55eb7c6608ecab75be30b208
Author: Zak H
Date: Sun Dec 31 23:06:59 2017 -0800
My commit 3
commit 3f90793b631ddce7be509c36b0244606a2c0e8ad
Author: Zak H
Date: Sun Dec 31 23:06:57 2017 -0800
My commit 2 (v1.0.0)
commit cc163adb8a3f7b7b52411db2b3d8bab9b7fb191e
Author: Zak H
Date: Sun Dec 31 23:06:55 2017 -0800
My commit 1

Even with only a handful of commits, you can see that it is difficult pinpointing the commit that started the bug.


Finding the Bug

Let's use git log -online to see a more cleaned up version of the commit history.

$ git log --oneline
3023b63 My commit 17
10ef028 My commit 16
598d4c4 My commit 15 (v1.0.4)
b9678b7 My commit 14
eb3f2f7 My commit 13
3cb475a My commit 12 (v1.0.3)
0419a38 My commit 11
15bc592 My commit 10
a33e366 My commit 9
ead472d My commit 8
8995d42 My commit 7 (v1.0.2)
be3b341 My commit 6
c54b58b My commit 5 (v1.0.1)
2642671 My commit 4
cfd7127 My commit 3
3f90793 My commit 2 (v1.0.0)
cc163ad My commit 1

We want to find the situation where the line “Here is my bad code 1 <- BUG INTRODUCED HERE” entered the picture.

Situation 1

Suppose we remember that our code was good until v1.0.2 and we want to check from that moment until the latest commit. We first start the bisect command:

$ git bisect start

We provide the good boundary and the bad boundary (no hash means the latest code):

$ git bisect good 8995d42
$ git bisect bad

Output:

Bisecting: 4 revisions left to test after this (roughly 2 steps)
[3cb475a4693b704793946a878007b40a1ff67cd1] My commit 12 (v1.0.3)

The bisect command has found the middle point in our defined range and automatically moved the code to commit 12. We can test our code now. In our case, we are going to output the content of test.txt:

$ cat test.txt

Output:

Here is my good code 1
Here is my good code 2
Here is my good code 3
Here is my good code 4
Here is my good code 5
Here is my good code 6
Here is my good code 7
Here is my good code 8
Here is my bad code 1 <-- BUG INTRODUCED HERE
Here is my bad code 2
Here is my bad code 3
Here is my bad code 4

We see that the state of test.txt is in the post-bug state. So it's in the bad state. So we let bisect command know:

$ git bisect bad

Output:

Bisecting: 2 revisions left to test after this (roughly 1 step)
[a33e366ad9f6004a61a468b48b36e0c0c802a815] My commit 9

It moves our code to commit 9. We test again:

$ cat test.txt

Output:

Here is my good code 1
Here is my good code 2
Here is my good code 3
Here is my good code 4
Here is my good code 5
Here is my good code 6
Here is my good code 7
Here is my good code 8
Here is my bad code 1 <-- BUG INTRODUCED HERE

We see that we have found the starting point of the bug. The commit “a33e366 My commit 9” is the culprit.

Finally, we put everything back to normal by:

$ git bisect reset

Output:

Previous HEAD position was a33e366… My commit 9
Switched to branch 'master'

Situation 2

In the same example, let's try a situation where another developer starts with the premise that the bug was introduced between v1.0.0 and v1.0.3. We can start the process again:

$ git bisect start
$ git bisect good 3f90793
$ git bisect bad 3cb475a

Output:

Bisecting: 4 revisions left to test after this (roughly 2 steps)
[8995d427668768af88266f1e78213506586b0157] My commit 7 (v1.0.2)

Bisect has moved our code to commit 7 or v1.0.2. Let's run our test:

$ cat test.txt

Output:

Here is my good code 1
Here is my good code 2
Here is my good code 3
Here is my good code 4
Here is my good code 5
Here is my good code 6
Here is my good code 7

We don't see any bad code. So, let git bisect know:

$ git bisect good

Output:

Bisecting: 2 revisions left to test after this (roughly 1 step)
[a33e366ad9f6004a61a468b48b36e0c0c802a815] My commit 9

It has moved us to commit 9. We test again:

$ cat test.txt

Output:

Here is my good code 1
Here is my good code 2
Here is my good code 3
Here is my good code 4
Here is my good code 5
Here is my good code 6
Here is my good code 7
Here is my good code 8
Here is my bad code 1 <-- BUG INTRODUCED HERE

We have again found the commit that introduced the bug. It was the commit “a33e366 My commit 9”. Even though we started with the different suspicion range, we found the same bug in a few steps.

Let's reset:

$ git bisect reset

Output:

Previous HEAD position was a33e366… My commit 9
Switched to branch 'master'

Conclusion

As you can see from the example, git bisect lets us pinpoint a problem faster. It is a great tool to enhance your productivity. Instead of going through the whole history of commits, you can take a more systematic approach to debugging.

Further Study:

https://git-scm.com/docs/git-bisect
https://git-scm.com/book/en/v2/Git-Tools-Debugging-with-Git

Emulate Mouse clicks by hovering using Clickless Mouse in Windows 10
Using a mouse or keyboard in the wrong posture of excessive usage can result in a lot of health issues, including strain, carpal tunnel syndrome, and ...
Add Mouse gestures to Windows 10 using these free tools
In recent years computers and operating systems have greatly evolved. There was a time when users had to use commands to navigate through file manager...
Control & manage mouse movement between multiple monitors in Windows 10
Dual Display Mouse Manager lets you control & configure mouse movement between multiple monitors, by slowing down its movements near the border. Windo...