Git undo cheat sheet: fix mistakes safely
This is the sheet you keep open in a tab. Git records three levels of state — the working tree, the index (staged) and commits — and each mistake lives at one of those levels, so each has a matching undo. The single rule that keeps you safe: read git status and prefer the least destructive fix first.
Almost nothing is truly lost. Even a hard reset leaves the old commits in the reflog for about 90 days, so a 'deleted' commit can usually be recovered.
Published
Pick the right undo
| Mistake | Safe fix | What happens |
|---|---|---|
| Edited a file, want it back | git restore <file> | discards unstaged working-tree changes, restoring the file to the index/HEAD |
| Staged the wrong file | git restore --staged <file> | un-stages the file, keeping your changes |
| Wrong commit message | git commit --amend | rewrites the last commit's message (local commits only) |
| Forgot a file in the last commit | git add <file> && git commit --amend --no-edit | adds the file to the last commit |
| Last commit is bad (pushed) | git revert <sha> | creates a NEW commit that reverses it — safe for shared history |
| Last commit is bad (local) | git reset --soft HEAD~1 | moves HEAD back one, keeping all changes staged |
| Throw away the last commit entirely | git reset --hard HEAD~1 | moves HEAD back and discards the changes — destructive |
| Recover a 'lost' commit | git reflog | shows every recent HEAD move so you can reset back to the old hash |
Reset, explained
# --soft: move HEAD back, keep everything staged (changes survive, ready to recommit)
git reset --soft HEAD~1
# --mixed (default): move HEAD back, unstage but keep changes in the working tree
git reset HEAD~1
# --hard: move HEAD back AND discard changes from working tree and index
git reset --hard HEAD~1
What each flag touches
- --soft
- moves only the branch pointer — commits, index and working tree are all left alone
- --mixed
- moves the pointer and resets the index — changes become unstaged but remain on disk (the default)
- --hard
- moves the pointer and resets index and working tree — changes are discarded
- HEAD~1
- the parent of the current commit; HEAD~2 is two back — use a hash to jump anywhere
git reset --hard permanently discards uncommitted work in the files it resets. If you might want it back, snapshot it first with git stash or a quick git add . && git commit. The commits themselves are still recoverable from the reflog, but uncommitted working-tree changes are not.Revert vs reset
git revert — it writes a new commit that undoes the old one, so history is appended, never rewritten. Keep git reset for local, unpushed commits, because reset moves the branch pointer backwards and rewriting pushed history will force a conflict for collaborators.# Reverse the effects of commit a1b2c3d and commit it as a new change
git revert a1b2c3d
# Revert a whole range (older..newer), committing each reversal
git revert HEAD~3..HEAD
# Revert but don't commit yet — stage the reversal for review
git revert --no-commit a1b2c3d
Stash: park changes without committing
# Save working changes and clean the tree
git stash
# Bring the most recent stash back and remove it from the list
git stash pop
# Bring it back but keep it in the stash list
git stash apply
# See what is stashed
git stash list
# Drop one stash (stash@{0} is the newest)
git stash drop stash@{0}
# Stash everything including untracked files
git stash -u
git stash list shows stash@{0}, stash@{1} and so on. pop applies and removes the top entry; apply applies it and leaves it in the list, which is the safer choice when you are unsure whether the merge back will succeed.The reflog: your undo button for undo
# Every place HEAD has been, newest first
git reflog
# Output looks like: a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# Restore the state from before the reset:
git reset --hard HEAD@{1}
# Or restore a specific commit by its hash:
git reset --hard a1b2c3d
gc.reflogExpire). Because it is local, it survives the mistakes that rewrite history, which is exactly why it is the standard recovery path after a bad --hard or rebase.References
- git-reset — the official semantics of --soft, --mixed and --hard.
- git-revert — undoing commits by appending, not rewriting.
- git-reflog — the recovery log and its expiration behaviour.
FAQ
How do I undo my last commit but keep the changes?
Use git reset --soft HEAD~1 to move the branch pointer back one commit while leaving the changes staged, or git reset HEAD~1 (mixed) to keep the changes but unstage them. Both keep your work; only git reset --hard HEAD~1 deletes it. If you already pushed, prefer git revert
What is the difference between git reset and git revert?
git reset moves the current branch pointer backwards, rewriting history, and can discard changes; it is for local, unpushed work. git revert computes the inverse of an existing commit and records it as a new commit on top of history, leaving the past intact — the safe choice for commits that have already been pushed and shared.
How do I undo a pushed commit?
Run git revert
Can I recover a commit I deleted with git reset --hard?
Usually yes. Run git reflog to find the hash of the state before the reset (look for the reset entry and the commit that came before it), then git reset --hard
How do I unstage a file?
git restore --staged
Related tools
- IPv4 subnet calculator — break any CIDR block into network, range, broadcast and usable hosts.
- IP range to CIDR — turn an arbitrary address range into its minimal covering CIDR blocks.
- VLSM calculator — split a block into right-sized subnets by host requirements.