The undo map
Intermediate
Why this matters
Almost every Git mistake is one of six situations, and each has one right answer. This page is the map; the lessons after it are the detail. If you read only one page in this section, read this one.
The map
The question that decides everything
Before choosing, ask: has this been pushed where other people can see it?
| Answer | What is available |
|---|---|
| No, it is local | Anything: amend, reset, rebase. You are rearranging your own furniture. |
| Yes, it is pushed | Add a new commit that undoes the old one: git revert. Rewriting means everyone else's history stops matching yours (lesson 12.8). |
That single question resolves most of the confusion between reset and revert, which is the pair people mix up most.
The six situations
1. I changed a file and want the committed version back
$ git status --short M docs/faq.md$ git restore docs/faq.md
Nothing is printed, and git status is clean again. The edit is gone for good: Git never recorded it, so the reflog cannot help. This is the most genuinely destructive command in everyday use, which is why it is worth pausing on.
2. I staged something by mistake
$ git add docs/getting-started.md
$ git status --shortM docs/getting-started.md$ git restore --staged docs/getting-started.md
$ git status --short M docs/getting-started.md
The M moved from the left column to the right: staged, then merely modified. Your edit is kept; only the staging is undone. Note how similar the two commands are, and how different the consequences: --staged is the safe one.
3. My last commit needs fixing, and I have not pushed it
$ git commit -am "docs: add Windos section"
$ git log --oneline -12c4263b docs: add Windos section$ git commit --amend -m "docs: add Windows section"
$ git log --oneline -197fc096 docs: add Windows section
The hash changed, which is the sign that this rewrites history: the old commit is replaced rather than edited. Fine while it is yours alone, a problem the moment it is pushed. git commit --amend --no-edit also works for "I forgot a file": stage the file, then amend.
4. A commit is wrong and it is already pushed
$ git revert 97fc096[main 4bd6eb3] Revert "docs: add Windows section"
1 file changed, 2 deletions(-)$ git log --oneline -34bd6eb3 Revert "docs: add Windows section"
97fc096 docs: add Windows section
d3b92ec chore: add CI configuration and contributing guide
Both commits are in the history: the mistake and its undo. That is the point. Everyone else's clone still matches yours, and nobody has to do anything. This is the only safe undo for shared work.
5. I want to move my branch back, and I have not pushed
$ git reset --soft HEAD~1
$ git status --shortA .github/workflows/ci.yml
A .gitlab-ci.yml
A .markdownlint.json
A CONTRIBUTING.mdThe commit is gone from the branch and its changes are staged, ready to be re-committed differently. That is --soft; --mixed and --hard differ in where the changes land, which is lesson 12.4 and worth reading before using --hard.
6. I need to park unfinished work
$ git stash push -m "windows draft"Saved working directory and index state On main: windows draft$ git stash liststash@{0}: On main: windows draft$ git stash pop M docs/getting-started.md
?? notes.txt
The tree is clean while the work waits, and pop brings it back. Note that plain git stash leaves untracked files where they are; git stash -u includes them.
The pairs people confuse
| These look alike | The difference |
|---|---|
git restore <file> and git restore --staged <file> |
The first discards your edit; the second only unstages it |
git reset and git revert |
Reset rewrites the branch; revert adds a commit. Pushed means revert |
git commit --amend and a new commit |
Amend replaces; a new commit adds. Pushed means add |
git stash pop and git stash apply |
Pop removes the stash after restoring it; apply keeps it |
git restore and git checkout |
restore is the modern, clearer name; checkout still works and does more things (command page) |
How to do it
The map above, as commands. Before any of them, run git status: it tells you which situation you are in, and it often prints the exact command you need in its hints.
The Source Control view covers most of the map: Discard Changes on a file is git restore, the minus button unstages, Commit → Undo Last Commit is a soft reset of one, and the ⋯ menu has Stash and Revert Commit.
Rollback on a file is git restore. The Commit tool window unstages with the checkbox. Right-click a commit in the Log for Undo Commit, Revert Commit and Reset Current Branch to Here, which is the whole map in one menu.
A merged merge request has a Revert button that creates the revert commit on a new branch and opens a merge request for it. That is the platform's version of situation 4 and the right way to undo something that has shipped.
A merged pull request has the same Revert button, producing a new pull request that undoes it.
Common mistakes
- Reaching for
reseton pushed work. Userevert; the pushed commit is already in other people's clones. - Typing
git restore <file>when you meant--staged. One discards your work, the other does not. - Amending a pushed commit, which forces everyone else to reconcile a history that no longer matches.
- Using
git stashas storage. It is a pocket, not a drawer; stashes accumulate and get forgotten (command page). - Running the fix before reading
git status, which usually tells you which situation you are in.
Try it yourself
Goal: walk the whole map once, in a repository where nothing matters.
- Edit
docs/faq.md, checkgit status --short, then undo it withgit restore. - Edit it again,
git addit, and undo only the staging withgit restore --staged. Confirm the edit survived. - Commit with a deliberately misspelled message, then fix it with
git commit --amend. Note that the hash changed. - Commit a real change, then
git revertit. Note that both commits are ingit log. - Make an edit,
git stash push -m "test", confirm the tree is clean, thengit stash pop.
Expected result: five undos performed, and a feel for which ones changed history and which added to it.
Show solution
Step 3 and step 4 are the pair to remember. Amend changed the hash, which means the old commit no longer exists; revert added a commit, which means nothing was removed. Everything else in this section is a variation on that distinction.