Finding Bugs with git bisect (Advanced)
Advanced: safe to skip
This module covers more advanced Git features. If you are still getting comfortable
with the everyday
add / commit / push / pull / branch workflow, feel
free to skip it for now and come back once those feel familiar.The problem
Sometimes you discover that something which used to work is now broken,
but you have no idea which of the last few dozen commits introduced the problem.
Reading every diff by hand would be slow and error-prone.
git bisect solves this by performing a binary search through your history.
You tell it one commit that is known to be good and one that is known to be
bad, and it repeatedly checks out a commit half-way between them and asks you
whether that one is good or bad. Each answer halves the range, so even hundreds of
commits are narrowed down in a handful of steps.A worked example
Imagine our
climate-analysis project has a test that used to pass:python -m pytest tests/test_conversion.py
but now fails. We know it worked when we tagged
v1.0, and it is broken at the
current HEAD. Let's find the culprit.(A tag is just a human-readable label for a commit -
v1.0 here is assumed to
already exist, perhaps marking a release. git tag itself is covered in the next
module, A Git Toolbox.)Bisecting manually
We start a bisect session and mark the two endpoints:
git bisect start git bisect bad # the current state (HEAD) is broken git bisect good v1.0 # this tag (or commit) was known to work
Bisecting: 6 revisions left to test after this (roughly 3 steps) [a1b2c3d...] Add rainfall parsing
Git checks out a commit half-way between
v1.0 and HEAD. We test it:python -m pytest tests/test_conversion.py
If it passes, mark the current commit as good:
git bisect good
Or, if it fails, mark the current commit as bad:
git bisect bad
We keep going -
good when the tests pass, bad when they fail - and Git keeps
halving the range. Eventually it announces the first bad commit:2777975a2334c2396ccb9faf98ab149824ec465b is the first bad commit commit 2777975a2334c2396ccb9faf98ab149824ec465b Author: A. Developer <dev@example.org> Date: Thu Nov 14 09:23:55 2013 -0600 Change argument type
That is exactly the commit that introduced the bug. When we're done, we return to
where we started:
git bisect reset
Bisecting automatically
If you have a command that exits 0 when the code is good and non-zero when it is
bad - which is exactly what a test runner like
pytest does - you can let Git
run the whole search for you:git bisect start git bisect bad HEAD git bisect good v1.0 git bisect run python -m pytest tests/test_conversion.py
Git will check out each candidate commit, run the command, interpret the exit code
as good/bad automatically, and print the first bad commit without any further
input from you. Remember to finish with:
git bisect reset
This is a strong argument for keeping a good test suite: with automated tests and
bisect, finding when a regression was introduced becomes a much simpler process.
Key Points
git bisectruns a binary search through history to find the commit that introduced a bug.- Start with
git bisect start, then mark onegoodand onebadcommit. - Answer
good/badat each step, or automate the whole search withgit bisect run <command>. - Finish with
git bisect resetto return to where you started. - A reliable test suite makes finding a regression a much simpler process, especially combined with
git bisect run.