Lesson 13 of 30
"I want this back" has four different real answers depending on exactly what happened — restore, reset, revert, or the reflog. Picking the wrong one can make things worse.
20 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
"Undo" in Git isn't one command — it's a decision tree, and picking the wrong branch of it can genuinely make things worse (rewriting history that's already been pushed, or discarding work you meant to keep). The three tools that matter, in order of how much they touch:
git restore — undoes changes to files, scoped to the working directory and/or staging area. It never touches commit history.git reset — moves the current branch pointer to a different commit, and optionally rewrites the working directory and staging area to match. It does touch history — it can make commits unreachable from any branch.git revert — makes a new commit that undoes the changes of an earlier one. History isn't rewritten; it grows forward instead.The rule that decides between reset and revert: has this been pushed and shared? If yes, revert — nobody else's clone breaks. If it's still local and only you have it, reset is fine and often cleaner.
Scenario 1 — you edited a file and want to throw the changes away, keeping the file at its last-committed state:
$ git status
modified: src/api/client.ts
$ git restore src/api/client.ts
$ git status
nothing to commit, working tree clean
This is the modern replacement for git checkout -- <file> — same effect, clearer name. It only touches the working directory; nothing else moves.
Scenario 2 — you accidentally git rm'd or overwrote a file that's still in the last commit, and want it back exactly as committed:
$ rm src/api/client.ts # oops
$ git restore src/api/client.ts
$ ls src/api/client.ts
src/api/client.ts
restore pulls the file's content straight from HEAD (or --source=<commit> for an older version) back into the working directory. This works even for a file deleted with plain rm, as long as it was committed — Git has the blob in the object store regardless of what's currently on disk.
Scenario 3 — you want to undo the last commit itself, and you understand what each reset mode does to the three trees (working directory, staging area, and HEAD):
$ git log --oneline -1
a1b2c3d add broken retry logic
$ git reset --soft HEAD~1 # HEAD moves back; staging + working dir untouched
$ git status
modified: src/api/client.ts # still staged, exactly as it was in that commit
$ git reset --mixed HEAD~1 # (the default) HEAD + staging move back; working dir untouched
$ git status
modified: src/api/client.ts # unstaged now, changes still on disk
$ git reset --hard HEAD~1 # HEAD + staging + working dir all move back
$ git status
nothing to commit, working tree clean # the changes are just gone from disk
--soft is for "I want to redo this commit's message or squash it into the next one" — nothing about the actual edits is lost. --mixed (the default if you type no flag) is for "I want to re-stage these differently." --hard is for "I genuinely want these changes gone" — and it's the one that can lose real work if you're wrong about what's committed elsewhere.
Scenario 4 — the bad commit is already pushed and someone else may have pulled it. reset would rewrite shared history; revert doesn't:
$ git revert a1b2c3d
[main 9f4e2d1] Revert "add broken retry logic"
1 file changed, 3 deletions(-)
$ git log --oneline -3
9f4e2d1 Revert "add broken retry logic"
a1b2c3d add broken retry logic
7c88fe0 add auth middleware
Both commits are still there. revert computed the inverse of a1b2c3d's changes and committed that inverse as a new, ordinary commit. Anyone who already pulled a1b2c3d just pulls the revert next — no force-push, no rewritten history, no coordination needed.
--hard reset feels irreversible but usually isn't — immediately. The commit you reset away from isn't deleted right away; it becomes unreachable but stays in the object store until Git's garbage collector eventually prunes it (default ~30 days). The reflog (its own lesson) can get you back to it in the meantime — but don't treat that as a safety net to lean on; treat --hard as if it were permanent.reset on a branch others have already pulled creates a mess, not a clean fix. If you reset --hard a pushed branch and force-push it, anyone who already pulled the old commits now has a diverged history that doesn't fast-forward — their next git pull fails or silently merges in the "undone" commits again. This is exactly why shared history gets revert, not reset.restore and checkout overlap, which is exactly why restore exists. git checkout does file-restore, branch-switching, and commit-detaching, three unrelated jobs behind one command name — a famous source of Git confusion. git restore is the modern, narrower tool for "give me this file's content back"; reach for it over checkout for file operations.Every reset mode is explained entirely by which of the three trees (HEAD, staging/index, working directory) it moves — the exact model from the three-trees concept. If that lesson clicked, --soft vs --mixed vs --hard stop being three commands to memorize and become one sliding scale of "how far back do I move the pointer."