Learning Objectives
By the end of this lesson you will be able to:
- Use
git restoreto discard file changes and to un-stage - Explain what problem
restoreandswitchwere introduced to solve - Translate the old
checkoutandresetrecipes into the modern commands - Restore a single file from any commit without moving your branch
Why These Commands Exist
For years, git checkout did three unrelated jobs:
git checkout main # switch branch
git checkout -- src/app.ts # discard changes to a file
git checkout 8f3c2a1 -- src/app.ts # get a file from another commit
One word, three meanings, distinguished by punctuation. The second and third are destructive and the first is not, and the difference is a --.
Git 2.23 split those jobs into two clearly named commands:
git switch— move between branches. Only that.git restore— put file contents back to some earlier state. Only that.
checkout still works and always will. But new code and new habits should use the specific commands, for the same reason you would not want one function called doThing().
git restore: Discarding Changes
git restore src/payments/refund.ts
That file goes back to how it looks in the last commit. Your edits to it are gone — permanently, since they were never committed.
git restore .
Every tracked file, back to HEAD. This is the "throw away my afternoon" command, so treat it with the same care as reset --hard.
Note that restore does not touch untracked files. Files Git has never seen survive it — removing those is git clean, in Module 7.
git restore --staged: Un-Staging
git restore --staged src/payments/refund.ts
The file leaves the index and goes back to being a modified-but-unstaged file. Your edits are kept — only the staging is undone.
This is the modern spelling of git reset <file>, and it is much clearer about what it does. Git even suggests it in git status:
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/payments/refund.ts
The Combination To Be Careful With
git restore --staged --worktree src/payments/refund.ts
Both at once: un-staged and reverted to HEAD. The edits are gone. This is the one form of restore that destroys work, so read the flags before pressing Enter.
Restoring From a Specific Commit
git restore --source=8f3c2a1 src/payments/refund.ts
Take that one file as it existed at that commit, and put it in your working directory. Your branch does not move; nothing else changes.
This is a genuinely useful tester tool. You have bisected a regression down to a commit and want to know whether one particular file is responsible:
git restore --source=HEAD~1 src/payments/refund.ts
# rebuild, retest — does the bug go away?
git restore src/payments/refund.ts # put it back
You have just tested a single file's contribution to a regression without touching the branch.
The Translation Table
| Old | Modern |
|---|---|
git checkout main |
git switch main |
git checkout -b feature/x |
git switch -c feature/x |
git checkout -- <file> |
git restore <file> |
git reset <file> |
git restore --staged <file> |
git checkout <sha> -- <file> |
git restore --source=<sha> <file> |
So When Is It Still reset?
The split is clean once you see it:
restoreworks on files. It never moves a branch.resetworks on the branch pointer. It moves history.
So git reset --soft HEAD~1 and git reset --hard origin/main from the previous lesson stay exactly as they were — those are branch operations and restore has no business doing them. It is only the file-level uses of reset that restore replaces.
Pro Tip: Read
git statusoutput rather than reciting commands from memory. It names the exact command for every state it reports — un-stage, discard, continue a rebase — and it is always correct for the Git version you are actually running.
Key Takeaways
switchmoves branches,restorerestores file contents;checkoutdid both and still worksgit restore <file>discards uncommitted edits permanentlygit restore --staged <file>un-stages while keeping the edits--staged --worktreetogether does both, and destroys the editsgit restore --source=<sha> <file>pulls one file from any commit without moving the branchrestorenever touches untracked files — that isgit cleanresetremains the command for anything that moves the branch pointer