Undoing Things: reset, revert, restore, reflog
· Jerwin Arnado ·
Sooner or later you git commit the wrong thing, git reset too hard, or delete a branch
you needed. The panic is always the same: did I just lose work? Almost never — Git is
remarkably hard to lose data in, if you know which of four commands matches your
situation. This is the cookbook. Find your scenario, run the command.
The mental model
Git has three places a change can live: the working directory (your files), the
staging area (git added, not yet committed), and history (committed). The four
undo commands each operate on different layers:
| Command | Operates on | Rewrites history? |
|---|---|---|
git restore |
working dir + staging | No |
git revert |
history (adds a new commit) | No |
git reset |
staging + history (moves the branch) | Yes |
git reflog |
the log of where HEAD has been | No (it’s a recovery lookup) |
The big safety line: restore, revert, and reflog are non-destructive to shared
history. reset --hard is the only one that can actually discard work — so it’s the one
to respect.
Scenario 1: discard uncommitted changes
You edited a file and want it back to the last commit. git restore (Git 2.23+, the
modern replacement for the overloaded git checkout -- file):
git restore path/to/file # discard working-dir changes to one file
git restore . # discard ALL unstaged changes — careful
Staged something by mistake and want to unstage it (keeping the edit)?
git restore --staged path/to/file
--staged moves it out of the staging area but leaves your edit in the working directory.
This is the “I git added too eagerly” undo.
Scenario 2: undo a commit you already pushed
The commit is on a shared branch, so you cannot rewrite history. Use git revert,
which creates a new commit that is the inverse of the bad one:
git revert a1b2c3d
A───B───C───D → A───B───C───D───D'
(bad) (D' undoes D)
History is preserved (D still exists), but its effect is cancelled by D’. This is the
only safe way to undo a pushed commit — and it pairs naturally with the
breaking-change discipline: the revert
itself is a revert: commit that explains what’s being walked back and why.
Scenario 3: undo a commit you have NOT pushed
The commit is local-only, so you can rewrite freely. git reset moves the branch
pointer backward. The flag decides what happens to the changes:
git reset --soft HEAD~1 # undo commit, KEEP changes staged
git reset HEAD~1 # undo commit, keep changes unstaged (--mixed, default)
git reset --hard HEAD~1 # undo commit, DISCARD changes entirely
| Flag | Branch pointer | Staging | Working dir |
|---|---|---|---|
--soft |
moves back | kept | kept |
--mixed (default) |
moves back | reset | kept |
--hard |
moves back | reset | discarded |
--soft is the everyday one: “I committed too early, give me my changes back staged so I
can redo it.” --hard is the dangerous one — it throws away working-directory changes
with no confirmation. Reach for it only when you’re certain.
Scenario 4: recover something you think you destroyed
Deleted a branch? reset --harded away a good commit? Botched
an interactive rebase? git reflog
is the time machine. It records every position HEAD has held — including ones that no
branch points to anymore:
git reflog
e0f1a2b HEAD@{0}: reset: moving to HEAD~1
3f2e1d0 HEAD@{1}: commit: feat(api): the work I thought I lost
9a8b7c6 HEAD@{2}: commit: earlier work
Find the commit you want back and restore it — either by resetting to it, or by branching from it:
git reset --hard HEAD@{1} # jump the current branch back to it
# or, safer — recover onto a new branch without touching the current one:
git branch recovered HEAD@{1}
Deleted a whole branch? Its last commit is still in the reflog (or findable via
git fsck --lost-found); git branch <name> <hash> brings it back. Commits survive in
the reflog for ~90 days before garbage collection — which is why “I lost my work” is
almost always “I haven’t run git reflog yet.”
A decision table
| Situation | Command |
|---|---|
| Discard edits to a file (not committed) | git restore <file> |
| Unstage a file (keep the edit) | git restore --staged <file> |
| Undo a pushed commit | git revert <hash> |
| Undo a local commit, keep changes | git reset --soft HEAD~1 |
| Undo a local commit, discard changes | git reset --hard HEAD~1 |
| Recover a lost commit/branch/rebase | git reflog → git reset or git branch |
Caveats and best practices
revertfor shared,resetfor local. This is the whole safety rule in five words. If the commit has left your machine, neverresetit —revertinstead.reset --hardandrestore .are the only data-losers here. Pause before either. If unsure whether you’ll want the changes back,git stashthem instead of discarding.- The reflog is local and personal. It records your HEAD movements, not your teammate’s. It can’t recover something that only ever existed on someone else’s machine.
git revertof a merge commit needs-mto pick which parent to keep — a sharp edge worth knowing before you hit it.
Conclusion
Four commands cover nearly every “undo” in Git:
git restore <file> # uncommitted changes
git revert <hash> # pushed commits (safe, adds a commit)
git reset --soft HEAD~1 # local commits (rewrites history)
git reflog # recover anything that looks lost
Match the command to the layer — working dir, history, or recovery — and the panic goes away. Git almost never actually loses your work; it just waits for you to ask the reflog.