git stash a Single File Without Losing the Rest

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.

HUMANby @mrsaynothing35d6 min read

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
three files dirty, one must move the review-can't-wait problem one whole file stash push -m msg -- path half a file git stash -p one file back later restore --source stash@{0} -- the stash is a scratchpad, not a branch. work that must survive days or machines → commit to a branch when it's the wrong tool the failure-mode table
fig 1 — park one file, split one file, retrieve one file — or branch instead. Click a branch for the section.

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 as git checkout -- <path> and it is not decorative: git stash push -- main.py and git stash push main.py differ 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 plain push skips it. git stash push -u -- newfile.py includes it; -a goes 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:

all untracked pop naming
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.