Decision guide: which undo do I need?
Intermediate Core Git
Why this matters
The rest of this section explains why. This page is for the moment when you need the answer, not the reasoning. Bookmark it; it is the page to open first when something has gone wrong. The reference section carries the same guide beside its siblings (Decision guides).
The flowchart
The same thing as a table
| What happened | Command |
|---|---|
| I edited a file and want the committed version back | git restore <file> — permanent, check git diff first |
| I staged the wrong thing | git restore --staged <file> — the edit is kept |
| I need to park this and do something else | git stash, later git stash pop |
| My last commit has a bad message, not pushed | git commit --amend -m "…" |
| I forgot a file in the last commit, not pushed | git add <file> then git commit --amend --no-edit |
| My last three commits should be one, not pushed | git reset --soft HEAD~3, then commit again |
| I want to drop the last commit and its changes, not pushed | git reset --hard HEAD~1 — check git status first |
| The commit is pushed and it is wrong | git revert <hash>, then push |
I committed to main by mistake, not pushed |
git branch <name> then git reset --hard origin/main |
| I have uncommitted work on the wrong branch | git stash, git switch -c <name>, git stash pop |
| I need this commit on another branch too | git switch <other> then git cherry-pick <hash> |
| A merge is half-done and conflicting | git merge --abort |
| A rebase is half-done and conflicting | git rebase --abort |
| The merge is committed but not pushed | git reset --hard ORIG_HEAD |
| The merge is pushed | git revert -m 1 <merge hash> |
| I deleted a branch I needed | git switch -c <name> <hash> from the delete message or git reflog |
| I deleted a file in a commit | git restore --source=<commit>~1 <path>, then add and commit |
| My commit vanished after a reset | git reflog, then git reset --hard <hash> |
| I am in detached HEAD with commits | git branch <name> <hash> |
| I dropped a stash | git stash apply <hash from the "Dropped" line> |
| My push is rejected after amending | git push --force-with-lease, on your own branch only |
| Someone force-pushed over my commit | git branch rescue/x <hash from your reflog>, then cherry-pick it back |
| I have no idea what I did | git reflog and read it before typing anything else |
The two questions, once more
Is it committed? If not, you are dealing with files: restore, restore --staged, stash. The reflog cannot help, because Git never saw the work.
Has it been pushed? If yes, add a commit that undoes it: revert. If no, you may rewrite freely: amend, reset, rebase.
Everything else in this section is detail hanging off those two.
Before you type anything
- Stop. More commands make the state harder to describe, not easier.
git status— which branch, what is staged, is an operation in progress.git log --oneline -5— what the history actually looks like.git reflogif something is missing.- Then choose from the table.
Thirty seconds of reading beats any command typed from memory, and the two commands in steps 2 and 3 change nothing.
What cannot be undone
Four things, all of them work Git never recorded (lesson 12.9):
- Uncommitted edits, after
git restoreorgit reset --hard. - Untracked files, after
git clean -fd. - Anything at all, in a repository folder you deleted.
- A commit older than the reflog's expiry, once Git has collected it.
Everything else on this page is a lookup, not a loss.
How to do it
The three commands that precede every fix:
$ git status
$ git log --oneline -5
$ git reflogThen one command from the table. If the fix does not do what you expected, git reflog again: your attempt is in it too.
The Source Control ⋯ menu covers the safe half of the table: Discard, Unstage, Stash, Undo Last Commit, Revert Commit. For anything involving a hash, use the terminal, which VS Code has built in.
The Log view's right-click menu is the closest thing to this table in a graphical tool: Revert Commit, Undo Commit, Reset Current Branch to Here…, Cherry-Pick, New Branch from Selected Commit. Local History covers the uncommitted cases.
The platform offers one entry from this table: Revert, on a commit or a merged merge request. That is deliberate, since it is the only one that is safe on shared history.
The same single entry: Revert on a merged pull request.
Common mistakes
- Typing a remembered command before reading
git status. - Reaching for
reseton pushed work whererevertis the answer. - Deleting the repository folder and re-cloning, which loses everything unpushed and is almost never necessary.
- Not copying the folder before an experiment on work that matters.
- Waiting. The reflog expires; recover today.
Try it yourself
Goal: rehearse the first thirty seconds.
- Put your practice repository into an odd state on purpose: start a merge that conflicts and leave it.
- Without looking at this page, run
git statusand write down what state you are in and what it suggests. - Check your answer against the table above.
- Apply the fix and confirm with
git log --oneline -3andgit status. - Repeat with a different mistake: commit to
main, or amend a pushed commit.
Expected result: you found the right command from git status alone, which is the skill this page exists to make unnecessary.
Show solution
Step 2 is the exercise; the table is the safety net. Git's status output names the operation in progress and lists the commands that end it, so most of this page is a restatement of something Git already told you. Reading that output is the habit that makes the rest of the section rarely needed.