# What is git bisect?

Git bisect finds the commit that introduced a bug by binary search, testing commits between a known good one and a known bad one to halve the range.

Last updated September 29, 2026, 9 min read

## Learning objectives

After reading this article you will be able to:

-   Define git bisect
-   Explain how binary search finds the breaking commit
-   Apply git bisect run with an automated check

## Related content

-   [What is debugging?](https://specstory.com/learning/debugging/debugging)
-   [What is root cause analysis (RCA)?](https://specstory.com/learning/debugging/root-cause-analysis)
-   [Why do AI coding agents break working features?](https://specstory.com/learning/debugging/agents-break-working-features)
-   [What is a flaky test?](https://specstory.com/learning/debugging/flaky-tests)

## What is git bisect?

Git bisect is a Git command that runs a binary search through a project's [commit](https://specstory.com/learning/glossary#commit) history to find the first commit where a behavior changed. A developer marks one commit where the software worked and one where it fails. Git then checks out commits between them, and each result halves the range until one commit is left.

[Git's manual](https://git-scm.com/docs/git-bisect) says git bisect uses "a binary search algorithm to find which commit in your project's history introduced a bug." It suits a [regression](https://specstory.com/learning/testing/regression-testing), a behavior that worked in an earlier version and broke later.

Bisection is one of the narrowing techniques of [debugging](https://specstory.com/learning/debugging/debugging). It needs a check that gives a clear good or bad answer on any commit, usually built from the bug's [reproduction steps](https://specstory.com/learning/debugging/reproduction-steps).

## How does git bisect work?

A bisect session moves through these steps:

1.  A developer runs `git bisect start` and marks a failing commit, usually the current one, with `git bisect bad`.
2.  The developer marks a working commit, e.g. the last release, with `git bisect good`.
3.  Git checks out a commit about halfway between the two and prints how many commits are left to test.
4.  The developer tests that commit and marks it good or bad.
5.  Git drops the half of the range that the mark rules out and checks out the middle of the rest.
6.  When one commit is left, Git prints it as the first bad commit and points the reference `refs/bisect/bad` at it.

A manual session uses these commands:

```bash
git bisect start
git bisect bad              # current commit fails
git bisect good v2.4.0      # last known good
# test the checked-out commit, then mark it
git bisect good             # or: git bisect bad
git bisect reset            # when done
```

Diagram: How bisect halves the range

Each line is the range still in doubt, and each dot is the commit tested. Each test rules out about half of the range.

Each test halves the range, so 100 commits take about 7 tests.

The terms `old` and `new` can replace good and bad, e.g. to find the commit that fixed a bug. To undo a wrong mark, a developer saves the output of `git bisect log` to a file, deletes the wrong line, and runs `git bisect replay` on that file.

## What is an example of git bisect?

Here is an illustrative example. Acme Co. sells furniture online. In its release `v2.4.0`, customers could edit their delivery address during checkout and keep their cart.

Since then, a [coding agent](https://specstory.com/learning/ai-coding/coding-agent) has added 40 commits to the main branch. A customer reports "The address saved, but the cart emptied." A developer reproduces the failure and starts the search:

1.  The developer runs `git bisect start`, `git bisect bad` on the current commit, and `git bisect good v2.4.0`.
2.  Git checks out commit 20 of the 40 and prints `Bisecting: 19 revisions left to test after this (roughly 4 steps)`.
3.  The developer adds 2 items to the cart, starts checkout, and changes the address. The cart keeps 2 items, so the developer runs `git bisect good`.
4.  Git checks out commit 30, where the cart empties, so the developer runs `git bisect bad`.
5.  Three more tests, on commits 25, 27, and 26, come out good, bad, and good.

After the fifth test, Git prints the first bad commit, shortened here:

```text
2e01d6a5367c4893bfd8a57a8560d498a06dc457 is the first bad commit
commit 2e01d6a5367c4893bfd8a57a8560d498a06dc457
Author: Acme developer <dev@example.com>

    Keep checkout state in one record

 src/checkout/address.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
```

The message of commit 27 describes a refactor, but its diff shows the address handler replacing the whole checkout record with one that holds only the address. This example is simplified. A real project would also need each old commit to build with the dependencies it had then.

## How do you automate it with git bisect run?

The command `git bisect run` replaces the person in the loop with a script. Git runs the script on each commit it checks out and marks the commit from the script's [exit code](https://specstory.com/learning/cli-and-web/exit-codes):

-   **Exit code 0.** The commit is good.
-   **Exit code 125.** The commit cannot be tested, so Git skips it.
-   **Exit codes 1 to 127, other than 125.** The commit is bad.
-   **Any other code.** Git stops the whole bisect, e.g. on 255 from a C program's `exit(-1)`.

A crash can end with a code above 128, e.g. 139 after a segmentation fault, so the script below turns that code into 1. A commit that does not build would fail the test too, so the script skips it.

For the Acme bug, the check is a Playwright test written from the reproduction steps. Older commits do not contain that test, so the script copies it in on each run. Git's manual advises keeping bisect scripts outside the repository:

```bash
#!/bin/sh
# ~/cart-check.sh, kept outside the repository
npm ci --silent || exit 125          # no clean install: skip this commit
npm run build --silent || exit 125   # does not build: skip this commit
cp ~/address-keeps-cart.spec.ts tests/ || exit 125
npx playwright test tests/address-keeps-cart.spec.ts --retries=0
status=$?
[ "$status" -gt 127 ] && status=1    # a crash counts as bad
rm tests/address-keeps-cart.spec.ts
exit $status
```

The `--retries=0` flag stops a retry from turning a failure into a pass. Before the search, the developer runs the script once on `v2.4.0`, where it exits 0, and once on the current commit, where it exits 1. Then `git bisect start HEAD v2.4.0` and `git bisect run sh ~/cart-check.sh` run the search, and `git bisect reset` ends it.

Git tests the same five commits as the manual search and prints `bisect found first bad commit`. The reset clears the bisect state and returns to the commit checked out before `git bisect start`. It also ends a search early, because Git has no `git bisect stop` command.

## What changes when a coding agent writes the code?

A coding agent can add many commits in one session, but a longer range adds few tests. The bigger change is in what each commit holds. An agent can leave a series of commits that never built. The `git bisect skip` command takes a range, so one command skips the series.

A coding agent can run `git bisect run` itself, because the command needs only a shell and an exit code. Bisect checks out old commits in the working tree where it runs. A separate working tree, e.g. one made with `git worktree add`, keeps the search away from unfinished work in the main checkout.

When an agent's change [breaks working features](https://specstory.com/learning/debugging/agents-break-working-features), the check should come from the bug report, not from the agent's reading of the code. A check written from the code can test a different behavior, and the search then names the commit that changed that behavior. A practical adjustment is a pre-commit [Git hook](https://specstory.com/learning/ci-cd/git-hooks) that runs the build. Then later commits are more likely to build, though `--no-verify` can skip the hook.

## What makes bisecting hard?

Bisect gives a wrong or unclear answer under these conditions:

-   **A check that is not repeatable.** A [flaky test](https://specstory.com/learning/debugging/flaky-tests) can fail on a good commit or pass on a bad one, and the search continues in the wrong half with no warning. For an intermittent bug, the script can run the test several times with retries off and exit 1 if any run fails.
-   **Commits that cannot be tested.** A commit that does not build gets `git bisect skip`, and Git moves to a nearby commit. If a skipped commit sits next to the first bad commit, Git lists several candidates instead of one.
-   **Large commits.** Bisect cannot split a commit, so a commit that changed 30 files leaves a long diff to read. Review has the same problem with [pull request size](https://specstory.com/learning/code-review/pull-request-size).
-   **More than one change.** Bisect relies on a single switch from good to bad in the range. If the behavior broke, was fixed, and broke again, the search can name the second break instead of the first.
-   **Merged branches.** A merged branch can hold commits that were broken or did not build, although the merge itself was fine. The `--first-parent` option follows only the first parent of each merge, so the search names the merge commit.

## How is git bisect different from reading the git log?

Reading the log means scanning commit messages and diffs with `git log` for a likely cause. The `git blame` command shows the commit that last changed each line. The [Pro Git book](https://git-scm.com/book/en/v2/Git-Tools-Debugging-with-Git) says that annotating a file this way "helps if you know where the issue is to begin with."

Git bisect needs no message or suspect file, because it tests behavior at each step. Bisect names the commit, and its diff is where [root cause analysis](https://specstory.com/learning/debugging/root-cause-analysis) starts.

## FAQs

### What does git bisect skip do?

Git bisect skip marks the current commit as one that cannot be tested, and Git moves to a nearby commit. The command also takes a range, e.g. a series of commits that never built. A skip next to the first bad commit leaves several candidates instead of one.

### Which exit code makes git bisect run skip a commit?

Exit code 125 makes git bisect run skip a commit, because Git reads it as a commit that cannot be tested. Code 0 marks a commit good, codes 1 to 127 other than 125 mark it bad, and any other code stops the bisect.

### Can git bisect find a flaky failure?

Git bisect can find a flaky failure when the script runs the test several times with retries off and exits 1 if any run fails. A single run can pass on a bad commit and send the search into the wrong half.

### How do you end a git bisect session?

A git bisect session ends with git bisect reset, which clears the bisect state and returns to the commit checked out before the session started. Git has no git bisect stop command, so the reset also ends a search early.

---

Source: [What is git bisect? | How to use git bisect run | SpecStory](https://specstory.com/learning/debugging/git-bisect)
