What is Git stash and when should you use it (vs. WIP commits, worktrees, autostash)

· Category: Git

Short answer

git stash snapshots your uncommitted working-tree and index changes onto a hidden stack, then resets the working tree to HEAD. It's a fast escape hatch when you need a clean tree right now (to switch branches, pull, or rebase) but aren't ready to commit.

git stash push -m "WIP: login form validation"
# or, since Git 2.13, just:
git stash -m "WIP: login form validation"

Restore with pop (which removes the entry from the stack on success) or apply (which keeps it):

git stash pop                  # restore the most recent and drop it
git stash apply stash@{2}      # restore a specific entry, keep the stack

Reference: git-stash(1).

What stash does and doesn't capture by default

By default git stash saves:

  • Modified tracked files (working tree)
  • Staged changes (index)

By default it does not save:

  • Untracked files (new files Git has never seen)
  • Ignored files (matched by .gitignore)

This is the #1 source of "where did my changes go?" reports — a brand new file you've been editing isn't tracked yet, so git stash leaves it sitting in the working tree. Use the right flag:

git stash -u   # also stash untracked files (--include-untracked)
git stash -a   # also stash ignored files  (--all). Use sparingly.

-a is rarely what you want — it pulls in node_modules/, .venv/, build artifacts, everything. Reach for -u 95% of the time.

Steps for the canonical workflow

# 1. You're mid-feature, urgent bug report comes in.
git status
#   modified:   src/login.js
#   modified:   src/api.js
#   ?? src/login.test.js                  <- untracked; will be missed without -u

# 2. Stash with a message and include untracked.
git stash push -u -m "WIP: login form validation"

# 3. Working tree is now clean — switch and fix the bug.
git switch main
git pull --ff-only origin main
git switch -c hotfix/csrf-token
# ... fix, commit, push, PR ...

# 4. Back to your feature branch.
git switch feature/user-login

# 5. Restore the stash. pop drops it from the stack on success;
#    if there's a conflict, the stash is kept so you can retry.
git stash pop

# 6. If pop conflicts, resolve and then drop manually.
git stash drop

Inspecting the stash stack

git stash list
# stash@{0}: On feature/user-login: WIP: login form validation
# stash@{1}: On main: experimental cache layer

git stash show -p stash@{0}        # show the diff
git stash show --stat stash@{0}    # files-changed summary

stash@{N} references are positional and shift when you push/drop. Treat them as ephemeral — never paste a stash@{3} into a script you might run tomorrow.

Partial and selective stashes

Two common variants beyond plain git stash:

Stash everything except what's already staged

git add src/login.js          # what you want to keep working on
git stash --keep-index        # stash everything else; staged work stays

This is the canonical way to "test only the staged subset" — useful before running a pre-commit hook or running tests on the exact thing you're about to commit.

Stash a hunk-by-hunk subset

git stash push -p -m "stash only the api.js refactor"

-p (--patch) walks you through each hunk and asks Stash this hunk [y,n,q,a,d,/,e,?]?. Useful when one file got too many changes and you only want to set part aside.

When stash is the wrong tool

Stash is convenient but lossy in two ways: messages are easy to misplace in the stack, and stashes don't get pushed to remotes — a wiped laptop loses every stash. For anything you might want to recover next week, use one of these instead:

A WIP commit

git add -A
git commit -m "WIP: do not push" --no-verify
# later, when ready to clean up:
git reset HEAD~1                   # undo the commit, keep the changes
# or, to amend it into the next real commit:
git commit --amend --no-edit

A WIP commit is a real commit on a real branch — git push works, git log shows it, you can rebase it. Most teams that have worked through "I lost three days to a corrupted stash" end up preferring this for anything beyond a few minutes' interruption.

git worktree for parallel branches

If you find yourself stashing every time the bug-bot pings you, you don't need stash — you need a second working tree:

git worktree add ../myrepo-hotfix main
cd ../myrepo-hotfix
git switch -c hotfix/csrf-token
# fix, commit, push. The original feature branch is untouched in the other directory.

git worktree list shows everything; git worktree remove ../myrepo-hotfix cleans up. See git-worktree(1).

rebase --autostash and pull --autostash

If you're stashing only because git rebase or git pull --rebase complained about uncommitted changes, the autostash option does it implicitly:

git pull --rebase --autostash
# or, set it once:
git config --global rebase.autoStash true
git config --global pull.rebase     true

After this, git pull on a dirty tree just works.

Common issues

git stash pop left a conflict and the stash is gone. It isn't — when pop hits a conflict it keeps the stash on the stack and exits non-zero. Run git stash list and you'll see stash@{0} is still there. Resolve the conflict, then git stash drop to remove the entry.

My untracked file disappeared after git stash. You used git stash without -u, then later cleaned the working tree with git clean -fd or similar. Untracked files that weren't stashed and were then cleaned are gone — Git never knew about them. Always use git stash -u when you have untracked changes that matter.

git stash apply says "could not restore untracked files from stash". Usually means a file with the same path now exists in the working tree, blocking restoration. Move or remove the conflicting path, then re-run git stash apply.

The stash is on the wrong branch. git stash records which branch you stashed from (visible in git stash list) but it's a label, not a constraint — you can apply on any branch. To explicitly move work between branches, switch first, then apply: git switch other-branch && git stash apply stash@{0}.

My stash stack is full of unlabeled WIP on main. You forgot to pass -m. Going forward, alias it: git config --global alias.stash-push '!f() { git stash push -u -m "$@"; }; f'. Old stashes can be inspected one at a time with git stash show -p stash@{N}; drop ones you don't need.

Tips

  • git stash branch newbranch stash@{2} creates a new branch from the commit the stash was based on and applies the stash on top — the cleanest way to "promote" an old stash to real branch when the original branch has moved on.
  • Stashes are stored as commits under refs/stash. They survive git gc only as long as the reflog entry survives — by default 90 days for unreachable objects (gc.reflogExpireUnreachable). Don't treat stash as long-term storage.
  • For separating commits you've already made (rather than uncommitted work), see how to amend a Git commit or how to cherry-pick a commit in Git.
  • If you stashed the wrong thing and need to recover after git stash drop, the stash commit is still in git fsck --unreachable for a while: git fsck --unreachable | grep commit and then git stash apply <sha>.