Skip to content

← Writing

engineering

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 refloggit reset or git branch

Caveats and best practices

  • revert for shared, reset for local. This is the whole safety rule in five words. If the commit has left your machine, never reset it — revert instead.
  • reset --hard and restore . are the only data-losers here. Pause before either. If unsure whether you’ll want the changes back, git stash them 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 revert of a merge commit needs -m to 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.