Skip to content
Home DevOps Git Stash: Untracked Files Follow Branches – CI Failures

Git Stash: Untracked Files Follow Branches – CI Failures

Where developers are forged. · Structured learning · Free forever.
📍 Part of: Git → Topic 15 of 19
git stash ignores untracked files.
🧑‍💻 Beginner-friendly — no prior DevOps experience needed
In this tutorial, you'll learn
git stash ignores untracked files.
  • git stash push -m 'description' — always add a message. Unnamed stashes become mystery boxes within 24 hours.
  • Use git stash -u to include untracked files; without it, new files stay in your working directory when you switch branches.
  • Prefer git stash apply over pop when conflicts are possible — pop deletes the stash before you resolve conflicts, leaving you with no backup.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer
  • git stash push -m 'description': saves tracked changes with a label
  • git stash -u: includes untracked files (new files not yet git add'd)
  • git stash pop: restores and removes from stack — use when confident no conflicts
  • git stash apply: restores but keeps in stack — use when conflicts are possible
🚨 START HERE

Git Stash Triage Cheat Sheet

Fast recovery for stash-related failures, lost changes, and wrong-branch applications.
🟡

Stash pop hit a merge conflict — stash is gone from the stack

Immediate ActionFind the stash commit in reflog before it expires (30 days).
Commands
git reflog | grep stash (find the stash application entry)
git fsck --no-reflogs | grep dangling (find dangling commit objects)
Fix Nowgit stash apply <hash> to recover. Prevention: use apply instead of pop when conflicts are possible.
🟡

Stashed changes on wrong branch — working directory has wrong files

Immediate ActionDo not commit. Reset the branch to clean state.
Commands
git reset --hard HEAD (undo the stash application)
git checkout <correct-branch> (switch to where you need the stash)
Fix Nowgit stash apply stash@{0} on the correct branch. If stash is gone: recover via reflog.
🟡

Cannot find which stash has my changes

Immediate ActionSearch stash diffs for the specific change.
Commands
git stash list (read messages — this is why you always use -m)
git stash show -p stash@{N} | grep 'search-term' (search each stash diff)
Fix NowAlways label stashes: git stash push -m 'description'. Unnamed stashes are mystery boxes.
🟡

Untracked files ended up on the wrong branch after stash + switch

Immediate ActionThe files were never stashed. Revert the commit that included them.
Commands
git log --oneline -3 (find the commit with the wrong files)
git revert <commit-hash> (undo the commit on the wrong branch)
Fix NowPrevention: always use git stash -u when stashing with untracked files.
🟡

git stash clear ran accidentally — all stashes gone

Immediate ActionFind dangling commit objects in the object store.
Commands
git fsck --no-reflogs | grep dangling (find lost stash commits)
git stash apply <hash> (recover each stash commit)
Fix NowPrevention: never run git stash clear without checking git stash list first.
Production Incident

Stash Pop on Wrong Branch: Untracked Files Committed to Main

A developer stashed untracked files without -u, switched to main for a hotfix, and the untracked files were silently committed to main. The resulting main build included half-finished feature code that broke the CI pipeline.
SymptomThe CI pipeline on main failed after a hotfix merge. The build included a new file — PaymentRetryService.java — that was not part of the hotfix. The file contained incomplete feature code with compilation errors. No developer had intentionally committed this file to main.
AssumptionThe developer assumed that git stash would save everything in their working directory. They did not realize that untracked files (files not yet git add'd) are excluded from stash by default. When they switched to main, the untracked PaymentRetryService.java remained in the working directory. During the hotfix, they ran git add . which staged the orphaned file. It was committed to main with the hotfix.
Root cause1. Developer was working on feature/payment-retry and created PaymentRetryService.java but had not run git add on it yet. 2. They ran git stash to save their work — but stash only saves tracked files by default. 3. PaymentRetryService.java was untracked, so it was NOT stashed. It remained in the working directory. 4. Developer switched to main: git checkout main. 5. PaymentRetryService.java (the untracked file) followed them to main. 6. During the hotfix, they ran git add . which staged everything — including PaymentRetryService.java. 7. The file was committed to main with incomplete, non-compiling feature code. 8. CI built from main and failed on the compilation error.
Fix1. Immediate: revert the offending commit on main: git revert <hotfix-commit-hash>. 2. The developer switched back to feature/payment-retry and stashed correctly: git stash push -u -m 'WIP: PaymentRetryService'. 3. Team rule: always use git stash push -u when stashing with untracked files. 4. Team rule: never run git add . on main — always add files individually or use git add -p. 5. Added pre-commit hook to warn when new files are staged on main that do not exist on the previous commit.
Key Lesson
git stash does not include untracked files by default. Use git stash -u to include them.Untracked files follow you across branch switches. They are not part of the stash or the commit — they are just files in the working directory.Never run git add . on main. Stage files individually or use git add -p to review each hunk.Always label stashes with git stash push -m 'description'. Unnamed stashes become mystery boxes.
Production Debug Guide

Systematic recovery paths for lost stashes, wrong-branch pops, and stash conflicts.

Ran git stash pop and hit a merge conflict — stash is gone from the stack1. git stash pop deletes the stash even on conflict. The stash is no longer in git stash list. 2. Check reflog: git reflog — find the stash application entry. 3. The stash commit object still exists: git fsck --no-reflogs | grep dangling — find dangling commit objects. 4. Recover: git stash apply <hash> using a hash that looks like your stash. 5. Prevention: use git stash apply when conflicts are possible. Then git stash drop manually after resolving.
Stashed changes applied to the wrong branch — working directory is now wrong1. Do NOT commit. The changes are staged/unstaged but not committed. 2. Undo: git reset --hard HEAD to restore the branch to its pre-pop state. 3. Switch to the correct branch: git checkout <correct-branch>. 4. Apply there: git stash apply stash@{0} (the stash is still in the stack if you used apply, or recover via reflog if you used pop).
Cannot find which stash has the changes I need1. List all stashes: git stash list — read the messages (this is why you always use -m). 2. If messages are unhelpful: git stash show -p stash@{0} — full diff of each stash. 3. Search stashes for a specific change: git stash show -p stash@{N} | grep 'search-term' for each N. 4. Or: git log --all --oneline --grep='search-term' — stash entries appear in the log.
Untracked files committed to the wrong branch after stash + switch1. The files were never stashed — they are untracked and followed you across branches. 2. Check: git log --oneline -3 — find the commit that includes the wrong files. 3. Revert: git revert <commit-hash> to undo the commit on the wrong branch. 4. Switch to correct branch: git checkout <correct-branch>. 5. Stash correctly: git stash push -u -m 'WIP: new files' to include untracked files.
git stash clear ran accidentally — all stashes are gone1. git stash clear removes all stashes with no confirmation. 2. Recovery: git fsck --no-reflogs | grep dangling — find dangling commit objects. 3. Each stash entry is a commit internally. Filter by date: look for commits created around the time you stashed. 4. Apply: git stash apply <hash> for each recovered stash commit. 5. Prevention: never run git stash clear without checking git stash list first.

git stash shelves uncommitted working directory and index changes into a stack. It is the standard tool for context switching — you stash your current work, switch to a hotfix branch, then unstash when you return. The core failure mode: stash pop deletes the stash even on merge conflicts, leaving you with no backup. Use apply + manual drop when conflicts are possible.

The second failure mode: stash does not include untracked files by default. New files you have not git add'd remain in the working directory when you switch branches. They get committed to the wrong branch. Use stash -u to include them.

Stashes are global to the repository, not scoped to a branch. You can pop a stash made on feature/x onto main. This is sometimes intentional and sometimes a very bad day. Always use stash push -m 'description' to label your stashes — unnamed stashes become mystery boxes within 24 hours.

Basic Stash: Save and Restore Work

The core workflow is three commands. git stash shelves everything in your working directory and index that differs from HEAD. git stash pop restores the most recent stash and removes it from the stack. git stash apply restores without removing — useful when you want to apply the same stash to multiple branches.

One thing that surprises developers coming from other VCS systems: git stash by default only stashes tracked files. If you created a new file that hasn't been git add-ed yet, it won't be stashed. It'll sit there in your working directory when you switch branches, which can lead to unintended commits on the wrong branch. Use git stash -u (or --include-untracked) to include new files.

The stash is a stack — each git stash pushes a new entry. git stash pop pops the top (most recent). This is correct behaviour for the common case but becomes confusion when you stash, do some more work, stash again, and then try to figure out which stash has what.

io/thecodeforge/git/StashBasics.sh · BASH
12345678910111213141516171819202122232425262728293031323334353637383940
# io.thecodeforge — Git Stash Basics

# ─────────────────────────────────────────────────────────────
# Save current work to stash (tracked files only)
# ─────────────────────────────────────────────────────────────
git stash

# Save with a descriptive message — always do this
git stash push -m "WIP: payment retry exponential backoff"

# Include untracked files (new files not yet git add'd)
git stash push -u -m "WIP: new PaymentRetryService class"

# ─────────────────────────────────────────────────────────────
# Restore most recent stash AND remove it from the stack
# ─────────────────────────────────────────────────────────────
git stash pop

# Restore most recent stash but KEEP it in the stack
git stash apply

# ─────────────────────────────────────────────────────────────
# List all stashes
# ─────────────────────────────────────────────────────────────
git stash list
# Output:
# stash@{0}: On feature/payment-retry: WIP: payment retry exponential backoff
# stash@{1}: On main: WIP: hotfix debug logging

# ─────────────────────────────────────────────────────────────
# Restore a specific stash by index
# ─────────────────────────────────────────────────────────────
git stash pop stash@{1}
git stash apply stash@{1}

# ─────────────────────────────────────────────────────────────
# See what's in a stash before applying
# ─────────────────────────────────────────────────────────────
git stash show stash@{0}
git stash show -p stash@{0}   # Full diff
▶ Output
Saved working directory and index state On feature/payment-retry: WIP: payment retry exponential backoff

# git stash list:
stash@{0}: On feature/payment-retry: WIP: payment retry exponential backoff
stash@{1}: On main: WIP: hotfix debug logging

# git stash show stash@{0}:
src/main/java/io/thecodeforge/payment/PaymentRetryService.java | 47 +++++++++
1 file changed, 47 insertions(+)
Mental Model
Stash Is a Stack, Not a Branch
Stash = stack. Not scoped to branches. Pop anywhere.
  • Stash is a stack — push adds, pop removes the top
  • Stashes are global — not attached to any branch
  • You can pop a stash made on feature/x onto main (or any branch)
  • This flexibility is powerful but dangerous — always check which branch you are on before popping
📊 Production Insight
The biggest production failure with basic stash: developers stash without -u, then run git clean on the wrong branch thinking it will remove build artifacts. git clean removes all untracked files — including the untracked files that were not stashed. The work is gone. There is no reflog for untracked files. The fix: always use stash -u when you have new files. And never run git clean -f without first running git clean -n (dry run) to see what will be deleted.
🎯 Key Takeaway
git stash saves tracked changes by default. Use -u to include untracked files. Always label stashes with push -m 'description'. Stashes are global to the repo — not scoped to any branch.

Stash Pop vs Apply: When to Use Each

The difference matters more than most tutorials let on.

git stash pop = apply + delete from stack. Use this 95% of the time. The moment you successfully restore your work, the stash entry is gone. Clean, no accumulation.

git stash apply = apply but leave in stack. Use this when: you want to apply the same changes to multiple branches (e.g. a config change you need on both a feature branch and a hotfix branch), or when you're not 100% sure the stash applies cleanly and you want to keep it as a backup while you verify.

There's one critical behaviour difference: if git stash pop encounters a conflict, it still removes the stash from the stack before throwing the conflict. You're now in a conflicted state with the stash gone. This is a Git footgun — you want git stash apply when there's any chance of conflicts, so the stash survives even if the apply fails.

io/thecodeforge/git/StashPopVsApply.sh · BASH
123456789101112131415161718192021222324252627282930
# io.thecodeforge — Stash Pop vs Apply

# ─────────────────────────────────────────────────────────────
# Scenario: you might have conflicts — use apply, not pop
# ─────────────────────────────────────────────────────────────
git stash apply stash@{0}

# If it applied cleanly with no conflicts, manually drop the stash
git stash drop stash@{0}

# If there ARE conflicts, resolve them, then drop manually
# The stash is still in the stack — you haven't lost anything
git status                # Shows conflicted files
# ... resolve conflicts ...
git add .
git stash drop stash@{0}  # Now safe to remove

# ─────────────────────────────────────────────────────────────
# Apply same stash to two branches
# ─────────────────────────────────────────────────────────────
git checkout feature/branch-a
git stash apply stash@{0}    # Applied to branch A — stash still exists
git checkout feature/branch-b
git stash apply stash@{0}    # Applied to branch B too
git stash drop stash@{0}     # Now clean up

# ─────────────────────────────────────────────────────────────
# Clear ALL stashes (nuclear — use with care)
# ─────────────────────────────────────────────────────────────
git stash clear
▶ Output
# Conflict during apply:
Auto-merging src/main/java/io/thecodeforge/payment/PaymentService.java
CONFLICT (content): Merge conflict in src/main/java/io/thecodeforge/payment/PaymentService.java

# Stash is still in stack — safe to resolve then drop
⚠ git stash pop deletes the stash even on conflict
If you run git stash pop and hit a merge conflict, Git removes the stash entry before you resolve the conflict. Your stash is gone from the stack. If you then abort the merge or mess up the conflict resolution, you have no backup. For anything involving cross-branch work or files you've touched recently on main, always use git stash apply so the stash survives a bad merge.
📊 Production Insight
The pop-vs-apply decision is about risk tolerance. pop is faster (one command) but has a catastrophic failure mode: if the apply fails, the stash is already deleted from the stack. You can recover via reflog (stash entries are commits internally), but most developers do not know this. The production rule: use apply when you are stashing across branches, when the target branch has changed significantly since you stashed, or when you are not 100% sure the stash applies cleanly. Use pop only when you are confident the apply will succeed.
🎯 Key Takeaway
pop = apply + delete. apply = apply + keep. Use apply when conflicts are possible — the stash survives a bad merge. Use pop only when confident the apply will succeed. Always apply, verify, then drop manually for safety.

Stashing Partial Changes and Specific Files

Sometimes you only want to stash part of your changes — for example, you've been working on two unrelated things in the same branch (happens to everyone, don't judge) and you want to commit one thing cleanly before stashing the other.

git stash push -- <path> stashes only the specified files. Everything else stays in your working directory.

git stash push -p (patch mode) drops you into an interactive hunk-by-hunk selection — exactly like git add -p. You choose yes/no for each chunk. This is powerful but slow for large changesets.

io/thecodeforge/git/StashPartial.sh · BASH
1234567891011121314151617181920212223
# io.thecodeforge — Stashing Partial Changes and Specific Files

# ─────────────────────────────────────────────────────────────
# Stash only specific files
# ─────────────────────────────────────────────────────────────
git stash push -m "WIP: retry config" -- src/main/java/io/thecodeforge/payment/RetryConfig.java

# Stash multiple specific files
git stash push -m "WIP: two related files" \
  -- src/main/java/io/thecodeforge/payment/RetryConfig.java \
  -- src/main/java/io/thecodeforge/payment/RetryPolicy.java

# ─────────────────────────────────────────────────────────────
# Interactive patch mode — choose which hunks to stash
# ─────────────────────────────────────────────────────────────
git stash push -p -m "WIP: partial changes"
# Git shows each change hunk and asks: stash this hunk? [y/n/q/a/d/?]

# ─────────────────────────────────────────────────────────────
# Create a branch from a stash (useful for long-lived stashes)
# ─────────────────────────────────────────────────────────────
git stash branch feature/retry-config stash@{0}
# Creates new branch at the commit where you stashed, applies the stash, drops it
▶ Output
Saved working directory and index state On feature/payment-retry: WIP: retry config

# Only RetryConfig.java was stashed
# RetryPolicy.java remains in working directory
Mental Model
git stash branch: Convert a Stash into a Branch
Stash > 3 days old? Convert it to a branch.
  • git stash branch <name> <stash>: creates branch at stash point, applies stash, drops it
  • Use when a stash has been sitting for days and has become important work
  • Branches are visible in git branch -a. Stashes are only visible in git stash list.
  • If a stash survives for weeks, it should have been a branch from the start.
📊 Production Insight
Partial stashing is the fix for the 'I committed two unrelated changes in one commit' problem — but in reverse. Instead of committing everything and trying to split later, you stash the changes you do not want to commit right now, commit the rest cleanly, then unstash. This produces cleaner commits than interactive rebase for the common case of 'I was working on feature X and accidentally started working on feature Y in the same branch'.
🎯 Key Takeaway
git stash push -- <path> stashes only specific files. git stash push -p gives hunk-by-hunk control. git stash branch converts a long-lived stash into a proper branch. Use partial stashing to separate unrelated changes before committing.
🗂 Git Stash Commands Compared
Choose based on what you need: save, restore, inspect, or clean up.
CommandEffectStash Removed?Best For
git stashStash tracked filesN/AQuick context switch
git stash -uStash tracked + untrackedN/AWhen you have new files
git stash popApply + remove from stackYesNormal restore — 95% of cases
git stash applyApply, keep in stackNoRisky merges or multi-branch apply
git stash dropRemove without applyingYesDiscard stash you no longer need
git stash clearRemove ALL stashesYes (all)Spring cleaning — careful
git stash branch <name>Create branch from stashYesLong-lived stashes that deserve a branch

🎯 Key Takeaways

  • git stash push -m 'description' — always add a message. Unnamed stashes become mystery boxes within 24 hours.
  • Use git stash -u to include untracked files; without it, new files stay in your working directory when you switch branches.
  • Prefer git stash apply over pop when conflicts are possible — pop deletes the stash before you resolve conflicts, leaving you with no backup.
  • git stash branch <name> <stash> converts an old stash into a proper branch — the right move when a stash has grown into more work than you expected.
  • Stashes are global to the repo, not scoped to a branch — applying a stash on the wrong branch is an easy mistake with sometimes messy consequences.
  • Stashes are commits internally — recoverable via git fsck --no-reflogs | grep dangling even after deletion. But do not rely on this as a primary safety net.

⚠ Common Mistakes to Avoid

    Running git stash without -u and losing new (untracked) files — they stay in the working directory when you switch branches and get committed to the wrong place.
    Accumulating a deep stash stack with no messages — stash@{7} with no description is useless. Always use git stash push -m 'description'.
    Using git stash pop when conflicts are possible — pop deletes the stash before the conflict is resolved. Use apply + manual drop instead.
    Forgetting that git stash is per-repository, not per-branch — stashes don't belong to branches. You can pop a stash made on feature/x onto main, which is sometimes intentional and sometimes a very bad day.
    Running git stash clear thinking it only clears 'old' stashes — it removes everything with no confirmation and no recovery except reflog.
    Running git clean -f after stashing without -u — git clean removes all untracked files, including the ones that were not stashed. The work is permanently lost with no reflog.
    Stashing and forgetting — stashes do not appear in git branch or git log. If you do not run git stash list regularly, stashes become invisible and eventually garbage-collected after 90 days.

Interview Questions on This Topic

  • QWhen would you use git stash apply instead of git stash pop?
  • QA developer stashed changes, switched branches, and now can't find which stash contains their work. How do they find and apply the right one?
  • QExplain how git stash stores its data internally. Is a stash a commit?
  • QYou have three stashes and want to apply the second one without disturbing the others. What command do you run?
  • QA developer ran git stash pop and hit a merge conflict. The stash is gone from git stash list. How do they recover it?
  • QA developer stashed without -u, switched to main, and untracked files were committed to main. Explain what happened and how to fix it.

Frequently Asked Questions

What is the difference between git stash pop and git stash apply?

git stash pop applies the stash and immediately removes it from the stash stack. git stash apply applies it but leaves it in the stack. Use apply when there's a chance of merge conflicts (so the stash survives if the apply fails) or when you want to apply the same stash to multiple branches.

Does git stash save untracked files?

No, by default git stash only saves tracked files that have been modified. To include untracked files (new files not yet added with git add), use git stash push -u or git stash push --include-untracked.

How do I see what's in a stash before applying it?

Run git stash show stash@{N} for a summary of changed files, or git stash show -p stash@{N} for the full diff. Replace N with the stash index from git stash list.

Can I recover a stash I accidentally dropped?

Sometimes. Run git fsck --no-reflogs | grep commit to find dangling commit objects (stash entries are commits internally). You can then run git stash apply <hash> on any hash that looks like your lost stash. This works within the gc.reflogExpire window (default 30 days) as long as you haven't run git gc.

How do I recover a stash after git stash pop hit a merge conflict?

git stash pop deletes the stash from the stack even on conflict. The stash commit still exists in the object store. Run git fsck --no-reflogs | grep dangling to find dangling commit objects. Apply with git stash apply <hash>. Prevention: use git stash apply instead of pop when conflicts are possible.

What happens to stashes after 90 days?

Stashes are garbage-collected after gc.reflogExpire (default 90 days). Once collected, they are gone permanently. If you need changes longer than a few days, convert the stash to a branch with git stash branch <name> <stash>.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousGit Squash Commits: Combine Multiple Commits into OneNext →Git Cherry Pick: Apply Commits Across Branches
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged