skip to content

In Git, what is the difference between git restore --staged and git restore -p?

level: middleimportance: should knowfreq 36%

answer

  1. source and destination, not magic
  2. one flag names which tree changes
  3. -p changes granularity, not direction
  4. only one of the two is recoverable

basics

~20 s

git restore --staged changes the index, unstaging a path while leaving your working-tree edits intact. git restore -p asks hunk by hunk and, without --staged, overwrites working-tree hunks from the index — which permanently discards those edits.

solid answer

~50 s

`git restore` copies content from a source into a destination. The flags choose both. `--staged` makes the **index** the destination and HEAD the default source, so `git restore --staged <path>` unstages the file and leaves your working-tree version untouched. It is the inverse of `git add`. `-p` is patch mode and changes only the *granularity*: you are prompted per hunk instead of acting on the whole file. With no `--staged`, the destination is the **working tree** and the source is the index, so `git restore -p <path>` discards the hunks you select — they are overwritten with the staged content and are gone for good, because they were never hashed into an object. The two combine: `git restore --staged -p <path>` unstages selected hunks, and `git restore --staged --worktree <path>` resets both trees from HEAD, destroying uncommitted work in the process.

code

console · 4 lines
console
$ git restore --staged src/app.js     # unstage, keep edits on disk
$ git restore -p src/app.js           # pick hunks to discard on disk
$ git restore --staged -p src/app.js  # pick hunks to unstage
$ git restore --staged --worktree src/app.js  # both trees back to HEAD

go deeper

for a junior

Remember that --staged unstages while keeping your edits on disk, and that git restore without it overwrites the file and loses unstaged changes.

for a middle

Explain the source-and-destination model: which tree each flag writes to, where the default source comes from, and that -p only changes granularity.

for a senior

Emphasise the recoverability asymmetry — unstaging is reversible, working-tree restores are not — and the habit of staging or stashing before any destructive restore.

for a principal

Own the team-level framing: prefer commands whose flags state which tree they modify, and make sure destructive operations on unrecorded work are rare, deliberate and never buried in scripts or aliases.

## One command, two destinations `git restore` was introduced to give the "put content back" operations a name of their own, and its interface is a source and a destination rather than a set of memorised idioms. - **Destination.** `--worktree` (the default) restores the working-tree file; `--staged` restores the index entry; both flags together restore each of them. - **Source.** With `--worktree` alone the default source is the index. As soon as `--staged` is involved, the default source is HEAD. `--source=<tree>` names any commit or tree explicitly. Everything else follows from those two axes. `git restore <path>` throws away unstaged edits by copying the staged content over the file. `git restore --staged <path>` unstages by copying HEAD's content over the index entry, leaving the file alone. `git restore --staged --worktree <path>` puts both back to HEAD, which discards staged *and* unstaged work in one stroke. ## What -p changes, and what it does not `-p` does not change the direction of the copy — only how much of it you approve at a time. Git presents the diff between source and destination hunk by hunk with the familiar keys: y, n, s to split, e to hand-edit, q to quit. So `git restore -p <path>` walks the difference between the index and the working tree, and each hunk you accept is reverted on disk. That is the tool for "I want to keep three of my five experimental changes", and it is destructive by design. `git restore --staged -p <path>` instead walks the difference between HEAD and the index, unstaging the hunks you pick — useful when patch-staging went one hunk too far. ## The asymmetry in recoverability This is the part worth being loud about. Unstaging is safe: the content is still in your working tree, and the blob you staged is already in the object database, so even a mistake leaves a recoverable copy. Restoring the working tree is not safe. Working-tree edits that were never staged and never committed exist in exactly one place — the file — and once overwritten there is nothing to recover them from. The reflog tracks refs, not files you never asked Git to record. The practical habit: if you are unsure whether you want the changes back later, stage or stash them before restoring, so a copy exists in the object database. And read the prompt carefully in patch mode, because the same keys mean "keep this" in `git add -p` and "throw this away" in `git restore -p`. ## Why the modern spelling is preferred The older equivalents overload one command with unrelated jobs — the same verb switched branches, restored files and unstaged changes depending on its arguments, which is exactly why the operations were hard to teach and easy to confuse. Splitting them into `git switch` for moving between branches and `git restore` for putting content back means the flag you type states which tree you are about to modify, and reviewers of your shell history can see it too. One last detail: `git restore` operates on paths and never moves HEAD or a branch pointer. If a path does not exist in the source, restoring can remove it from the destination, so a path you created after the source commit can disappear from your working tree when you restore both trees from HEAD.

  • Which of these operations can you undo, and which cannot?
    Unstaging with `--staged` is recoverable — the content is still in your working tree, and the staged blob already exists in the object database. Restoring the working tree is not: unstaged, uncommitted edits live only in the file, so overwriting them leaves nothing to recover from. The reflog tracks refs, not unrecorded file content.
  • What does git restore --staged --worktree on a path do?
    It restores both the index entry and the working-tree file from HEAD, wiping staged and unstaged changes to that path in one step. Treat it as the deliberate "throw away everything I did to this file" command, and stage or stash first if any of that work might still be wanted.
  • How do you unstage just a few hunks after git add -p staged too much?
    Run `git restore --staged -p <path>`. That walks the difference between HEAD and the index and unstages the hunks you accept, leaving your working tree completely untouched — so the changes are still on disk, just no longer part of the next commit.

saying these in an interview costs you the question

  • Thinks --staged also reverts the file on disk
  • Believes restored working-tree edits are in the reflog
  • Assumes -p reverses the direction of the restore
  • Uses the same key reflex as git add -p without reading the prompt
  • Says git restore moves the branch pointer

context