How to Undo Mistakes in Git: reset, revert, restore and reflog
By Nitheesh Rajendran · · 4 min read
Git rarely loses work permanently, but its undo commands overlap and the names are confusing. This guide sorts them by situation: undoing changes in your files, undoing a commit that is already shared, rewriting local history, and recovering after a mistake.
The command output shown was captured from a throwaway repository with three commits (one, two, three).
First, find out what state you are in
Before undoing anything, look:
git status
git log --oneline
Most undo commands act on one of three places: the working tree (your files), the index (staged changes), or the commit history. Choosing the right command starts with knowing which one holds the change you want gone.
Discard changes in a file: git restore
git restore (Git 2.23 and newer) replaces a file in your working tree with the committed version.
echo x >> f # edit the file
git restore f # throw the edit away
This is destructive: uncommitted edits are not recoverable afterwards, because Git never recorded them. To unstage a file but keep your edits, use:
git restore --staged f
Undo a shared commit: git revert
git revert creates a new commit that applies the opposite change. History is not rewritten, so it is safe for commits that others may already have pulled.
git revert --no-edit HEAD
git log --oneline
f8a367c Revert "three"
726c197 three
f7b0f95 two
20187e1 one
The original commit stays in the history, and the new one cancels its effect. Use revert on main or any branch that other people use.
Move the branch back: git reset
git reset moves the current branch pointer to another commit. It rewrites local history, so use it on commits you have not pushed. It has three common modes:
| Mode | Branch moves | Index (staged) | Working tree |
|---|---|---|---|
--soft |
yes | kept | kept |
--mixed (default) |
yes | reset | kept |
--hard |
yes | reset | reset |
git reset --soft HEAD~1undoes the last commit but leaves its changes staged, ready to recommit with a better message or combined with other changes.git reset HEAD~1(mixed) undoes the commit and unstages the changes, but your files keep the edits.git reset --hard HEAD~1undoes the commit and discards the changes in your working tree.
The --hard mode is the one that can destroy uncommitted work, so check git status before using it.
Recover "lost" commits: git reflog
Even after a hard reset, the commits still exist in the object database for a while. The reflog is a log of where HEAD has pointed:
git reflog
f7b0f95 HEAD@{0}: reset: moving to HEAD
f7b0f95 HEAD@{1}: reset: moving to HEAD~2
f8a367c HEAD@{2}: revert: Revert "three"
726c197 HEAD@{3}: commit: three
f7b0f95 HEAD@{4}: commit: two
20187e1 HEAD@{5}: commit (initial): one
Find the entry from before the mistake, then move back to it:
git reset --hard 'HEAD@{3}' # back to the state at commit "three"
(Quote the expression so your shell does not interpret the braces.) Alternatively, create a branch at the old commit to keep it safely: git branch rescue 726c197.
The reflog is local to your clone and entries expire (by default after 90 days for reachable commits and 30 days for unreachable ones). It cannot recover changes that were never committed or staged.
Fix the last commit message or contents: git commit --amend
If the only problem is the most recent commit, amend it:
git add forgotten-file
git commit --amend
Amending rewrites that commit, so avoid it after pushing unless you are sure nobody else has pulled it.
Which command should I use?
- Edited a file and want the old version back:
git restore <file> - Staged the wrong file:
git restore --staged <file> - A pushed commit was wrong:
git revert <commit> - An unpushed commit was wrong, keep the changes:
git reset --soft HEAD~1 - An unpushed commit was wrong, discard everything:
git reset --hard HEAD~1 - I reset too far or lost a branch:
git reflog, thengit reset --hardto the entry - Only the last message or contents need fixing:
git commit --amend
Common mistakes
- Running
git reset --hardwith uncommitted work you wanted. - Force-pushing rewritten history to a shared branch. If you must, prefer
git push --force-with-lease, which fails if someone else has pushed in the meantime. - Reverting a merge commit without specifying the parent (
git revert -m 1 <merge>). - Assuming
git checkout .is the only way. It still works, butgit restoresays what it does.
Summary
Use restore for files, revert for shared history, reset for local history, and reflog as the safety net. Commit early and often: anything committed is almost always recoverable, and anything uncommitted may not be.