Skip to content
masterclass Buddy

Git · Command

git bisect

git bisect does a binary search through commit history to find the commit that introduced a bug. More generally, it finds the commit where any testable property of the project changed.

Last updated

How it works

You start a session and mark one commit as bad (it has the bug) and at least one as good (it does not). Git then checks out a commit about halfway between them. You test it and mark it good or bad, and each answer cuts the remaining range roughly in half. When no candidates are left, Git reports the first bad commit and points refs/bisect/bad at it. You can do the testing by hand or hand it to a script with git bisect run, which reads the script's exit code. When you finish, git bisect reset clears the bisect state and puts you back where you started.

Syntax

git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>] [--no-checkout] [--first-parent] [--reset-when-found[=<where>]] [<bad> [<good>...]] [--] [<pathspec>...]
git bisect (bad|new|<term-new>) [<rev>]
git bisect (good|old|<term-old>) [<rev>...]
git bisect skip [(<rev>|<range>)...]
git bisect reset [<commit>]
git bisect run [--reset-when-found[=<where>]] <cmd> [<arg>...]

Parameters

NameTypeDescription
<bad> [<good>...]commit-ishOptional arguments to start: one known-bad commit followed by any number of known-good commits. Giving them here saves running separate bad/good commands.
<pathspec>pathLimits the search to commits that touch these paths, so fewer steps are needed.
--term-old / --term-newstringYour own names for the before and after states, such as fast/slow or broken/fixed.
--no-checkoutflagDoes not check out each candidate. It only moves BISECT_HEAD to the commit being tested. This is assumed in bare repositories.
--first-parentflagFollows only the first parent of merge commits, so a merge is reported as the culprit and the commits on the merged branch are ignored.
--reset-when-found[=<where>]original | foundAfter the culprit is found, report it and clean up the bisect state. The default, original, returns to the commit you started from; found leaves the culprit checked out.
<cmd> [<arg>...]commandFor bisect run: a script or command whose exit code marks each commit as good, bad, or skipped.

Returns

Prints a description of the first bad (or new) commit and leaves refs/bisect/bad pointing at it.

Key points

  • The search ends when there are no candidates left. Git then prints details of the culprit commit.
  • To automate the search with bisect run, your script should exit 0 for a good commit and with a code from 1 to 127, except 125, for a bad one.
  • Exit code 125 tells bisect run that a commit can't be tested, for example because it doesn't build. Bisect then skips that commit instead of marking it good or bad.
  • If you pass paths after -- to bisect start, Git only considers commits that touch those paths, which reduces the number of steps.
  • If you already know several good commits, list them all after the bad commit when you start. This narrows the search range from the beginning.
  • If you marked a commit wrongly, save the output of git bisect log to a file, delete the wrong lines, then run reset followed by replay to rebuild the corrected session.
  • Use --no-checkout when your test doesn't need the files on disk. Git then only updates BISECT_HEAD at each step, which is faster.
  • Use --first-parent to avoid blaming individual commits on a merged branch that were broken or didn't build, when the merge result itself was fine.

Common mistakes

  • If you skip a commit right next to the culprit, bisect may only be able to name a group of possible first-bad commits instead of a single one.
  • With bisect run, any exit code outside the accepted ranges stops the whole session. A script that calls exit(-1) actually returns 255, which also stops it.
  • Within one session you must use either good/bad or old/new. You can't mix the two pairs of terms.

Example: Automated bisect that skips broken builds

#!/usr/bin/env bash
# Save as ~/bisect-test.sh (keep it outside the repo) and chmod +x it
set -u
make >/dev/null 2>&1 || exit 125   # can't build: skip this commit
./run_regression_test.sh            # exit 0 = good, 1 = bad

# --- Then, in the repository: ---
# git bisect start HEAD v1.2 --      # HEAD is bad, v1.2 is good
# git bisect run ~/bisect-test.sh
# git show refs/bisect/bad           # inspect the culprit
# git bisect reset                   # return to the original HEAD

The script skips commits that won't build by exiting 125 and otherwise reports the test's exit code, so bisect run can find the first bad commit without any manual steps.

When to use it

Finding a regression after a release

A feature worked in the last tagged release and is broken on main. Start bisect with main as bad and the tag as good, then test each commit Git checks out until it names the culprit.

Finding which commit fixed a bug

Start with --term-old broken --term-new fixed to find the commit that fixed a problem, for example to decide whether that fix should be backported.

Finding a performance regression in CI

Use bisect run with a benchmark script that exits non-zero when timings go over a threshold, and use the terms fast/slow so the output is easy to read.

Interview questions

QHow does git bisect find a bad commit, and how many steps should it take?

It does a binary search over the commits that are reachable from the bad commit but not from any good commit. Each commit you test as good or bad removes about half of the remaining candidates, so the number of steps grows with the logarithm of the range. In the docs' example, about 675 candidate revisions need roughly 10 steps.

Follow-up: Does this work when history contains merges? Yes. Bisect works on the commit graph, not just a straight line of commits. If you want a broken merge reported as one culprit instead of commits on the merged branch, use --first-parent.

QHow do you automate a bisect, and what does the script's exit code mean?

Run git bisect run <cmd> after setting good and bad bounds. Exit 0 marks the commit good. Exit 1 to 127, except 125, marks it bad. Exit 125 skips the commit, and any other code stops the bisect.

Follow-up: Why is 125 the skip code instead of 126 or 127? POSIX shells already use 126 and 127 for 'command found but not executable' and 'command not found', so 125 is the highest value that doesn't clash with them.

QWhat do you do if you marked a commit as good when it was actually bad?

Save the output of git bisect log to a file and delete the wrong entry. Then run git bisect reset followed by git bisect replay <file>. This rebuilds the session with only the correct marks.

Follow-up: How do you leave the session but stay on the culprit commit? Run git bisect reset bisect/bad. Alternatively, start with --reset-when-found=found, which leaves the first bad commit checked out once the search ends.

  • git bisect run
  • git bisect skip
  • git bisect reset
  • git bisect log
  • git bisect replay
  • git bisect visualize
  • git blame
  • git rev-list --bisect

Source: Git - git-bisect Documentation. Checked 2026-11-02.