Previously, in Part 8, we stepped into Git's editing room — squashing messy commits, rebasing branches, and cherry-picking hotfixes — and I promised you a climbing rope.
Here it is, the biggest relief in this whole series: in Git, almost nothing is ever truly lost. Every commit you have ever made is still in there, even the ones you think you deleted. Today we learn to find them, to undo mistakes three different ways, and — as the grand finale — to hunt down a bug like a detective. There is a real bug waiting for us in the workshop repo, and it has been hiding there since the day you cloned it.
Reflog: the security-camera footage
You know git log: the photo album, showing the history of your current branch.
But Git keeps a second record that almost nobody teaches beginners:
git reflog, a diary of every single place HEAD has been — every
commit, every checkout, every rebase, every reset. If the log is the album, the reflog is
the security-camera footage of the whole building. Let's use it to survive a disaster we
cause on purpose. In your clone of the workshop repo:
git switch -c experiment
# edit Components/PatientCard.razor a little, then:
git commit -am "Try a bolder patient card layout"
git switch master
git branch -D experiment
That capital -D means "delete even though it was never merged." The branch is
gone. git log shows no trace of your bold new layout. This is the moment where,
years ago, I would have made a sad sound and rewritten the whole thing from memory. Instead,
roll the footage:
git reflog
9c8b7a6 HEAD@{0}: checkout: moving from experiment to master
4e5f6a7 HEAD@{1}: commit: Try a bolder patient card layout
9c8b7a6 HEAD@{2}: checkout: moving from master to experiment
There it is. HEAD@{1} means "where HEAD was one move ago" — and that is our
"deleted" commit, alive and well. Deleting the branch only deleted the label; the
photo was never destroyed. Hand it a new label and it is fully resurrected:
git branch rescued HEAD@{1}
git log --oneline rescued
# Try a bolder patient card layout — back from the dead
Tip: the reflog is local to your machine and Git prunes old entries after about ninety days. It is a rescue rope, not an archive — once you have rescued something, put it on a proper branch.
Reset: three flavors, explained once
git reset has a scary reputation, mostly because nobody explains it properly.
Here it is, once and for all. Reset moves your branch label backwards to an older commit —
say git reset --soft HEAD~1 to step back one. The flavor decides how much else
comes along for the ride:
| Flavor | What it touches | When it is the right tool |
|---|---|---|
--soft | Moves the branch label; your changes stay staged | "Those last two commits should really be one" — step back, recommit |
--mixed (default) | Also unstages everything | "Let me rethink what goes into this commit from scratch" |
--hard | Also wipes your working directory clean | "Throw this work away. I mean it." |
Careful: --hard is the one genuinely dangerous command in
this post. The reflog records commits, so committed work can always be rescued —
but changes you never committed have no photo to recover. git reset --hard
deletes those forever. Read it twice before you press Enter.
Revert: the polite undo for shared history
Reset rewrites history, so the Golden Rule from Part 8 applies: local drafts only. What if
the bad commit is already pushed, already pulled, already public? Then you use
git revert, which does something beautifully honest: it creates a new
commit that applies the exact opposite of an old one. Nothing is rewritten. The album gains
a page that says "that earlier photo was a mistake, here is the correction" — and that is
completely safe to push.
| Question | Reset | Revert |
|---|---|---|
| What it does | Moves your branch label backwards | Adds a new commit that undoes an old one |
| History afterwards | Rewritten | Preserved, plus one honest page |
| Safe on pushed branches? | No | Yes |
| Best for | Your local drafts | Anything anyone else has seen |
Bisect: the bug hunt
Now for the finale. Run the workshop app (dotnet run in the ClinicApp folder),
open the /patients page, and search for "asha" — all
lowercase. Nothing. But Asha is right there in the list. Type "Asha" with a
capital A and she appears. The search has quietly become case-sensitive. And here is the
eerie part: I can tell you it worked perfectly back at tag v0.1. Somewhere in
the commits since then, a bug slipped in — but which one? You could read six diffs by hand.
Or you could let Git do a binary search for you:
git bisect start
git bisect bad HEAD
git bisect good v0.1
Bisecting: 2 revisions left to test after this (roughly 2 steps)
Git checks out the commit halfway between good and bad — "Add the new-patient form with validation" — and waits. Your only job at each step: check whether the bug is here. Run the app and try the search, or just peek at the filter code in the Patients page. Lowercase search works here, so tell Git and let it jump again:
git bisect good
# Git checks out "Show a patient count above the list" — search is broken here:
git bisect bad
# Git checks out "Tidy up the search filter" — broken here too:
git bisect bad
d4e5f6b is the first bad commit
Tidy up the search filter
Three tests, and Git has cornered the culprit. Look at that commit message: "Tidy up the
search filter." It sounds so innocent. Read the actual diff with
git show d4e5f6b:
- var results = Patients.Where(p => p.Name.ToLower().Contains(search.ToLower()));
+ var results = Patients.Where(p => p.Name.Contains(search));
There it is. Someone "tidied up" the filter by removing the ToLower() calls —
shorter code, nicer-looking, and quietly case-sensitive. The lesson is worth framing:
commit messages can lie, but bisect never does. It does not care what
anyone wrote in the message; it only cares where the behavior changed. Now finish the job
properly, on a branch, like the professional you have become:
git bisect reset
git switch -c fix/case-insensitive-search
# restore the ToLower() comparison in the Patients page, then:
git commit -am "Make the patient search case-insensitive again"
Tip: bisect works on any repo where you know one good state and one bad state — and because it halves the suspects each round, a thousand commits need only about ten tests. It is the detective tool nobody teaches beginners. You now know it.
Next time: the grand finale
You can now rescue deleted work, undo anything three different ways, and corner a bug in minutes. For our finale, Part 10 opens the hood: how Git actually thinks, why it is so fast, and where your journey goes from here. See you there.