The undo toolbox
restore, reset, revert, amend — which one, and when.
1 Learn the idea
Git has several "undo" commands. Each one is simple, but people mix them up. They are easier to tell apart once you ask which area you want to undo, and whether anyone else has seen it yet.
Pick the tool by the situation
- I messed up a file and want the last saved version back →
git restore <file>(discards working-directory changes — gone for good). - I staged something by accident →
git restore --staged <file>(the change stays in your folder, just not in the next commit). - My last commit has a typo / forgot a file →
git commit --amend(replaces the last commit with a new one). - I want to throw away the last N local commits →
git resetwith--soft,--mixed(default) or--hard. - A bad commit is already pushed →
git revert <commit>(makes a new commit that does the opposite).
The three reset modes
All three move the current branch (and HEAD) back to the commit you name. They differ in how much of the three areas they also rewind:
--soft: moves the branch only. The undone changes remain staged, ready to recommit.--mixed(the default): moves the branch and resets the staging area. Changes remain in your working directory, unstaged.--hard: moves the branch and resets staging and the working directory. Uncommitted changes are destroyed.
2 Watch it happen
Press Play or use → to step through. Watch the graph as each command runs.
Read this demo as text
$ edit menu.md "Soup, Slaad (oops, cat on keyboard)"
We ruin
menu.mdin the working directory. Nothing is staged or committed yet.$ git restore menu.md
git restorecopies the file back from the staging area. The mess is gone for good, because git never saw it.$ edit menu.md "Soup, Salad" $ git add menu.md
A real change this time, and it is staged.
$ git restore --staged menu.md
restore --stagedunstages: the staging area goes back to HEAD's version, but your edit is still in the working directory.$ git commit -am "Add slaad"
Committed, with a typo in the message.
$ git commit --amend -m "Add salad"
--amendbuilds a brand-new commit with the fixed message and movesmainonto it. The old one is left behind, greyed out. (Commits never change.)$ git reset --soft HEAD~1
reset --softmovesmainback one commit. The salad change is still staged, so you could recommit it right away.$ git reset
reset(mixed, the default) resets the staging area to match HEAD. The change now exists only in the working directory.$ git reset --hard
reset --hardalso overwrites the working directory. The salad edit is destroyed. Double-check before you use it.$ git revert HEAD
Now say "Add prices" was already pushed. Resetting would rewrite shared history.
revertinstead adds a new commit that undoes it, and history only moves forward.
git commit --amendto fix the last commitgit reset --soft HEAD~1to redo itgit reset --hard HEAD~1to throw it away- Rewriting is fine: nobody has the old commits
git revert <commit>: new commit, opposite change- History only moves forward, so teammates can pull normally
- Never reset/amend + force-push a shared branch
- Check the graph: is
origin/mainon or after that commit?
git restore <file>Discard working-directory changesgit restore --staged <file>Unstage, keep the changegit commit --amendReplace the last commitgit reset --soft|--mixed|--hard <commit>Move the branch back (rewinds 1, 2 or 3 areas)git revert <commit>Undo a shared commit with a new commit3 Check your understanding
You ran git reset --soft HEAD~1. Where are the changes from the undone commit?
Look at the graph. Which commit can you safely remove with git reset --hard HEAD~1?
A buggy commit was pushed to main yesterday and teammates have pulled it. What is the safe fix?
You git add-ed secrets.env by mistake (not committed). How do you take it out of the next commit but keep the file?
4 Practice for real
Clean up a messy Monday
Three problems: (1) menu.md has garbage edits. Discard them. (2) Your unpushed "WIP junk" commit should disappear. (3) The already pushed "Break prices" commit must be undone without rewriting shared history. Finish with a clean working tree.