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:

The three flavors of git reset
FlavorWhat it touchesWhen it is the right tool
--softMoves 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"
--hardAlso 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.

Reset vs revert
QuestionResetRevert
What it doesMoves your branch label backwardsAdds a new commit that undoes an old one
History afterwardsRewrittenPreserved, plus one honest page
Safe on pushed branches?NoYes
Best forYour local draftsAnything 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.