Separate HEAD, index, and working tree before you restore anything
- Run git status to identify staged versus unstaged paths.
- Use git diff for working-tree changes and git diff --staged for index changes before deciding what is disposable.
- State the desired transition in words: “unstage but keep edits,” “discard unstaged edits,” or “recover this path from commit X.”
- Use --patch when only some hunks are unwanted; do not replace a whole file just because the command is shorter.
Which layer will change?
README.md has edits in the working tree and you already staged them. If you then change README.md again and run git restore README.md, which version remains: HEAD, the staged version, or the newest unstaged edit?
Reveal the reasoning
The working-tree copy is restored from the index by default, so it becomes the staged version. The index remains staged. A plain restore does not move HEAD and does not unstage the file.
Choose by the state transition you actually need
Change path content in the working tree, index, or both without moving branch history.
Add a corrective commit when published history should remain intact.
Change the index or move HEAD when private-history state itself needs to change.
Options that change the destination or source
--stagedRestore the selected path in the index. Without an explicit --source, the source defaults to HEAD.
Unstaging selected changes while keeping the working-tree edits available for further work.--worktreeRestore the selected path in the working tree. This is the default destination when --staged is not supplied.
Replacing local tracked-file content with the chosen source after you have verified the local edits are disposable.--source=<tree>Choose the commit, branch, tag, or other tree whose content should become the restore source.
Recovering a file version from a known commit rather than simply matching the index or HEAD.-p, --patchInteractively choose individual hunks to restore instead of replacing every change in the path.
Keeping some local edits while discarding only selected hunks.--staged --worktreeRestore both the index and working-tree copy of the selected path.
Only when you deliberately want both local states to match the chosen source, typically after inspecting what will be discarded.Move one layer at a time
Beginner: unstage a file but keep the edits
You staged config/app.yml too early. The edits are still useful, but you do not want them in the next commit yet.
git status
git diff --staged -- config/app.yml
git restore --staged config/app.yml--staged updates the index entry from HEAD by default. The working-tree copy stays edited, so the change becomes unstaged rather than disappearing.
Expected shape: git status shows config/app.yml under Changes not staged for commit.Practical: discard one tracked file’s unstaged edits
A generated local edit to src/theme.css is definitely unwanted. You have inspected the diff and want the working copy to match the staged/index version again.
git diff -- src/theme.css
git restore src/theme.cssWithout --staged, restore targets the working tree and uses the index as the default source. The unstaged tracked-file edits are replaced, so inspect the diff first.
Advanced: recover one file from a known earlier commit
A refactor changed many files, but you want docs/diagram.md to match commit 4f2c1ab while leaving the branch pointer and other files alone.
git show 4f2c1ab:docs/diagram.md
git restore --source=4f2c1ab --worktree -- docs/diagram.md
git diff -- docs/diagram.mdThe explicit source makes the intended file state inspectable. Only the selected working-tree path changes; HEAD and branch history stay where they are.
Selective: discard only some hunks
A file contains one accidental formatting change and one useful bug fix. You want to keep the bug fix.
git diff -- src/parser.ts
git restore -p -- src/parser.tsPatch mode asks about individual hunks so you can reverse only the unwanted parts instead of replacing the entire file.
When restore would discard the wrong state, stop before execution
The most important restore recovery skill is prevention. Local edits that were never committed or stashed may not have an easy Git recovery path after they are overwritten.
- Read all local layers
Inspect repository state plus both unstaged and staged diffs before choosing a restore destination.
git status git diff git diff --staged - Inspect an explicit historical source
If you plan to restore from a commit, view that file from the source first so the incoming content is not a surprise.
git show <commit>:<path> - Prefer the narrowest change
Restore one path or use patch mode instead of applying a broad worktree/index replacement.
- Verify immediately
After restoring, repeat status and the relevant diff so the result is proven before additional edits hide what happened.
git status git diff git diff --staged - Use history-oriented tools for history problems
If the recovery goal involves a published commit or moving private HEAD, stop using restore and inspect revert or reset instead.
Failure modes worth recognizing
Running git restore on a tracked file before inspecting the diff
A plain working-tree restore can remove local edits. Git is doing exactly what was requested even if those edits were valuable.
Recovery: Inspect with git diff first. If the discarded content was never committed or stashed, recovery may be difficult or impossible, so treat inspection as the safety step rather than relying on an undo afterward.Confusing --staged with “delete my changes”
git restore --staged normally changes the index while leaving the working-tree file edited. It is an unstaging operation, not the same as discarding the file edit.
Recovery: Run git status plus git diff and git diff --staged to verify which layer still contains the change.Using restore when the real problem is branch history
Restore changes paths in the working tree/index; it does not move HEAD, erase commits, or add an auditable inverse commit.
Recovery: If you need to preserve shared history while undoing a published commit, consider git revert. If private branch history itself must move, inspect git reset deliberately.Restoring both --staged and --worktree without naming the source mentally
Changing both layers can remove both staged and unstaged variants of the selected path. The operation is much easier to reason about when you state the desired source explicitly.
Recovery: Before running the command, say “make both copies match HEAD” or name another source, inspect the existing diffs, then run the exact restore you intend.Predict the local-state transition before revealing it
You staged src/api.ts, then realized it needs one more edit before the commit. You want to remove it from the index but keep every working-tree change. What should you inspect and run?
Hint
The destination you want to change is the index, not the working tree.
Reveal command and reasoning
git status
git diff --staged -- src/api.ts
git restore --staged src/api.ts
git statusThe staged diff proves what is in the index. --staged restores that index entry from HEAD by default while leaving the working-tree content available for continued editing.
Build intuition across HEAD, index, and working tree
In a disposable repository, edit one tracked file and stage it. Edit it again so staged and unstaged versions differ. Predict and test plain git restore, git restore --staged, and git restore --source=HEAD --staged --worktree on separate disposable copies or repeated reset setups. Finish by using git restore -p to discard one hunk while keeping another, and explain which layer changed after each operation.
Questions that change the recovery choice
What does plain git restore <file> restore from?
When --staged is not used, the working tree is the default destination and the index is the default source. That means a plain restore usually discards unstaged changes in a tracked file so it matches the staged/index version.
How do I unstage without losing my edits?
Use git restore --staged <path>. By default the index entry is restored from HEAD while the working-tree file remains edited.
How is restore different from reset?
Restore is path-focused and works on the working tree/index without moving branch history. Reset can move HEAD in commit mode and can also update the index. Choose based on the state you intend to change, not on which command sounds like “undo.”
How is restore different from revert?
Restore changes local file/index state and does not create a corrective commit. Revert preserves published history by adding a new commit whose patch reverses an earlier commit.
Can I restore only part of a file?
Yes. Use git restore -p <path> to select hunks interactively. Inspect the diff first so each decision is tied to a known local change.