Find the exact commit that introduced a bug
How do I find which commit broke my code?
Short answer
git bisect binary-searches your history. Tell it one commit that works and one that doesn't; it checks out the midpoint and asks you to test, halving the range each time. With git bisect run it tests automatically and names the guilty commit unattended.
git bisect start
git bisect bad
git bisect good v1.2.0
Start bisecting between a known-good tag and the broken present
Does this match your situation?
- Something worked last week and doesn't now.
- There are too many commits to review by hand.
- A test started failing and nobody knows which change caused it.
Step-by-step fix
Find a commit where it definitely worked
You need one known-good reference point. A release tag is ideal; otherwise pick a commit from before the problem appeared and verify it manually.
step 1git log --oneline --since='3 weeks ago' git tag --sort=-v:refname | head -5Start the bisect
Mark the current state bad and the old commit good. Git immediately checks out a commit halfway between them for you to test.
step 2git bisect start git bisect bad # current commit is broken git bisect good v1.2.0 # this tag was fineTest and answer, repeatedly
Build or run whatever demonstrates the bug, then tell Git the verdict. Each answer halves the remaining range, so even a thousand commits resolve in about ten rounds.
step 3# test the checked-out commit, then: git bisect good # this one works # or git bisect bad # this one is broken # or git bisect skip # can't test this one (won't build)Git prints how many revisions are left and roughly how many steps remain.
Automate it with a test command
If you can express the bug as a command that exits non-zero when broken, hand it to Git and walk away. This is by far the most powerful form of bisect.
step 4git bisect start HEAD v1.2.0 git bisect run npm test # any command works: git bisect run ./scripts/check.sh git bisect run pytest tests/test_save.pyRead the verdict and reset
Git names the first bad commit and shows its diff. Always reset afterwards to return to the branch you started on.
step 5# Git prints: a1b2c3d is the first bad commit git show a1b2c3d git bisect reset
Why this works
Because every commit is a complete, buildable snapshot, any commit in your history can be checked out and tested in isolation. That turns 'which change broke this' into a sorted-search problem, and binary search over n commits takes about log₂(n) tests — 1024 commits need only ten. git bisect maintains the search state for you, and bisect run automates the answering step by interpreting your command's exit code.
How to stop it happening again
- Keep commits small and atomic so bisect points at a change you can actually read.
- Ensure every commit builds — never commit a broken intermediate state.
- Run CI on every push so breakages are caught within one commit of being introduced.
Commands used in this guide
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
How does git bisect work?
It performs a binary search over your commit history. You mark one commit good and one bad; Git checks out the midpoint and you report the result. Each answer halves the search range, so it narrows a thousand commits down to one in roughly ten steps.
Can git bisect run automatically?
Yes. git bisect run <command> executes your command at each step and interprets the exit code — zero means good, non-zero means bad. Pair it with your test suite and Git finds the offending commit with no further input from you.
What if a commit in the middle won't build?
Use git bisect skip. Git excludes that commit and picks another nearby one. If a whole region is unbuildable, bisect may end up reporting a range of candidates instead of a single commit.
How do I stop a bisect?
git bisect reset returns you to the branch and commit you were on before starting. Always run it when you're done — otherwise you're left on a detached HEAD partway through history.
Related rescue guides
A file was deleted months ago — how do I find and restore it?
git log --all --full-history -- "**/filename" finds every commit that ever touched the file, including the one that deleted it. Restore it from the commit before the deletion with git checkout <hash>^ -- <path>.
Read the fix →Always fixableHow do I revert just one file to a previous version?
git restore --source=<commit> -- <path> rewrites one file to its state at that commit and leaves everything else alone. The classic equivalent is git checkout <commit> -- <path>. Neither moves your branch or affects any other file.
Read the fix →Always fixableHow do I undo a commit that's already on the remote?
On a shared branch, use git revert <hash> — it adds a new commit that reverses the change, so nobody else has to do anything. Rewriting with reset plus a force-push is only appropriate on a branch you're certain nobody else has pulled.
Read the fix →Always fixableGit says I'm in 'detached HEAD' — what does that mean and how do I fix it?
Detached HEAD means you're on a commit rather than a branch. If you made no commits, git switch - takes you back. If you did commit, create a branch at your current position first with git switch -c <name>, otherwise those commits have no reference and become hard to find.
Read the fix →