Finding Bugs with bisect
Something worked last month and is broken today, and there are hundreds of commits in between. git bisect finds the exact commit that introduced the problem with a binary search: you mark one good and one bad commit, and Git checks out the midpoint again and again until only the culprit is left.
The bug
The shop's price script should print 30.00 for three items at 10.00, but now it prints something else:
./price.sh
3.00
It was right at the v1.0 release, eleven commits ago:
git log --oneline
8afbcb4 (HEAD -> main) Add terms page 1c4ff23 Add privacy page 0c990e5 Add returns page 816a5be Add shipping page 12c4019 Add blog page 91e9994 Tidy up price calculation cb21dbc Add faq page 9ecd82b Add contact page 093dc95 Add about page 6651aef Add home page f368159 (tag: v1.0) Add price calculator
A manual bisect
Start, and tell Git one bad commit (here the current one) and one good one:
git bisect start git bisect bad git bisect good v1.0
status: waiting for both good and bad commits status: waiting for good commit(s), bad commit known Bisecting: 4 revisions left to test after this (roughly 2 steps) [91e9994155a4f4179253026d7092ba070fe998cd] Tidy up price calculation
Git has checked out a commit halfway between. Test it and report the result:
./price.sh git bisect bad
3.00 Bisecting: 2 revisions left to test after this (roughly 1 step) [093dc955657b736849a590c930bb9dcaa35d90d1] Add about page
./price.sh git bisect good
30.00 Bisecting: 0 revisions left to test after this (roughly 1 step) [cb21dbcc76089a3ef4aa87fa01d38730526320b4] Add faq page
./price.sh git bisect good
30.00
91e9994155a4f4179253026d7092ba070fe998cd is the first bad commit
commit 91e9994155a4f4179253026d7092ba070fe998cd
Author: Sam Rivera <sam@example.com>
Date: Mon Mar 2 10:17:00 2026 +0000
Tidy up price calculation
price.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)Git names the first bad commit and shows what it changed. With ten commits to search it needed only three tests; with 1,000 it would need about ten.
When you're done, return to where you started:
git bisect reset
Previous HEAD position was cb21dbc Add faq page Switched to branch 'main'
Automating it with bisect run
If you can write a command that tells good from bad by its exit code — 0 for good, anything from 1 to 127 (except 125) for bad — Git can do the whole search on its own. Most often that command is your test suite or a single test:
cat > ~/check-price.sh <<'EOF' #!/bin/sh test "$(./price.sh)" = "30.00" EOF chmod +x ~/check-price.sh
git bisect start HEAD v1.0 git bisect run ~/check-price.sh
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[91e9994155a4f4179253026d7092ba070fe998cd] Tidy up price calculation
running '/home/sam/check-price.sh'
Bisecting: 2 revisions left to test after this (roughly 1 step)
[093dc955657b736849a590c930bb9dcaa35d90d1] Add about page
running '/home/sam/check-price.sh'
Bisecting: 0 revisions left to test after this (roughly 1 step)
[cb21dbcc76089a3ef4aa87fa01d38730526320b4] Add faq page
running '/home/sam/check-price.sh'
91e9994155a4f4179253026d7092ba070fe998cd is the first bad commit
commit 91e9994155a4f4179253026d7092ba070fe998cd
Author: Sam Rivera <sam@example.com>
Date: Mon Mar 2 10:17:00 2026 +0000
Tidy up price calculation
price.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
bisect found first bad commitgit bisect reset
Previous HEAD position was cb21dbc Add faq page Switched to branch 'main'
The test script lives outside the repository so that it stays the same while Git checks out older commits.
Other bisect commands
| Command | Use |
|---|---|
git bisect skip | This commit can't be tested (e.g. it doesn't build) — try a nearby one |
Exit code 125 in bisect run | The same as skip, from a script |
git bisect log | Show the decisions so far |
git bisect visualize | List the commits still in range |
git bisect terms --term-old fast --term-new slow | Use other words than good/bad — e.g. to find when something got faster |
Tips
- Small commits make bisect precise. If the guilty commit changed one thing, the cause is obvious; if it changed fifty files, you're still searching.
- Every commit should build. Commits that don't compile force you to skip, which blurs the result. Tidy history before merging helps here.
- Start with a wide range. Any commit you're sure was good will do; bisect is fast even across thousands of commits.
Try it yourself
Create a repository with a script that prints a number. Make ten commits, introducing a change to the number in commit 7. Find it first with manual git bisect good/bad, then again with git bisect run and a one-line test.