TL;DR
- Start a session
git bisect start
Set the bad commit
Either the current commit you're checked out has the bug
git bisect bad
or a specific commit has the bug, for example,
dcf12e8
git bisect bad dcf12e8
- Set the good commit (e.g.
eff13d3
doesn't have the bug )git bisect good eff13d3
Run and test the application yourself (you have to tell Git if the bug is there or not)
Either the bug is present, then run
git bisect bad
or the bug is not present, then run
git bisect good
- Repeat step 4 until the first bad commit is found
Note: you can use tags instead of commit hashes as well.
When is it useful?
I usually find it quite useful when the two following conditions are met:
- The bug was not present in the latest release but it's present in the current release candidate
- I have no idea what is causing the bug (therefore I don't know where to look)
Imagine the following scenario. A new release (let's say 2.80) is about to be released and QA is doing regression. You're enjoying a deserved coffee after a job well done when, unexpectedly, you're tagged on Slack...you know that it's not a good signal...
So, you've just been informed that a blocker was found during regression and it has to be fixed. At this point, several thoughts go through your mind:
"Okay, hopefully, it's not my code that is breaking the app so I'm safe" (AKA throw one of your colleagues under the bus)
"Let's look at the ticket, it will surely be something easy to fix. D'oh! It isn't!"
Okay, you have no choice but to fix it. You read the ticket and the bug is odd. You don't have a clue why it's failing and what is causing it.
The only thing you know for sure is that 2.80 has the bug but 2.79 doesn't. Then, git bisect
is your friend in this scenario.
How it works under the hood
Git Bisect relies on the binary search algorithm to find the first bad commit (the commit that introduced the bug). So, let's start by starting the session:
git bisect start
Then, we have to let Git know which commit has the bug for sure and which one doesn't. We know that 2.80
has the bug and that 2.79
doesn't, so:
git bisect bad 2.80
(or git bisect bad
if the current commit has the bug)
git bisect good 2.79
At this point, our setup is something like this:
Then Git automatically checks out a commit in the middle and we have to run the application to check if the bug is present or not:
Let's say that the bug is present in C3 (dcf12e8
), then we know that the second half has the bug for sure, therefore, the bug was introduced somewhere in the first half. We have to let Git know that C3 has the bug:
git bisect bad dcf12e8
Now Git automatically checks out a new commit:
Let's say now that the bug is not present in C1 (eff13d3
), then we have to let Git know about this:
git bisect good eff13d3
So the first half is bug-free, therefore, it is discarded. Only the C2 commit is left and Git checks it out, we run the app and let's say that the bug is present. Bingo! The first bad commit has just been found (because C1 is bug-free):
Finally, Git will provide you with the hash (author, date, etc.) of the first bad commit.
Final thoughts
git bisect
is not the Holy Grail, sometimes even if you find the commit that introduced the bug, it's simply not enough to figure out what the problem is.
But, even so, it's a skill that can help you to discard possible scenarios and fence the problem.
Documentation
git-scm.com/docs/git-bisect
git-tower.com/learn/git/faq/git-bisect