Lesson 3 of 3 in Investigating
Finding the bad commit
Story: Don't comb through every moment of the timeline, Agent. Cut it in half, and keep cutting.
Something worked a month ago and doesn't work now. Thirty commits came in between, and any of them could be the one that broke it. Searching helps when you know what text to look for, but often you don't: you only know that something is wrong.
git bisect finds the commit by halving. You tell it one commit where things were fine (good) and one where they're broken (bad). It checks out a commit halfway between them. You test that commit and tell git whether it's good or bad, and half of the suspects are gone. Repeat until one is left: the first bad commit, the one that broke it.
Each answer halves what's left, so it takes very few steps: about 5 for 30 commits, and about 10 for a thousand.
Starting a bisect
This café menu says the pie costs 60. It cost 6 when the menu was first written, seven commits ago:
$ git log --oneline
2e49212 (HEAD -> main) Add soda
4091c44 Add a scone
7f2b7f9 Add a salad
f6273a6 Add juice
ee58b7b Add coffee
f7ef7e8 Add a cake
15d4ab5 Add tea
afcedc7 Add the menu
Start a bisect, then mark the two ends:
$ git bisect start
status: waiting for both good and bad commits
$ git bisect bad
status: waiting for good commit(s), bad commit known
$ git bisect good afcedc7
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[ee58b7b728c0407fc3cfc8f6a243b075410fe9d5] Add coffee
git bisect bad with nothing after it marks the commit you're on: here, the newest one on main. git bisect good afcedc7 marks the first commit. git then checks out Add coffee, halfway between them, and tells you how many commits are left to test after this one.
You can also mark both ends as you start, the bad one first: git bisect start main afcedc7 does all three steps in one.
Testing each commit
You're now on a detached HEAD, at the commit git picked, and your files are as they were in that commit. In the Live Graph, HEAD jumps to each commit git checks out. Test it. Here, that means looking at the menu:
$ cat menu.txt
Soup 4
Bread 2
Pie 6
Tea 2
Cake 3
Coffee 3
The pie costs 6, so this commit is good. Tell git:
$ git bisect good
Bisecting: 1 revision left to test after this (roughly 1 step)
[7f2b7f902638477af797c4aadd3e2ba590d14ab2] Add a salad
git checks out the next commit to test. Keep going: look, then git bisect good or git bisect bad. In Add a salad the pie costs 60, so that one is bad, and so is the next one git checks out. Then only one suspect is left, and git names it:
$ git bisect bad
f6273a6141124895a21bea2e6e1b6b31458221e8 is the first bad commit
commit f6273a6141124895a21bea2e6e1b6b31458221e8 (HEAD)
Author: You <you@example.com>
Date: Fri Feb 9 09:00:00 2024 +0000
Add juice
menu.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Add juice changed the pie's price while it added juice. git doesn't check that commit out for you: HEAD stays on the last commit you tested. Here they're the same commit, so git shows (HEAD) after its id. Often they aren't, so take the id from this message.
Judge every commit by the same test, and only by that test. One wrong answer sends git into the wrong half, and it names the wrong commit. If you slip, run git bisect reset and start again.
Finishing
A bisect keeps going until you end it, and git status reminds you:
$ git status
HEAD detached at f6273a6
You are currently bisecting, started from branch 'main'.
(use "git bisect reset" to get back to the original branch)
nothing to commit, working tree clean
git bisect reset ends it and switches back to the branch you started from:
$ git bisect reset
Previous HEAD position was f6273a6 Add juice
Switched to branch 'main'
To keep track of the first bad commit, put a tag on it, before or after you reset: git tag pie-price f6273a6. Then look at what it changed with git show, and fix it on your branch.
When to bisect
Bisect when you know what is wrong but not which change did it.
- If you know the exact text that broke it,
git log -Sfinds the commit faster. But-Sonly sees text that was added or removed. When a line only moved, its text is still there, so-Sdoesn't list that commit. git blamenames the last commit that changed a line. When a later commit re-indented or reworded the broken line, blame names that later commit.
Bisect doesn't care how the bug got in. It only needs you to tell a good commit from a bad one.
On your own computer
git bisect can do more than this Terminal lets you:
git bisect run <script>runs a test script on each commit and marks it for you.git bisect skipskips a commit you can't test, such as one that doesn't build.git bisect loglists your answers so far.
On a real project, testing a commit usually means building it and running the tests or the app, not only reading a file.
| Command | What it does |
|---|---|
git bisect start |
Starts a bisect. git bisect start <bad> <good> marks both ends at once. |
git bisect bad |
Marks the commit you're on as bad, or the one you name. |
git bisect good |
Marks the commit you're on as good, or the one you name. |
git bisect reset |
Ends the bisect and switches back to the branch you started from. |
New commands
git bisect startgit bisect goodgit bisect badgit bisect reset
Explainer: Finding the bad commit
Graph as text
project: your repo, you are here
- HEAD → main at 2e49212 "Add soda"
Branches and tags
- main at 2e49212 "Add soda"
Commits, newest first
- 2e49212 "Add soda"; parent 4091c44; refs: HEAD → main
- 4091c44 "Add a scone"; parent 7f2b7f9
- 7f2b7f9 "Add a salad"; parent f6273a6
- f6273a6 "Add juice"; parent ee58b7b
- ee58b7b "Add coffee"; parent f7ef7e8
- f7ef7e8 "Add a cake"; parent 15d4ab5
- 15d4ab5 "Add tea"; parent afcedc7
- afcedc7 "Add the menu"; no parent
Challenge 1 of 3
Story: Ines got halfway through this search before her shift ended. Pick up where she left off, Agent.
Checkout offers whatever is on the first line of shipping.txt, so the file must start with its Standard line. On main it starts with Express. A bisect to find the commit that broke it is already running: main is marked bad, the first commit good, and one commit in between bad. The commit checked out now is the next one to test.
Finish the bisect. Create a tag broke-shipping at the first bad commit, and end the bisect.
Unlocks after Branching & Merging.
Terminal
Unlocks after Branching & Merging.
This Lesson can be played once Branching & Merging is complete. Its text and Explainer are open to everyone.