Looking before moving
The earlier lesson on finding the change that broke it gave you git log -p -- file and bisecting. Two more reading commands belong beside them, because most people reach for a destructive command when a reading one would have answered the question.
git show a3f9c21 # one commit: message plus its full diff
git show a3f9c21:src/booking.ts # one file, exactly as it was then
git log -S "cancelBooking" --oneline # commits that added or removed that textThat last one is the pickaxe. When a function has vanished and nobody remembers deleting it, -S finds the commit where the string stopped appearing. It searches content, not messages, so it works even when every message says “update”.
HEAD means “where I am now”. HEAD~1 is its parent, HEAD~3 three commits back. git diff HEAD~3 is everything since then.
Way one: bring back a single file
git restore --source a3f9c21 src/booking.tsNothing else moves. Only that file is copied out of that commit into your working tree, where it sits as an ordinary uncommitted change you can look at, keep or discard. The older spelling, git checkout a3f9c21 -- src/booking.ts, does the same thing.
This is the most useful and least known of the three. An agent has rewritten a file it should not have touched; the rest of its work is good. Restore the one file and keep the rest. No reset, no lost work.
Way two: undo a commit with a new commit
git revert a3f9c21Revert reads the diff of that commit, applies the reverse of it, and makes a new commit on top. The original stays in the history. The log shows both the change and its undoing, which is the honest record of what happened.
Because it adds rather than rewrites, revert is safe after pushing. The remote receives one more commit, like any other. Reverting a commit from a week ago may conflict with what came after it; git will say so and you resolve it as in the conflict lesson.
Way three: move everything back
git reset --hard a3f9c21 is the command from the first git lesson. It moves your branch pointer back to that commit and makes the working tree match. Every later commit is abandoned; every uncommitted change is destroyed.
It is the right tool for the two-hour agent session that went nowhere and was never pushed. It is the wrong tool for anything the remote has already seen, and the mechanism is worth understanding: the remote still holds the commits you abandoned, so your next push is rejected as “non-fast-forward”. The suggested fix in most search results is git push --force, which makes the remote match you and deletes those commits from the only other copy. If a collaborator, or the host, or you on another machine has built on them, that work is now orphaned.
The rule is short. Not pushed: reset is fine. Pushed: revert.
Detached HEAD is a viewing mode, not an error
Run git checkout a3f9c21 without a file path and git prints a paragraph that begins “You are in ‘detached HEAD’ state”. People read this as something having gone wrong. It has not. You are looking at an old snapshot without being on any branch, which is exactly what bisecting does.
The one hazard: commits made here belong to no branch, and when you switch away they become unreachable. If you find yourself wanting to keep work made in this state, git switch -c rescue turns it into a branch first. Otherwise git switch main takes you home.
The undo for undo
reset --hard to the wrong commit is the mistake everyone makes once. The recovery is a command most people meet only after they need it.
git reflogThe reflog is a private log of every place HEAD has pointed, kept for about ninety days, one line per move:
e7c21a0 HEAD@{0}: reset: moving to a3f9c21
9b4d7f3 HEAD@{1}: commit: Bookings can be cancelled by the customerLine one is the mistake. Line two is where you were before it. git reset --hard HEAD@{1} puts you back, commits and all. The abandoned commits were never deleted, only left without a name; the reflog is the list of names.
What nothing brings back
The reflog records commits. It cannot recover what was never committed. git reset --hard and git restore . both destroy uncommitted edits, and those edits had no id, so no log holds them. Some editors keep a local history of file saves that can rescue you; git cannot.
That is the mechanism behind the habit this course keeps repeating. A commit is not a backup. It is the thing that makes every other recovery possible.
Every lesson here is open. Your progress is recorded once you pass the Building with AI exam.
Check your understanding
Yesterday you pushed a commit that broke the cancel button, and the host has deployed it. You want the app back the way it was. Which is the right move, and why?
Pick the one you would defend. Your answer stays private.
Stuck on something?Ask Beatriz, your AI tutor