SkillByAIOpen interactive version →

Lesson 6 / 25

Finding the Breaking Commit With git bisect

Binary search through history.

Automated binary search

When something used to work, git bisect finds the commit that broke it by binary search: mark a known good and a known bad commit, and git checks out the midpoint for testing. With git bisect run <script> the test is automatic: exit code 0 means good, non-zero means bad. Finding the breaking commit narrows the search to its diff, which is often the best context for a fix (or a candidate for a straight revert).

Bisect over eight commits, run

I ran this with Python 3 (standard library) and, where it uses git, real git in a throwaway temporary repository. Candidate patches are written by hand to stand in for model output. In a temporary repository with eight commits, the sixth introduces an off-by-one for large inputs. git bisect run with a one-line check script identifies "change 6" as the first bad commit after testing three commits.

import os, subprocess, tempfile
d = tempfile.mkdtemp()
def git(*a): return subprocess.run(["git", *a], cwd=d, capture_output=True, text=True).stdout
git("init", "-q"); git("config", "user.email", "demo@example.com"); git("config", "user.name", "demo")
for i in range(1, 9):
    body = "def add(a, b):\n    return a + b\n" if i < 6 else "def add(a, b):\n    return a + b + (1 if a > 100 else 0)\n"
    open(os.path.join(d, "calc.py"), "w").write(body + f"# revision {i}\n")
    git("add", "."); git("commit", "-qm", f"change {i}")
open(os.path.join(d, "check.sh"), "w").write('python3 -c "from calc import add; assert add(200, 1) == 201"\n')
git("bisect", "start", "HEAD", "HEAD~7")
out = git("bisect", "run", "sh", "check.sh")
first_bad = [l for l in out.splitlines() if "is the first bad commit" in l][0].split()[0]
print("first bad commit message:", git("log", "-1", "--format=%s", first_bad).strip())
print("commits tested by bisect:", out.count("running"))
git("bisect", "reset")

Output:

first bad commit message: change 6
commits tested by bisect: 3

Make the check script fast and exact

A bisect script should test only the failing behaviour and exit with a clear code; flaky checks send bisect to the wrong commit.

Quick check: What does git bisect run need from the test script?

  • A new branch per commit
  • A screenshot
  • A pull request
  • Exit code 0 for good commits and non-zero for bad ones
Answer

Exit code 0 for good commits and non-zero for bad ones — Exit codes drive the binary search.