GitStashing Untracked Files

Stashing Untracked Files

The single most common stash bug report is "my new file followed me to another branch." The reason is simple: by default, git stash does not include untracked files. If Git has never seen the file before, it stays in place when you stash. This page covers how to include untracked (and ignored) files, and when to use a WIP commit instead.

The default behaviour

What stash sees and ignores

Bash
echo "// new" > src/feature.js
echo "// edited" >> src/existing.js

git status
# Changes not staged for commit:
#   modified:   src/existing.js
# Untracked files:
#   src/feature.js

git stash
# Saved working directory and index state WIP on main: ...

git status
# Untracked files:
#   src/feature.js

Only the modification to existing.js was stashed. feature.js is still sitting there, untracked. Switch branches now and it will appear to "follow" you — but really it's not following anything; it's in the working directory and Git just hasn't been told what to do with it.

Including untracked files: -u

The fix

Bash
git stash push -u -m "feature + edits"
# or  --include-untracked

git status
# nothing to commit, working tree clean
  • -u adds untracked files (those not in .gitignore).

  • It still respects your gitignore — ignored files are not stashed.

  • On restore, the untracked files reappear as untracked, not staged.

Including ignored files: -a

The much rarer flag

Bash
git stash push -a -m "include build artifacts too"
# or  --all
Warning
`-a` stashes *everything*, including files matched by `.gitignore` — that often means `node_modules/`, `dist/`, `.next/`, build caches, secrets in `.env` files, and more. Stashes balloon in size, and you risk capturing files you explicitly chose to ignore. Use `-a` only when you have a specific reason.
Flag comparison

Flag

Tracked + modified

Staged

Untracked

Ignored

(none)

Yes

Yes

No

No

-u / --include-untracked

Yes

Yes

Yes

No

-a / --all

Yes

Yes

Yes

Yes

The classic mistake

Untracked file 'follows' a branch switch

Bash
# On feature/login
echo "// scratch" > src/scratch.js     # untracked

git stash
git switch main

ls src/
# scratch.js                            # still here

# If you commit on main now and push, scratch.js could leak.
# Worse: if it conflicts with a real file on another branch,
# you get a confusing checkout error.
Why Git does this
Git is conservative with files it doesn't know about. It won't silently swallow your `feature.js` into the stash and risk losing it if the stash gets dropped. The trade-off is that you have to opt in with `-u`.
Defensive habit: check before stashing

A one-second habit

Bash
git status            # look at the Untracked section
# If anything important is there, use -u
git stash push -u -m "..."
Tip
Make `git status` the second command of every stash. It takes less time than typing `-u`, and it stops the silent bug at the source.
When ignored files actually matter
  • A scratch file you put in an ignored directory and forgot about — -a is the only way to stash it short of un-ignoring.

  • You're reproducing a bug that depends on a generated artifact you can't regenerate quickly.

  • You're moving between machines via patch files and want byte-for-byte fidelity (in which case, prefer git format-patch over stash).

Most days, the answer is: don't stash ignored files. They're ignored for a reason — they're reproducible, large, or sensitive.

The better alternative for new files: a WIP commit

When you have lots of new files, branches beat stash

Bash
git switch -c wip/login-form
git add -A
git commit -m "WIP: login form scaffolding"

# Later, when ready:
git switch feature/login
git cherry-pick wip/login-form     # or rebase
# ...and  git branch -D wip/login-form  when done

Why prefer this for new files? Because:

  • A commit is reflog-protected for ~90 days; a stash for ~30.

  • A branch is pushable. You can back it up to the remote.

  • You can reference it by name forever — no fragile stash@{2} index.

  • You can rebase, cherry-pick, or PR it directly.

Real-world recipe: catching the "follows me" bug

The whole flow

Bash
# 1. Always status first
git status
# Untracked files:
#   src/new-thing.js

# 2. Decide: stash with -u, OR commit on a WIP branch
git stash push -u -m "WIP including new-thing.js"

# 3. Move freely
git switch main
git pull

# 4. Come back and restore — untracked file returns as untracked
git switch -
git stash pop
Interaction with .gitignore

git stash -u honours .gitignore exactly as the rest of Git does. If you keep your .gitignore clean, -u is almost always safe to use. If your .gitignore is sloppy, you'll sometimes accidentally stash files you didn't mean to — another argument for keeping your ignore rules tidy.

Mental model: -u for "everything I'm working on"
Default stash is for "just what Git already tracks." `-u` is for "everything I'm actively working on, tracked or not." `-a` is for "literally every byte in the working directory." Pick the smallest flag that captures your intent.