Git interview question
A test started failing sometime in the last 200 commits. How do you find the commit that broke it?
Short answer
Use git bisect: mark a known bad commit and a known good one, and Git binary-searches the range, so 200 commits need about eight checks. Automate it with git bisect run and a script that exits 0 for good, 1 for bad and 125 to skip untestable commits. Make the check deterministic first.
Answer
I would use git bisect, which performs a binary search over the commit history. I give it one commit where the test fails (usually HEAD) and one where it passed (a release tag, or the last green CI run), and Git checks out the midpoint for me to test. Each answer halves the range, so 200 commits need about 8 steps (2^8 = 256). With git bisect run and a script, the whole search runs unattended.
Before starting: make the check reliable
Bisect assumes each commit is consistently good or bad. If the test is flaky, bisect will confidently point at the wrong commit. So first:
- Confirm the failure reproduces locally, not only in CI.
- Narrow to the smallest check that shows the problem: one test, one project, one browser.
- If it is intermittent, run it several times per step (for example Playwright’s
--repeat-each=5) and treat any failure as bad.
Manual bisect
git bisect start
git bisect bad HEAD
git bisect good v2.14.0 # or a SHA from the last green pipeline
# Git checks out a commit in the middle
npx playwright test tests/checkout.spec.ts --project=chromium
git bisect good # or: git bisect bad
# repeat until Git prints "<sha> is the first bad commit"
git bisect reset # return to where you started
git bisect skip marks a commit that cannot be tested (for example it does not build), and Git picks a nearby one. Recent Git versions also accept old/new terms (git bisect start --term-old=fast --term-new=slow) for hunting changes that are not strictly bugs, such as a performance drop.
Automated bisect
git bisect run <cmd> runs a command at each step and interprets its exit code:
0: good1to127(except125): bad125: skip (cannot test this commit)
#!/usr/bin/env bash
# bisect-check.sh
set -u
npm ci --silent || exit 125 # dependencies changed per commit; skip if install fails
npx playwright test tests/checkout.spec.ts --project=chromium --repeat-each=3 --reporter=line
git bisect start HEAD v2.14.0
git bisect run ./bisect-check.sh
git bisect log > bisect.log # keep a record for the bug report
git bisect reset
Notes:
- Keep the script outside the tracked tree, or at least ensure checkouts do not change it. If old commits do not have the script, Git cannot run it; place it in
/tmpor another untracked path. - Dependencies differ across commits. Reinstall from the lockfile at each step (
npm ci,pip install -r requirements.txt) or results are meaningless. - Environment and data: if the test depends on a shared backend, old front-end commits may be incompatible with today’s API. Bisecting is most reliable when the test runs against a locally started version of the same commit.
- Merge-heavy histories:
git bisect start --first-parent(Git 2.29+) only steps through the mainline commits, identifying which merged PR introduced the problem rather than a commit inside it.
After finding it
Read the commit and its PR, write or adjust a test that pins the regression at the lowest sensible layer, and link the bisect result in the bug report. Often the “bad” commit is legitimate and the test needs updating; bisect tells you where to look, not who is wrong.
What interviewers look for
- Choosing bisect and explaining the logarithmic search.
- Automation with
bisect runand correct exit-code semantics, including 125. - Dealing with flakiness and per-commit dependencies.
- Practical details:
skip,reset,--first-parent, saving the log.
A strong answer covers
- Uses git bisect with good and bad commits
- Explains binary search and logarithmic number of steps
- Automates with git bisect run and exit codes
- Handles flaky or untestable commits: skip, repeat runs, deterministic check
- Considers environment and dependencies when checking out old commits