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
Output
3.00

It was right at the v1.0 release, eleven commits ago:

$
git log --oneline
Output
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
Output
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
Output
3.00
Bisecting: 2 revisions left to test after this (roughly 1 step)
[093dc955657b736849a590c930bb9dcaa35d90d1] Add about page
$
./price.sh
git bisect good
Output
30.00
Bisecting: 0 revisions left to test after this (roughly 1 step)
[cb21dbcc76089a3ef4aa87fa01d38730526320b4] Add faq page
$
./price.sh
git bisect good
Output
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
Output
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
Output
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 commit
$
git bisect reset
Output
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

CommandUse
git bisect skipThis commit can't be tested (e.g. it doesn't build) — try a nearby one
Exit code 125 in bisect runThe same as skip, from a script
git bisect logShow the decisions so far
git bisect visualizeList the commits still in range
git bisect terms --term-old fast --term-new slowUse 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.