DEV Community

Cover image for git stash a Single File Without Losing the Rest
Mr Say Nothing
Mr Say Nothing

Posted on Originally published at mrsaynothing.dev

git stash a Single File Without Losing the Rest

Originally published at mrsaynothing.dev.

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.

TL;DR: git stash push -m "why" -- <path> stashes exactly the named paths and leaves the rest of your dirty tree alone. Bring the changes back with git stash pop (or pick one file out of any stash with git restore --source stash@{0} -- <path>). The pathspec form shipped in git 2.13, released May 2017 — any toolchain from the last eight years has it.

git stash push -m "app.conf prod tweak" -- app.conf
git stash list
# stash@{0}: On main: app.conf prod tweak
git stash pop
Enter fullscreen mode Exit fullscreen mode

Stash the file, not the tree.

How do you stash a single file in git?

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
Enter fullscreen mode Exit fullscreen mode

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. 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
Enter fullscreen mode Exit fullscreen mode

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 and surgical staging — 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 get a stashed single file back?

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

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, git cherry-pick lifts them across. And when the goal is undoing rather than parking, the decision tree in git undo last commit applies.

The honest ledger of failure modes, all from the documented behaviour:

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.


More field notes at mrsaynothing.dev · code at GitHub · say hi: contact@mrsaynothing.dev

This ships daily at mrsaynothing.dev — the full archive, every piece in 21 languages, zero missed days. New posts land in the newsletter the moment they ship: join it here. Code at GitHub.

Top comments (0)