skip to content

What does git add -p do, and why would you stage only part of a file?

level: middleimportance: must knowfreq 62%

answer

  1. work at hunk granularity, not file
  2. the working tree is not modified
  3. one intent per commit
  4. it doubles as a self-review pass

basics

~20 s

git add -p walks the diff between your working tree and the index hunk by hunk and asks whether to stage each one. Only the accepted hunks enter the index, so one messy file can become several focused commits.

solid answer

~50 s

`git add -p` (short for `--patch`) puts you in interactive patch mode. Git computes the diff between the working tree and the index, splits it into hunks, and prompts you for each: stage it, skip it, split it further, or edit it by hand. Accepted hunks are applied to the index only — your working-tree file is never modified, so nothing is lost by declining a hunk. The payoff is atomic commits. When a single file ends up holding a bug fix, a rename and a stray debug print, you stage just the fix, commit it, then handle the rest separately. Reviewers get one intent per commit, and a later bisect or revert lands cleanly. A second benefit is that it forces you to reread your own diff line by line, which is where leftover debugging code usually gets caught. One caution: the staged subset is a state nothing ever compiled, so check it before committing.

code

console · 9 lines
console
$ git add -p src/cart.js
@@ -8,6 +8,7 @@ function total(items) {
-  return items.reduce((a, b) => a + b.price, 0);
+  console.log(items);
+  return items.reduce((a, b) => a + b.price ?? 0, 0);
 }
Stage this hunk [y,n,q,a,d,s,e,?]? s
$ git diff --cached
$ git commit -m "fix: treat a missing price as zero"

go deeper

for a junior

Know that -p stands for patch mode and lets you stage some hunks of a file and not others, and that saying no to a hunk leaves it safely in your working tree.

for a middle

Explain that Git diffs the working tree against the index, splits it into hunks, and applies only the accepted ones to the index, and name the untracked-file limitation and its intent-to-add workaround.

for a senior

Show the production judgment: commits split by intent so bisect and revert stay useful, plus the discipline of verifying a staged subset that was never built before it lands on a shared branch.

for a principal

Own commit hygiene as a team property — argue what an atomic commit buys in review throughput and incident response, and where enforcing it becomes ceremony rather than value.

## What patch mode is doing `git add -p` is not a different way of adding files; it is the ordinary add operation driven at the granularity of a diff hunk. Git diffs the working tree against the index, chops the result into hunks (a run of changed lines plus surrounding context), and presents them one at a time. Answering yes applies that hunk to the index; answering no leaves the index untouched for those lines. Crucially the working tree is read-only in this flow. Declining a hunk does not discard it — it simply stays unstaged, still on disk, ready for the next commit. That is what makes patch mode safe to explore. ## Why partial staging matters Real work is messy. You open a file to fix a null check, notice a badly named variable, tidy the imports your editor complained about, and add a `console.log` to trace something. Committing all of that together produces a commit whose message can only be a lie by omission, and whose diff a reviewer has to mentally unpick. Partial staging turns that one working tree into a sequence of commits that each say one true thing. The benefits compound downstream: a bisect that lands on a small commit points at a small suspect; a revert of the fix does not also revert the rename; `git log` on a file becomes a narrative rather than a pile of "various changes". The review effect is the real reason experienced engineers use it habitually. Walking hunk by hunk is a self-review pass you get for free — debug statements, commented-out experiments and accidental whitespace churn all surface at the exact moment you are deciding whether they belong in the commit. ## Where the mode shows up elsewhere The same interactive machinery backs several commands, and knowing that saves learning four interfaces: `git commit -p` stages hunks and commits in one step, `git restore -p` picks hunks to discard or unstage, `git checkout -p` is the older spelling of the same idea, `git reset -p` unstages hunks, and `git stash push -p` stashes a selected subset. The prompts and keys are identical everywhere. `git add -i` is the menu-driven front end over the same operations, offering numbered choices — status, update, revert, add untracked, patch, diff, quit, help — with `patch` dropping you into exactly the loop `-p` gives you directly. Most people go straight to `-p`. ## Two limitations to name **Untracked files are invisible.** Patch mode diffs against the index, and a file with no index entry has no diff to show, so `git add -p` will not offer it at all. `git add -N <path>` (also spelled `--intent-to-add`) records an empty entry for the path, after which its whole content appears as one addition hunk you can split and stage selectively. **The staged state was never tested.** Whatever you assemble in the index is a combination that has not existed on disk, so it may not compile. If the fix hunk you staged calls a helper defined in a hunk you skipped, the commit is broken in isolation and a future bisect will blame it. Reading `git diff --cached` before committing catches most of this; for anything risky, verify the staged snapshot properly before it lands.

  • Why does git add -p never offer hunks from a brand-new untracked file, and how do you fix that?
    Patch mode diffs the working tree against the index, and an untracked file has no index entry, so there is no diff to split. Run `git add -N <path>` (intent-to-add) to record an empty entry first; the file's content then appears as a single addition hunk that you can split and stage selectively.
  • What is the risk of committing a partially staged change, and how do you guard against it?
    The staged snapshot is a state that never existed on disk, so it may not build — for example if you staged a call but skipped the function it calls. That breaks bisect later, when the commit fails for reasons unrelated to the bug. Review `git diff --cached` before committing, and for risky splits verify the staged tree builds before you push it.
  • Which other Git commands share this interactive hunk interface?
    `git commit -p`, `git restore -p`, `git checkout -p`, `git reset -p` and `git stash push -p` all use the same hunk loop and the same keys, and `git add -i` wraps the operations in a numbered menu. Learning the keys once covers staging, unstaging and discarding.

It is the difference between mailing the whole desk drawer and picking out the two documents that belong in this envelope.

saying these in an interview costs you the question

  • Thinks -p edits or discards working-tree changes
  • Believes it can stage untracked files as-is
  • Assumes the staged subset is guaranteed to compile
  • Says partial staging rewrites existing commits
  • Treats declining a hunk as losing the change

context