Bisect is the most underrated tool in Git. On a history of tens of thousands of commits it pinpoints the source of a regression in 10-15 steps.
Basic manual workflow
git bisect start
git bisect bad # current commit is broken
git bisect good v1.4 # v1.4 is known to work
Git checks out the midpoint commit. You test it and respond:
git bisect bad # bug is present
git bisect good # bug is absent
Git halves the interval and checks out the next candidate. After
log2(N) answers:
a1b2c3d is the first bad commit
commit a1b2c3d
Author: ...
Date: ...
refactor: extract calculator class
When you are done:
git bisect reset # return HEAD to the branch you started on
Automated mode: bisect run
If you have a checker script, Git does everything on its own.
git bisect start
git bisect bad
git bisect good v1.4
git bisect run ./check.sh
Script contract:
exit 0- commit is good (test passed)exit 1..124, 126..127- commit is bad (test failed)exit 125- skip this commit (could not be tested)- any other code - fatal error, bisect stops
Pytest and most test frameworks work directly (they return 0/1):
git bisect run pytest tests/test_login.py::test_password_reset
Useful commands during bisect
git bisect log # log of the current bisect session
git bisect visualize # show the remaining commit range
git bisect skip # skip current commit (does not build / not relevant)
git bisect reset # exit and return to the original branch
git bisect replay <log> # replay a session from a saved log
Custom terms
If good/bad does not fit (for example, you are looking for when a feature first appeared):
git bisect start --term-old=missing --term-new=present
git bisect present
git bisect missing v1.0
Pitfalls
- Intermediate commits do not build. Bisect requires every step
to be testable. If commits are atomic (see atomic-commit) you
are fine. If the history has noise, use
bisect skiporexit 125in the script. - Intermittent regression. If the bug appeared, disappeared, then appeared again, bisect will find some boundary but not the first one. The fix is a more precise test.
- External dependencies. If the result depends on a package version, update dependencies in the script before the test. Otherwise bisect catches the environment change, not the commit.