git bisect: binary search for bugs
"The app worked last month. Somewhere in the 500 commits since, a bug crept in." Checking commits one by one is 500 tests. Bisect is smarter: it runs a binary search over the history, where each test you perform eliminates half of the remaining commits. (It's the same halving that makes the number-guessing game winnable in a handful of guesses.)
$ git bisect start $ git bisect bad # current commit is broken $ git bisect good a1b2c3d # this old commit was fine Bisecting: 250 revisions left to test after this
Git checks out the commit halfway between good and bad. You test the app and report git bisect good or git bisect bad. Either answer eliminates half the remaining suspects, and Git jumps to the middle of what's left. When one commit remains, that's your culprit, read it with git show (lesson 3-1). git bisect reset returns you to normal.
Halving is powerful: 500 commits need about 9 tests, 1024 need 10, a million need 20, because each test cuts the range in half (log₂ 1024 = 10). Small commits with honest messages (lesson 2-3, lesson 3-3) make the culprit obvious once found.
Quiz
A bug appeared somewhere in the last 1024 commits. Roughly how many test runs does git bisect need to find the guilty commit?
Code exercise · python
Run this. It counts how many halvings it takes to shrink a suspect list down to one commit — the number of tests a bisect costs. (Real bisect can need one more or fewer, depending on where the midpoints round.) Notice how slowly the cost grows: 2000x more commits, barely 2x more tests.
Final recovery drill, combining lesson 4-3's safety net. You ran git reset --hard one commit too far and an afternoon of work vanished from git log. Get it back.
Step 1/3: List every position HEAD has been, to find the lost commit.
Your cheatsheet
Everything from the course, by task:
| I want to... | Command | Lesson |
|---|---|---|
| start a repo / copy one | git init / git clone <url> | 2-1, 7-1 |
| see the current state | git status | 2-1 |
| stage, then save | git add <f>, git commit -m "..." | 2-2, 2-3 |
| read history / one commit | git log --oneline / git show <hash> | 3-1 |
| see changes | git diff, git diff --staged | 3-2 |
| find who changed a line | git blame <file> | 3-4 |
| unstage / discard edits | git restore --staged <f> / git restore <f> | 4-1 |
| rewind commits | git reset --soft/--mixed/--hard HEAD~1 | 4-2 |
| fix last commit / undo shared one | git commit --amend / git revert <hash> | 4-3 |
| find lost commits | git reflog | 4-3 |
| branch and switch | git switch -c <name> | 5-2 |
| combine branches | git merge <branch> | 5-3, 6-2 |
| sync with the team | git pull, git push, git fetch | 7-2, 7-3 |
| tidy local history | git rebase -i HEAD~n | 9-2 |
| copy one commit here | git cherry-pick <hash> | 9-3 |
| shelve work / mark release | git stash / git tag -a v1.0.0 | 10-1 |
| hunt a bug's commit | git bisect | 10-2 |
Quiz
Graduation scenario. A commit pushed to the shared main last week broke checkout. Teammates have all pulled it. Which command undoes its effect correctly?
Problem
Your test suite broke somewhere in the last 300 commits. Which git subcommand finds the exact guilty commit in about 8 or 9 test runs?