Undoing Things & Recovery
Module 08 — Undoing Things & Recovery
Goal: Confidently fix mistakes — unstage, discard, amend, reset, revert — and learn the reflog, the safety net that makes “lost” work almost always recoverable.
The freedom to experiment comes from knowing you can always undo. This module is your insurance policy.
8.1 A map of “undo” by situation
| Situation | Command |
|---|---|
| Unstage a file (keep the edits) | git restore --staged file |
| Discard edits in working dir | git restore file |
| Fix the last commit’s message/content | git commit --amend |
| Move branch pointer back, keep changes | git reset --soft / --mixed |
| Move branch pointer back, delete changes | git reset --hard |
| Undo a commit by making a new opposite commit | git revert <hash> |
| Recover something you thought was lost | git reflog |
Let’s go through the important ones.
8.2 Unstaging and discarding (the gentle undos)
# You staged a file but changed your mind — keep the edits, just unstage:
git restore --staged notes.txt
# You edited a file and want to throw the edits away entirely:
git restore notes.txt # WARNING: discards uncommitted changes, no recovery
git restore is the modern, clear command. (Older tutorials use git checkout -- file and git reset HEAD file for the same things.)
8.3 Amending the last commit
Forgot a file, or made a typo in the message? Fix the most recent commit:
git add forgotten-file.js
git commit --amend # opens editor to edit the message too
git commit --amend -m "Better message"
Important: amend replaces the last commit with a new one (new hash). So only amend commits you haven’t pushed — amending pushed commits causes the same sync problems as rebasing shared history (Module 05).
8.4 Reset — moving the branch pointer
git reset moves your current branch pointer to an earlier commit. It comes in three flavors that differ in what they do to your staging area and files:
git reset --soft HEAD~1 # move pointer back 1; keep changes STAGED
git reset --mixed HEAD~1 # (default) move back 1; keep changes UNSTAGED
git reset --hard HEAD~1 # move back 1; DISCARD changes entirely
(HEAD~1 means “one commit before HEAD”; HEAD~3 means three back.)
--soft: “Undo the commit, but I want to re-commit these same changes differently.” Great for squashing the last few commits into one.--mixed: “Undo the commit and the staging, leave my edits in the working directory.” The default.--hard: “Pretend those commits never happened and erase the changes.” Dangerous — this is the one that can lose work. But even here, the reflog (section 8.6) usually saves you.
Rule of thumb:
resetrewrites history, so use it on local, unpushed commits. For anything already shared, userevertinstead.
8.5 Revert — the safe undo for shared history
git revert doesn’t delete anything. It creates a new commit that is the exact inverse of a previous one, cancelling its effect while keeping the history honest:
git revert a1b2c3d # makes a new commit undoing commit a1b2c3d
Because it only adds a commit, it’s safe to use on commits that are already pushed and shared. This is the correct way to undo a bad change on main that your team already has.
Reset vs. revert in one line: reset hides history (local only); revert adds an honest “undo” commit (safe for shared branches).
8.6 The reflog — your ultimate safety net
Here’s the most reassuring fact in all of Git. Remember .git/logs/HEAD from Module 04? It records every position HEAD has ever held — every commit, checkout, reset, rebase, merge. View it:
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: The work I thought I lost
# ...
Did a git reset --hard nuke a commit? Its hash is still right there in the reflog. Recover it:
git reset --hard e4f5g6h # jump back to that exact state
# or put it on a new branch to be safe:
git branch recovered e4f5g6h
Why does this work? From Module 04: commits are objects in .git/objects/, and deleting a branch or resetting only moves pointers — the objects themselves linger until garbage collection, which doesn’t run for weeks and won’t touch anything the reflog still references. So:
As long as you committed it at least once, you can almost always get it back. The reflog is why experienced developers experiment without fear.
(The reflog is local to your machine and entries expire after ~90 days by default. It’s a personal safety net, not a shared backup.)
8.7 Cleaning untracked files
reset and restore don’t touch untracked files. To remove those:
git clean -n # DRY RUN: show what would be deleted
git clean -fd # actually delete untracked files (-f) and folders (-d)
Always run with -n first — git clean deletes files that were never committed, so they’re not recoverable via reflog.
Try it yourself
- Make 3 commits. Run
git reset --soft HEAD~2and notice the last two commits’ changes are now staged together — commit them as one. (You just squashed.) - Make a commit, then
git revertit. Look atgit log— both the original and the revert appear. - Do a
git reset --hard HEAD~1to “lose” a commit, then rungit reflog, find its hash, andgit reset --hardback to it. Feel the relief.
Key takeaways
git restoreunstages or discards working changes;git commit --amendfixes the last (unpushed) commit.git resetmoves your branch pointer: —soft keeps staged, —mixed keeps unstaged, —hard discards. Use on local history only.git revertsafely undoes a commit by adding an inverse commit — the right tool for shared branches.- The reflog records every move of HEAD, so committed work is almost always recoverable. Experiment without fear.
Next: Module 09 — Advanced Git