Every Way to Undo, and Which One You Want
Four commands undo four different things and the internet recommends them interchangeably. Name what you regret first — a file, a staging decision, a commit, a pushed commit — and the right command falls out of the answer.
Worth reading first: Making a Commit Worth Reading
Four commands undo four different things and the internet recommends them interchangeably. Name what you regret first — a file, a staging decision, a commit, a pushed commit — and the right command falls out of the answer.
Name what you regret before picking a command
This chapter exists because the top search result for "git undo" is a Stack Overflow answer with eleven commands and forty thousand upvotes, and running the wrong one of them destroys work.
The reliable method is to stop and say out loud what you want to lose. There are only six answers, and each has exactly one right command.
$ git restore path/to/file
Destroys work permanently
Overwrites the file from the last commit
Your edit was never in Git — not committed, not staged, not stored anywhere. Nothing can bring it back. This is the single most dangerous command a beginner runs while trying to be careful.
Want to keep it for later instead? git stash sets it aside where you can get it back.
restore throws away work, and does not ask
git restore overwrites a file in your working tree with the version from the last commit. Whatever you had typed is gone — not in the index, not in a commit, not in the reflog. Gone.
$ git restore src/app.js # this file, back to the last commit$ git restore . # every modified file. There is no confirmation prompt.There is no undo for this because there is nothing to undo from. Git can recover anything it has ever recorded, and it never recorded that edit.
If you are not sure you want to lose it, do not. git stash sets your changes aside somewhere you can get them back.
$ git stash push -m "half-finished search filter"# working tree is clean now $ git stash list # what is stashed$ git stash pop # bring the most recent one back and remove it from the stash$ git stash apply stash@{1} # bring an older one back and KEEP it in the stashreset moves a branch pointer backwards
git reset does one thing: it points your current branch at a different commit. What it does to your files afterwards is decided entirely by which of three flags you pass, and those three flags are the whole difficulty.
--soft
Move the branch. Leave the index and the working tree alone. Your changes are still staged, ready to be re-committed differently.
--mixed (the default)
Move the branch and reset the index. Your changes are in the working tree, unstaged. Nothing is lost.
--hard
Move the branch, reset the index, and overwrite the working tree. Your changes are destroyed. This is the one people mean when they say reset is dangerous.
# "I want to redo the last commit differently"$ git reset --soft HEAD~1# ... adjust what is staged ...$ git commit -m "The message and contents I actually meant" # "Uncommit the last three, keep all the work in my folder"$ git reset HEAD~3 # "Throw the last commit away entirely, including the code"$ git reset --hard HEAD~1HEAD~1means "one commit before HEAD", HEAD~3 means three before, and so on. You can also give a hash directly.
revert adds a commit that cancels an old one
git revertis the safe undo, and it is the only one that is honest about what happened. It does not remove the bad commit. It computes the opposite of that commit's diff and commits that.
$ git revert 2c7a91f # opens an editor with a pre-written message$ git revert --no-edit 2c7a91f # accept the default message$ git revert HEAD # undo the very last commit, safely* 8e2f1d5 Revert "Cache the user lookup for 60 seconds"* 2c7a91f Cache the user lookup for 60 seconds* 1a5e8c3 Set up the routerBoth commits are in the history. That is not clutter — it is the record. Anyone reading it later sees that the caching was tried and withdrawn, which is genuinely useful information and is exactly what a reset would have erased.
Pretends the commit never existed. Only safe on commits that only you have.
Records that it existed and was undone. Safe always, including on main, including after a hundred people pulled it.
Pushed? Revert. Not pushed? Either, and reset is tidier.
reflog is the safety net under all of it
Every time HEAD moves — every commit, checkout, reset, merge, rebase, amend — Git writes a line to the reflog. It is local, it is not part of the history, nobody else ever sees it, and it keeps roughly ninety days.
It is how you recover from a reset --hard you should not have run.
$ git reflog9f3c1a2 HEAD@{0}: reset: moving to HEAD~34b8e0d7 HEAD@{1}: commit: Handle the empty next param2c7a91f HEAD@{2}: commit: Fix the redirect when next is empty7d1f6b4 HEAD@{3}: commit: Add the settings page1a5e8c3 HEAD@{4}: checkout: moving from main to fix/login- 1
Find where you were before the mistake
HEAD@{1} here is the commit the branch pointed at immediately before the reset. Its hash is 4b8e0d7.
- 2
Look before you leap
git show 4b8e0d7 confirms it is the commit you think it is. Cheap, and it costs nothing to check.
- 3
Put the branch back
git reset --hard 4b8e0d7 moves your branch back to where it was. The three commits are exactly as they were, with the same hashes.
Key takeaways
- Say what you want to lose before you pick a command. Six situations, six answers.
- git restore overwrites a file from the last commit and the edit is unrecoverable — it was never in Git.
- git stash sets work aside safely when you are not sure you want to lose it.
- git reset moves the branch pointer; --soft keeps changes staged, --mixed keeps them unstaged, --hard destroys them.
- git reset --soft HEAD~1 is how you redo the last commit differently.
- git revert adds a new commit that cancels an old one, leaving both in the history.
- Pushed? Revert. Not pushed? Reset is fine and tidier.
- Reverting a merge needs -m 1 to say which parent was the mainline.
- git reflog records every move of HEAD for about ninety days and can recover a branch after a bad reset --hard.
- The reflog cannot recover work that was never committed. That is the case for committing often.
Quick check
Answer these to unlock the next chapter — 3 of 4 to pass. You can retake it anytime.
Answer every question to check.
Make a free account to read on
Every chapter is free — an account is how your progress, XP, and streak follow you from your laptop to your phone, and how you show up on the leaderboard. No payment, no trial.