Git bisect is a built-in part of git that is easy to use, incredibly useful when the need arises, but also relatively unknown and underappreciated.
git bisect will find the commit that introduced a change
So if you're ever in the situation where something has broken, or some other change was introduced, and you want to know where in the history it happened, git bisect is the tool to use.
How it works
You need to tell git some range of commits to look through, where at the start of this range the code is "good", and at the end is "bad". Bisect will then go through the commits and you must tell it at each stage whether the code is now "good" or "bad", until it finds the commit that first made it "bad".
It uses a binary search to efficiently step through the changes. You can go through hundreds of commits in just a handful of steps. This makes git bisect incredibly powerful and efficient.
If you have a shell command that will exit 0 for good and exit > 0 for bad (like a unit test), the entire process can be automated!
Here's a visual walkthrough of it.
Firstly, your commits look like this, you just don't know it yet:
Somewhere in there, the code went from good (bottom, green) to bad (red, top).
You start by telling it a known bad point – in this case the HEAD commit.
Next, you find some old commit where you know the code is good (in this example we check this by running a unit test) and tell bisect that this commit is good.
Git will immediately jump to the commit in the middle of these known good and bad commits. You find out whether this commit is good or bad, and inform git. In this case it is bad.
Because this commit is bad, all the subsequent commits must also be bad, so git gets to work on the remaining grey commits.
It jumps to the next "middle" commit (there is no exact middle so it will pick one of the two middle commits), and again you tell git whether this commit is good or bad.
In this case it is good, which means it can colour the rest of the graph in, and determine exactly which commit introduced the breaking change.
Automating it further
If you have an test you can run from the CLI that has a non-zero exit code in the case of a failure (like unit tests), you can automate the entire process.
Remember when we were in this position, where we had identified our one known good and one known bad commit?
If you now run this command
1git bisect run [your test command here]
It will perform the same binary search as before, but at each step, it runs the test command, automatically marking each commit as good and bad until it finds the first bad commit!
Try it yourself
I have prepared a repo where you can try this out to find which commit introduced a bug. The bug can be detected using this command:
which means you can try the fully automated version!