Suggest an editImprove this articleRefine the answer for “What is git bisect?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**git bisect** uses binary search to find the commit that introduced a bug. Mark a known-good and a known-bad commit, and Git checks out midpoints until it pinpoints the exact one. ```bash git bisect start git bisect bad # current is broken git bisect good v1.0 # this worked # ~10 tests for 1000 commits git bisect reset ``` **Key:** log2(N) tests, not N.Shown above the full answer for quick recall.Answer (EN)Image**git bisect** is a Git command that uses binary search to find the exact commit that introduced a bug, by checking out midpoints between a known-good and a known-bad commit until it narrows down to one. ## Theory ### TL;DR - Binary search cuts the commit range in half each step: 1000 commits = ~10 tests - You mark commits as `good` (working) or `bad` (broken); Git picks each midpoint automatically - Always run `git bisect bad` on the broken commit first, then `git bisect good <older-commit>` - `git bisect run ./test.sh` automates everything: Git runs your script at each midpoint without any input from you - Rule of thumb: more than 30 commits since the last good state? Use bisect. Fewer? `git log -p` is faster ### Quick example ```bash git bisect start git bisect bad # HEAD is broken git bisect good v1.0 # v1.0 worked fine # Output: "Bisecting: 512 revisions left to test (roughly 9 steps)" # Git checks out the midpoint commit automatically # Test it, then mark the result: git bisect good # or: git bisect bad # After ~9 more marks: # "abc123 is the first bad commit" git bisect reset # Return to original branch ``` 1000 commits, 10 tests. That is the whole idea. ### Why binary search changes the math Checking commits one by one takes up to N checks. Binary search takes log2(N). For 1000 commits that is ~10. For 100,000 commits that is ~17. The difference matters a lot when each test run takes two minutes. Git builds a temporary ref at `refs/bisect/` to track good/bad boundaries, sorts commits topologically, and picks the midpoint using an exponential search in `bisect--helper.c`. It does not simply divide the commit count by two. The algorithm accounts for the DAG shape, which is why the selected midpoint sometimes looks like it jumped ahead or back in a counterintuitive way. ### When to use git bisect - Bug appeared after a large merge or PR: bisect between the merge base and HEAD - Production broke with no obvious recent changes: bisect from the last release tag - A test fails and you can reproduce it consistently: use `bisect run` with a script - Someone changed behavior without a clear commit message: bisect finds the commit, `git show` shows the diff One case where you should pause first: if other engineers are actively pushing to the same branch during your session, history can shift. Start bisect on a local copy or a dedicated branch. ### Automating with bisect run ```bash git bisect start git bisect bad git bisect good v1.0 git bisect run node test/auth.test.js # Exit codes: 0 = good, 1-127 = bad, 125 = skip this commit, 128+ = abort ``` Exit code 125 tells Git to skip a commit entirely. This covers commits that do not compile, have missing dependencies, or predate a file your test expects. Git skips them and continues searching. ### How it works internally Git stores bisect state in `.git/refs/bisect/` and `.git/BISECT_LOG`. The log records every mark so you can replay a session with `git bisect replay bisect.log`. Useful when you need to pause, or share the debugging session with a teammate. Merge commits get deprioritized during midpoint selection by default, since they do not introduce code directly. If you suspect a merge commit is the source, `git bisect visualize` shows the remaining candidates as a graph so you can spot it. ### Common mistakes **Running `bisect good HEAD` when HEAD is already broken.** This creates overlapping boundaries and Git aborts the session. Start with `bisect bad` first, always. Then mark a known-good older commit. **Forgetting `bisect reset`.** After finding the bad commit, you are in a detached HEAD state. Commits, pushes, and most branch operations behave unexpectedly until you reset. Run `git bisect reset` or `git checkout main`. **Testing manually with inconsistent results.** Bisect assumes your test is deterministic. If you are eyeballing "does this look right?" you will eventually mark the wrong commit. Write a script and use `bisect run`. **Starting with uncommitted changes.** Git refuses to check out commits when the working tree is dirty. ```bash # This fails: echo "debug" >> index.js git bisect start # fatal: cannot bisect on dirty working tree # Do this instead: git stash git bisect start # ... git bisect reset git stash pop ``` **Bisecting across a rebase.** Rewriting history changes commit SHAs. If someone rebases the repo mid-session, bisect loses its reference points. Save progress first: `git bisect log > session.log`, then restore with `git bisect replay session.log`. ### Real-world usage - Linux kernel: contributors use `git bisect run make test` to locate driver regressions; documented in the official contribution guide at kernel.org - Node.js: `git bisect run node test/parallel/test-http.js` for regressions in the HTTP core module - Chromium: automated bisect bot triggers on every CI build failure - React: bisect between release tags when a renderer regression appears between versions In practice, the hardest part is not running bisect itself but writing the test script. If the bug only shows up under load or in a specific environment, a plain `npm test` will not catch it. The script has to reproduce the exact failure condition. ### Follow-up questions **Q:** How do you bisect a bug that was introduced inside a merge commit? **A:** Git skips merge commits by default because they do not add code directly. Use `git bisect visualize` to check whether a merge commit appears in the remaining candidate range. If it does, test it manually via `git checkout -b test-merge <sha>`, then mark the result in your bisect session. **Q:** How does Git pick the "middle" commit? **A:** It does not split the commit count in half. `bisect--helper.c` runs an exponential search from the bad end to find the commit that minimizes worst-case remaining steps, given the actual DAG topology. In repos with many merges, the midpoint can look non-obvious. **Q:** What exit codes does `bisect run` expect? **A:** 0 means good, 1 to 127 (except 125) means bad, 125 means skip this commit, 128 or above aborts the entire session. Use 125 when a commit cannot be tested at all. **Q:** What is the time complexity of git bisect? **A:** O(log N) on average. For 1 million commits, roughly 20 tests. Git picks the midpoint greedily each time to minimize remaining steps, so it stays efficient even with unusual history shapes. **Q:** How do you limit bisect to changes in a specific file? **A:** git bisect operates per commit, not per file. Narrow the range first with `git log --follow -- path/to/file` to identify commits that touched the file, then use those as `good`/`bad` boundaries. For function-level history, `git log -L :functionName:file.js` shows line-by-line changes. ## Examples ### Simple repo: finding a bad commit step by step ```bash git init bisect-demo && cd bisect-demo # 10 clean commits, then one that breaks things for i in {1..10}; do echo "v$i" > file.txt; git add .; git commit -m "commit $i"; done echo "BUG" >> file.txt && git commit -am "commit 11" git bisect start git bisect bad # commit 11 is broken git bisect good HEAD~5 # 5 commits back was fine # Git checks out ~commit 8 cat file.txt # No BUG line - working git bisect good # Git checks out ~commit 10 cat file.txt # No BUG line - working git bisect good # Git checks out commit 11 git bisect bad # Output: "commit 11 is the first bad commit" git bisect reset ``` 4 checks for 11 commits. Without bisect you would check each one. ### Production bug: automated auth regression hunt Your Express app stopped setting `req.user` somewhere between v4.18.0 and v4.19.2. Write a test that exits 0 on pass and 1 on fail, then let bisect run it at each midpoint automatically: ```bash cd express git bisect start git bisect bad # v4.19.2 fails the auth test git bisect good v4.18.0 # v4.18.0 passed git bisect run node test/auth.test.js # Git runs the test at each midpoint # After ~7 iterations: "8f4d2a1 is the first bad commit" # git show 8f4d2a1 reveals the PR that changed route handling git bisect reset ``` No reading through 50 diffs by hand. Git does the narrowing, you read exactly one diff. ### Advanced: skipping commits that cannot be tested In a long repo history some old commits will not compile or are missing dependencies. Handle them with exit code 125: ```bash #!/bin/bash # test.sh npm install 2>/dev/null if [ $? -ne 0 ]; then exit 125 # Cannot test this commit, skip it fi npm test exit $? # 0 = good, non-zero = bad ``` ```bash git bisect start git bisect bad git bisect good v2.0.0 git bisect run ./test.sh # Skips broken commits, still finds the culprit # "a1b2c3 is the first bad commit" git bisect reset ``` This pattern holds up in monorepos where old commits predate a `package.json` restructure.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.