■ the answer first

git stash push -m "why" -- <path> parks exactly the named file; every other dirty file never moves.

One file back out of any stash: git restore --source stash@{0} -- <path> — the entry stays.

Pathspec form ships since git 2.13 (May 2017); untracked files need -u.

THE SPLIT

Three dirty files, one must move. Watch the tree divide — one file to the stash, two never move:

BEFORE · git status --short

 M app.conf   ← must move
 M notes.md
 M main.py

AFTER

 M notes.md   ← never moved
 M main.py    ← never moved

stash@{0}: On main: app.conf prod tweak

app.conf is back at its committed state and sits in stash@{0} — the -m message earns its three seconds the moment git stash list has more than one entry; names like "WIP" age badly

THE PATHSPEC IS THE WHOLE TRICK

Plain git stash sweeps the whole working tree — every tracked modification goes in, everything returns to HEAD. Wrong shape when the dirty tree is mixed: one file ready to hand over, the rest genuinely unfinished. Two details worth knowing:

HALF A FILE: THE INTERACTIVE SPLIT

When the file itself is half-ready — three good hunks, one embarrassing one — git stash -p (or git stash push -p) walks each hunk and asks Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]?. Answer y for hunks that belong in the stash, n for the ones that stay; the result is a stash containing only what you approved. The same hunk-by-hunk machinery powers git add -p, so the reflex transfers. The two granularities, side by side:

git stash push -- <path>git stash -p
Granularitywhole filesindividual hunks
Speedone command, scriptableinteractive, per-hunk
Repeatable in CIyes (paths as arguments)no — needs a human at the prompt
Best for"this file, not the rest""this change, not that one"
Sincegit 2.13 (May 2017)long-available

Rule of thumb from the docs' own model: pathspec when the boundary is the file, -p when the boundary is inside it.

ONE FILE BACK OUT

git stash pop restores stash@{0} and deletes the entry — the normal path, but pop is all-or-nothing per stash, and a conflict aborts the pop with the entry preserved. Two finer-grained options:

# 1. apply without dropping (safe re-run)
git stash apply stash@{0}

# 2. pull ONE file out of a stash, leave the entry intact
git restore --source stash@{0} -- app.conf
# pre-2.23 spelling of the same operation:
git checkout stash@{0} -- app.conf

verify: git stash list after the restore → stash@{0} still standing — only pop deletes the entry

WHEN THE STASH IS THE WRONG TOOL

The stash is a scratchpad, not a branch. Stashes live only on your machine, carry no message beyond the one line, and are famously easy to forget — git stash list archaeology is a genre. Work that must survive days, travel between machines, or carry a real commit message belongs on a branch: git switch -c wip-labels, commit, switch back. The stash is for the next few hours, not the next sprint.

■ honest stamp Tested on: git 2.13+ pathspec · git 2.23+ restore · all majors. First published 2026-09-21. The pathspec form, the -u flag for untracked paths, the hunk prompt and the restore-from-stash spelling are git's documented stash behaviour — the before/after status output above is exactly what the sequence prints.

end of the single file · nothing fetched · everything local