git bisect

28 Feb 2020 | categories: tips

prev: Y2K and the Year 2038 Problem | next: Creating a personal VPN using AWS

Git is great at helping you unfuck situations you’ve gotten yourself into, it is like Winston Wolf in that regard. During your development process have you ever realised a feature that was once working is now incredibly broken and you have no idea when this bug was introduced? Of course you have, you’re only human.

To carry on this string of recent posts on git I think it’s time we went over a very useful feature which, in usual git fashion, has a name that confuses and scares people. It’s not completely obvious what it is for so here is what the git man pages say about git bisect:

Use binary search to find the commit that introduced a bug

Yes, it is this simple.

If you are like me and you didn’t study computer science at university you might not know what binary search is, and that’s fine. We can’t know everything. I feel it would be good to get you quickly up to speed on what binary search is before talking about git bisect.

In short binary search is an algorithm for finding things quickly. If we have a sorted list of elements like so

1  4  7  9  13  42  81  102

And we are trying to find the number 42 we would start off looking at the middle element

1  4  7  9  13  42  81  102
            ^

Is this the element we are after? Nope, then is it smaller or larger than the element we are after? It’s smaller. Grand, we then focus our attention on the upper half of the list and pick the middle element again and repeat that process.

13  42  81  102
        ^

81 is larger than the element we need so we take the lower half of the list and repeat

13  42
    ^

We check the element and rejoice because we have found what we are after.

A real life example of this would be when you search for a term in the index of a book, you are looking for git so you flick to the back of the book and see you’ve landed in the K section. Well, you know G is before K so you flick back a few pages and see you’re in the D section, you go forward a few pages to G.

Database indexes also work in a similar fashion.

Big O

Binary search has a big O of O(log₂n) on average which means it’s quick. In our number example we found our element in 3 iterations which was pretty good but with even bigger numbers the efficiency of this algorithm really shines through.

If we have a list of 1,000,000,000 items it will take just 29 iterations to find what we are looking for. Fire a few more zeros on there and it takes 39 iterations to find an item in a list of 1,000,000,000,000 elements.

Back to git

Now you know the power of binary search you can understand how the tool does was it does. It will help you to (very quickly) hone in on the commit that has disrupted your work flow allowing you to publicly flog whoever authored the commit, but let’s be honest it was probably you anyway.

Examples are a great way to show git in action so let’s have a look at one of my side projects

code/death-ray [master] » git status --oneline

25fb6f8 (HEAD -> master) build sprinkler system
72ad2d3 replace robot sharks with live ones
809a5eb install robot sharks
a61be52 dig moat
7bb5349 unleash coronavirus
5bc64b2 download entire ABBA discography
a47b2a2 install speakers
2b1db02 top up live bees
6817a4a install puppy crusher
4c560a4 Initial commit

My death ray was working perfectly last week but right now it won’t even start, it was definitely working when I added those bees…

Starting git bisect

All you need to know before using git bisect is a commit in which your code is broken, this is usually your current commit aka the one HEAD is pointing at. And a commit in which your code was working as intended, which for this example was 2b1db02.

To start you run the start command and tell git which commit is the bad one, if you leave out the commit git will use HEAD.

code/death-ray [master] » git bisect start
code/death-ray [master] » git bisect bad

Now all that’s left to do is tell git which commit is good

code/death-ray [master] » git bisect good 2b1db02
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[7bb5349c36a3c49dae0a4100ca85258959180090] unleash coronavirus
code/death-ray [7bb5349] » _

Git has now checked out commit 7bb5349 and told us there’s about 2 steps left in this process and it is awaiting our input.

The Good, The Bad, and the Broken

Let’s revisit our git log again only this time I’ll show where our good and bad commits are and which commit we are currently on

25fb6f8 (HEAD -> master) build sprinkler system <-- bad
72ad2d3 replace robot sharks with live ones
809a5eb install robot sharks
a61be52 dig moat
7bb5349 unleash coronavirus                     <-- HEAD
5bc64b2 download entire ABBA discography
a47b2a2 install speakers
2b1db02 top up live bees                        <-- good
...

Looking at it here we can see git had picked a commit in the middle of our commit range, like we did with our simple binary search example at the start of this post.

Git is waiting for us to tell it whether this commit is good or bad, we check our code and nope this commit is a dud, we can mark it bad like we did before. There’s no need to pass a commit has as HEAD is used.

code/death-ray [7bb5349] » git bisect bad
Bisecting: 0 revisions left to test after this (roughly 1 step)
[5bc64b236d64f08d8af61116a4fa274e10830689] download entire ABBA discography
code/death-ray [5bc64b2] » _

Narrowing it down

Looking at our git log this is how git would view our commits, we have very quickly narrowed it down to two commits.

25fb6f8 (HEAD -> master) build sprinkler system < -- bad
72ad2d3 replace robot sharks with live ones     < -- bad
809a5eb install robot sharks                    < -- bad
a61be52 dig moat                                < -- bad
7bb5349 unleash coronavirus                     < -- bad
5bc64b2 download entire ABBA discography        < -- HEAD
a47b2a2 install speakers
2b1db02 top up live bees                        <-- good
...

We test our code and nope still broken. We mark it and move onto the last commit.

code/death-ray [5bc64b2] » git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[a47b2a2d3b09804b8dac6ecd250fb6d45e25d350] install speakers
code/death-ray [a47b2a2] » _

Damn bees

This is the final commit to check. We give it a whirl and yes, the once broken feature is working. That’s on me for thinking ABBA and live bees would play nicely together. We mark this commit as good and git triumphantly tells us this is the bad commit.

code/death-ray [a47b2a2] » git bisect good
5bc64b236d64f08d8af61116a4fa274e10830689 is the first bad commit
commit 5bc64b236d64f08d8af61116a4fa274e10830689
Author: Skip Gibson <evil.dr.skip@gmail.com>
Date:   Fri Feb 28 10:34:08 2020 +0000

    download entire ABBA discography

    :000000 100644 0000000000000000000000000000000000000000
    e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 A   file4373

After we have finished our investigation we need to get out of bisect mode using git bisect reset, which will checkout the position HEAD was at before we started sleuthing around in our commit history.

Conclusion

Git has your back. Don’t be afraid of it. Also bees are more complicated than you think.

prev: Y2K and the Year 2038 Problem | next: Creating a personal VPN using AWS @skipcloud