git stash a Single File Without Losing the Rest
Stash one file while the rest of your work stays put: git stash push -- <path>, then pop one file back out of any stash with git restore. Copy-paste commands.
A code review lands while three files are half-edited and only one of them can move. Stashing everything and re-typing the other two files is the ritual nobody misses.
Bring everything back with git stash pop, or pick one file out of any stash with git restore --source stash@{0} -- <path>. Stash the file, not the tree.
git stash push -m "app.conf prod tweak" -- app.conf
git stash list
# stash@{0}: On main: app.conf prod tweak
git stash pop
How do you stash a single file in git?
The pathspec is the whole trick.
Plain git stash sweeps the whole working tree — every tracked modification goes into the stash and everything goes back to HEAD. That is the wrong shape when the dirty tree is mixed: one file is ready to hand to someone, the rest is genuinely unfinished. The fix is the push verb with a pathspec, per the git-stash documentation:
# before: three dirty files
$ git status --short
M app.conf
M notes.md
M main.py
# stash only app.conf
$ git stash push -m "app.conf prod tweak" -- app.conf
Saved working directory and index state On main: app.conf prod tweak
$ git status --short
M notes.md
M main.py
verify: git status --short after the push → notes.md + main.py still dirty, app.conf clean
app.conf is back at its committed state and sits in stash@{0}; notes.md and main.py never moved. The -m message is optional but earns its three seconds the moment git stash list has more than one entry — stash names like "WIP" age badly.
Two details worth knowing:
- The pathspec comes after
--. Everything after the double dash is paths, not flags. This is the same convention asgit checkout -- <path>and it is not decorative:git stash push -- main.pyandgit stash push main.pydiffer in older git versions where a bare path could be misread. - Untracked files need
-u. A brand-new file has no tracked history to stash, so plainpushskips it.git stash push -u -- newfile.pyincludes it;-agoes further and sweeps ignored files too.
How do you stash part of one file?
When the file itself is half-ready — three good hunks, one embarrassing one — the interactive flow splits it:
git stash -p # or: git stash push -p
Git walks each hunk and asks Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]?. Answer y for the hunks that belong in the stash, n for the ones that stay. The result is a stash containing only what you approved, with the rest of the file still dirty in the working tree. The same hunk-by-hunk machinery powers git add -p, so the reflex transfers.
git stash push -- <path> |
git stash -p |
|
|---|---|---|
| Granularity | whole files | individual hunks |
| Speed | one command, scriptable | interactive, per-hunk |
| Repeatable in CI | yes (paths as arguments) | no — needs a human at the prompt |
| Best for | "this file, not the rest" | "this change, not that one" |
| Since | git 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.
How do you pop a single file from the stash?
One file out, the stash stays.
git stash pop restores stash@{0} and deletes the entry. That is 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
The restore/checkout form is the one that answers "I stashed three changes together and now only need one of them back" — it copies that path's stashed content into the working tree and leaves stash@{0} standing. Note it overwrites the working-tree copy of that file; if the current edits on that path matter, diff first:
git diff stash@{0} -- app.conf
verify: git diff stash@{0} -- app.conf → stashed vs working-tree, before anything overwrites
Stashes are stored as real commits on a reflog-like stack, which is why stash@{0} accepts any commit-ish syntax — and why a stash dropped by mistake is recoverable from the reflog until garbage collection eats it.
Dropped a stash by mistake?
The commit is still in the object database: git fsck --unreachable | grep commit finds the dangling sha, and git stash store -m "rescued" <sha> re-attaches it to the stash stack. Works until garbage collection runs — same clock as every other reflog rescue.
When is a stash the wrong tool?
A stash is a scratchpad, not a branch: no name in git branch, no review step, no diff shown by default, silently stacking entries you will forget. If the work-in-progress needs to survive context switches between machines or days, commit it to a branch instead — history attached beats a nameless stack. When those parked commits later need to land somewhere else, cherry-pick multiple commits lifts them across. And when the goal is undoing rather than parking, the decision tree in git undo last commit applies instead. The git-undo hub collects the whole cluster.
Every failure mode below comes from the documented behaviour — nothing invented:
| Symptom | Cause | Fix |
|---|---|---|
| New file missing after stash | untracked paths are skipped | git stash push -u -- <path> |
error: Your local changes ... would be overwritten on pop |
target path changed since the stash | commit or stash the new state first, then pop |
| Popped changes vanished | pop hit a conflict and aborted | resolve, then git stash apply |
| "Which stash was it?" | unnamed pile of WIP stashes |
-m messages, every time |
The pathspec form is eight years old (May 2017) and most git stash muscle memory predates it. Worth one re-learn: the whole-tree sweep is the special case now, not the default.