Git Stash: Complete Guide to Saving Work in Progress

Published February 12, 2026 · 22 min read

You are halfway through implementing a feature when a critical bug report comes in. Your working directory is full of uncommitted changes, and you need a clean slate to start the hotfix. This is exactly where git stash saves the day. It shelves your work-in-progress changes, gives you a clean working tree, and lets you come back to your work later as if you never left.

This guide covers every aspect of git stash: the basic workflow, advanced options like partial stashing and branch creation, how stash works internally, and the real-world workflows that make it indispensable for day-to-day development. If you’re specifically trying to undo file changes or unstage a file (undo git add), also see Git Restore.

Table of Contents

What Is Git Stash and When to Use It

Git stash takes your uncommitted changes — both staged (in the index) and unstaged (in the working directory) — saves them on a stack, and reverts your working directory to match the HEAD commit. Think of it as a clipboard for your in-progress work.

Common scenarios where stash is essential:

Stash is not a substitute for commits. It is a temporary holding area. If you stash something and forget about it for weeks, you are doing it wrong. Commit early and often; use stash for short-lived context switches.

Basic Stash Workflow

The simplest stash workflow is three commands: stash your changes, do something else, and pop them back.

# You're working on a feature and have uncommitted changes
$ git status
On branch feature/login
Changes not staged for commit:
  modified:   src/auth.js
  modified:   src/api.js

# Stash everything
$ git stash
Saved working directory and index state WIP on feature/login: a1b2c3d Add login form

# Working directory is now clean
$ git status
On branch feature/login
nothing to commit, working tree clean

# Switch to another branch, fix a bug, whatever you need
$ git checkout main
$ git checkout feature/login

# Bring your changes back
$ git stash pop
On branch feature/login
Changes not staged for commit:
  modified:   src/auth.js
  modified:   src/api.js
Dropped refs/stash@{0}

When you run git stash with no arguments, Git saves both staged and unstaged tracked changes. After popping, all changes come back as unstaged, regardless of whether they were staged before. To preserve the staging state, use the --index flag:

# Preserve staged vs unstaged distinction
$ git stash pop --index

git stash push with Messages

The bare git stash command generates a default message like WIP on branch: hash message. When you have multiple stashes, those messages are useless. Always add a descriptive message:

# Stash with a meaningful message
$ git stash push -m "login form validation - half done"
Saved working directory and index state On feature/login: login form validation - half done

# Another stash with context
$ git stash push -m "API rate limiting experiment"

The push subcommand is the modern form (Git 2.13+). The older git stash save "message" syntax still works but is deprecated. Always use push for new work.

# Deprecated (still works, but avoid)
$ git stash save "my message"

# Modern equivalent
$ git stash push -m "my message"

You can also stash only staged changes with --staged (Git 2.35+):

# Stash only what's in the index (staged)
$ git stash push --staged -m "staged changes only"

git stash list and Navigating Stashes

Stashes are stored on a stack (LIFO). Each stash gets a reference like stash@{0}, stash@{1}, etc. The most recent stash is always stash@{0}.

$ git stash list
stash@{0}: On feature/login: API rate limiting experiment
stash@{1}: On feature/login: login form validation - half done
stash@{2}: WIP on main: e5f6a7b Update README

You can reference any stash by its index when running pop, apply, show, or drop:

# Apply the second stash (index 1) without removing it
$ git stash apply stash@{1}

# Pop a specific stash
$ git stash pop stash@{2}

# Show what's in a specific stash
$ git stash show stash@{1}

The stash list uses the reflog format, so you can also use time-based references:

# Stashes from the last hour
$ git stash list --since="1 hour ago"

# Format stash list with date
$ git stash list --date=relative

git stash pop vs apply

This is one of the most common questions about stash. Both commands reapply stashed changes, but they differ in one critical way:

Command Applies Changes Removes Stash Entry On Conflict
git stash pop Yes Yes (if clean apply) Keeps the stash
git stash apply Yes No Keeps the stash

Use pop when you are done with the stash and want it removed from the stack. This is the most common case: you stashed to switch branches, now you are back and want your changes.

Use apply when you want to keep the stash around. This is useful when:

# Safe approach: apply first, drop manually after verifying
$ git stash apply
# ... verify everything looks good ...
$ git stash drop stash@{0}

git stash show: Viewing Stashed Changes

Before applying a stash, you often want to see what it contains. git stash show gives you a summary by default and a full diff with -p:

# Summary of changes (default: diffstat)
$ git stash show
 src/auth.js | 24 ++++++++++++++++--------
 src/api.js  |  8 ++++++--
 2 files changed, 22 insertions(+), 10 deletions(-)

# Full diff (patch format)
$ git stash show -p
diff --git a/src/auth.js b/src/auth.js
index 1a2b3c4..5d6e7f8 100644
--- a/src/auth.js
+++ b/src/auth.js
@@ -10,6 +10,12 @@ function validateLogin(email, password) {
+  if (!email.includes('@')) {
+    return { valid: false, error: 'Invalid email format' };
+  }
...

# Show a specific stash
$ git stash show -p stash@{2}

# Show only file names
$ git stash show --name-only stash@{0}
src/auth.js
src/api.js

# Show with stat and patch combined
$ git stash show --stat -p stash@{0}

You can also use any git diff flag with stash show, including --color-words for inline diffs or --name-status to see which files were added, modified, or deleted.

Stashing Specific Files

Sometimes you do not want to stash everything. Maybe you have changes in five files but only need to set aside two of them. Use git stash push with pathspecs:

# Stash only specific files
$ git stash push -m "auth changes" src/auth.js src/middleware/auth.js

# Stash everything in a directory
$ git stash push -m "all API changes" src/api/

# Stash using glob patterns
$ git stash push -m "test files" -- "*.test.js"

The files you specify are stashed and reverted. All other uncommitted changes remain in your working directory untouched. The -- separator before glob patterns prevents Git from misinterpreting them as flags.

This is one of the most useful but least known features of stash. Before pathspec support was added in Git 2.13, you had to manually stage files and use workarounds. Now it is straightforward.

# Verify: only specified files were stashed
$ git stash show --name-only
src/auth.js
src/middleware/auth.js

# Your other changes are still in the working directory
$ git status
Changes not staged for commit:
  modified:   src/dashboard.js
  modified:   src/utils.js

Stashing Untracked and Ignored Files

By default, git stash only saves tracked files (files that Git already knows about). New files that have never been added are ignored. This catches people off guard regularly.

# Default stash: ONLY tracked files
$ git stash
# New files are left behind!

# Include untracked files with -u (or --include-untracked)
$ git stash push -u -m "including new files"

# Include EVERYTHING: untracked AND ignored files with -a (or --all)
$ git stash push -a -m "full clean slate"

The -u flag is what you want most of the time. It picks up new files you have created but not yet added to Git. The -a flag also includes files matched by .gitignore (like node_modules/, build artifacts, and .env files). Use -a sparingly — stashing node_modules can create an enormous stash entry.

When to use each flag

Partial/Interactive Stash (-p)

The -p (patch) flag lets you interactively choose which hunks within files to stash. This is the most granular control you can get:

$ git stash push -p -m "just the validation logic"
diff --git a/src/auth.js b/src/auth.js
--- a/src/auth.js
+++ b/src/auth.js
@@ -10,6 +10,12 @@ function validateLogin(email, password) {
+  if (!email.includes('@')) {
+    return { valid: false, error: 'Invalid email' };
+  }
(1/3) Stash this hunk [y,n,q,a,d,j,J,g,/,e,?]?

The interactive prompt options:

Key Action
yStash this hunk
nSkip this hunk
qQuit (do not stash remaining hunks)
aStash this hunk and all remaining hunks in this file
dSkip this hunk and all remaining hunks in this file
sSplit this hunk into smaller hunks
eManually edit this hunk

Partial stash is powerful when you have interleaved changes in a single file — some ready for a commit and some that need to be set aside. Combined with descriptive messages, it lets you surgically separate concerns.

Creating Branches from Stash

Sometimes you stash changes, and by the time you come back, the branch has moved so far ahead that applying the stash causes conflicts. Or maybe you realize the stashed work deserves its own feature branch. git stash branch solves both cases:

# Create a new branch from the stash, starting from the commit
# where the stash was originally created
$ git stash branch feature/validation stash@{0}
Switched to a new branch 'feature/validation'
On branch feature/validation
Changes not staged for commit:
  modified:   src/auth.js
Dropped stash@{0}

This command does three things in one step:

  1. Creates a new branch starting from the commit where the stash was originally recorded
  2. Applies the stashed changes to the new branch
  3. Drops the stash entry (like pop)

Because the branch starts from the exact commit where you stashed, there is zero chance of conflicts. This is the safest way to recover stashed work that has become difficult to apply.

# If you don't specify a stash, it uses stash@{0}
$ git stash branch my-new-branch

# You can also specify any stash
$ git stash branch rescue-work stash@{3}

Stash Conflicts and Resolution

When you apply or pop a stash, Git performs a three-way merge between the stash, the commit where the stash was created, and your current HEAD. If the same lines changed in both the stash and the commits since, you get a conflict:

$ git stash pop
Auto-merging src/auth.js
CONFLICT (content): Merge conflict in src/auth.js
The stash entry is kept in case you need it again.

Notice the last line: when pop encounters conflicts, the stash is not dropped. This is a safety measure. You need to resolve the conflict manually.

# 1. Open the conflicted file and resolve the markers
<<<<<<< Updated upstream
  const token = generateJWT(user.id);
=======
  const token = createToken(user);
>>>>>>> Stashed changes

# 2. After resolving, stage the file
$ git add src/auth.js

# 3. The stash is still there - drop it manually
$ git stash drop stash@{0}

If the conflict is too messy and you want to abort, reset your working directory:

# Undo the stash apply and go back to clean state
$ git checkout -- .

# Or use git restore (Git 2.23+)
$ git restore .

# Your stash is still safe on the stack
$ git stash list

git stash drop and clear

Stashes accumulate over time. Clean them up to avoid confusion.

# Drop a specific stash
$ git stash drop stash@{0}
Dropped stash@{0} (a1b2c3d4e5f6...)

# Drop a stash by index (same thing, shorter)
$ git stash drop stash@{2}

# WARNING: Nuclear option - delete ALL stashes
$ git stash clear

git stash clear is irreversible. There is no confirmation prompt. All stash entries are permanently deleted. Before running it, list your stashes and make sure nothing important is there:

# Review before clearing
$ git stash list
stash@{0}: On main: temp experiment
stash@{1}: On feature/auth: validation WIP
stash@{2}: On main: debug logging

# If stash@{1} is important, apply or branch it first
$ git stash branch save-validation stash@{1}

# Now safe to clear the rest
$ git stash clear

Dropped stashes can sometimes be recovered using git fsck --unreachable if Git has not yet garbage-collected them, but do not rely on this. Treat drop and clear as permanent.

Advanced: Stash Internals

Understanding how stash works under the hood helps you debug edge cases and appreciate why certain flags exist.

A stash entry is not a special data structure — it is a regular commit object. Specifically, each stash creates two or three commit objects:

# Stash commit structure:
#
#   stash@{0} (merge commit W)
#   |-- Parent 1: HEAD commit at time of stash
#   |-- Parent 2: Index state (commit I)
#   +-- Parent 3: Untracked files (commit U, only with -u or -a)
#
# W = Working directory state
# I = Index (staged) state
# U = Untracked files

You can inspect these directly:

# Show the stash commit
$ git log --oneline --graph stash@{0}

# The stash ref is stored in .git/refs/stash
$ cat .git/refs/stash

# The full stash reflog
$ git reflog show stash

# Treat stash as a commit - diff against anything
$ git diff stash@{0} HEAD
$ git diff stash@{0}^1 stash@{0}  # Changes in the stash

Because stashes are commits, you can cherry-pick them, reference them in diffs, and even push them to a remote (though you should not). The --index flag on apply/pop works because Git stores the index state in a separate parent commit, allowing it to reconstruct which changes were staged.

Real-World Workflows

Quick Context Switching

The most common workflow. You are on a feature branch and need to fix something on main:

# Stash current work
$ git stash push -u -m "feature/payments: stripe integration WIP"

# Switch and fix
$ git checkout main
$ git pull
# ... make the fix, commit, push ...

# Come back
$ git checkout feature/payments
$ git stash pop

Code Review Preparation

Before reviewing a colleague's pull request, clean up your working directory so you do not accidentally include your changes in test runs:

# Stash everything including new files
$ git stash push -u -m "my WIP before reviewing PR #42"

# Check out the PR branch
$ git fetch origin
$ git checkout origin/feature/new-dashboard

# ... review, test, comment ...

# Go back to your branch and restore
$ git checkout feature/my-work
$ git stash pop

Experimental Changes

Try a risky refactor without committing to it:

# Make your experimental changes...

# Save the experiment
$ git stash push -m "experiment: replace axios with fetch"

# Try a different approach...

# Save that too
$ git stash push -m "experiment: replace axios with ky"

# Compare both experiments
$ git stash show -p stash@{0}  # ky approach
$ git stash show -p stash@{1}  # fetch approach

# Keep the one you prefer
$ git stash pop stash@{0}

# Clean up the other
$ git stash drop stash@{0}  # was stash@{1}, shifted after pop

Pulling with a Dirty Working Tree

You cannot pull if you have local changes that would conflict with incoming changes. Stash provides a clean workaround:

# This pattern is so common it's worth memorizing
$ git stash push -u -m "before pull"
$ git pull --rebase
$ git stash pop

If conflicts arise during the stash pop, resolve them as described in the conflicts section.

Common Pitfalls and Best Practices

Pitfall: Forgetting untracked files

The most common stash mistake. You create new files, run git stash, and the new files are still there. Your "clean" working directory is not actually clean.

# Wrong: new files left behind
$ git stash

# Right: include untracked files
$ git stash push -u

Pitfall: Stash stack confusion

When you pop or drop a stash, all entries above it shift down. stash@{2} becomes stash@{1}, and so on. If you are dropping multiple stashes, work from the highest index down:

# Correct order: drop from highest to lowest
$ git stash drop stash@{3}
$ git stash drop stash@{2}

# Wrong: indices shift after each drop
$ git stash drop stash@{1}  # drops what was originally stash@{1}
$ git stash drop stash@{2}  # NOT what you think - indices shifted!

Pitfall: Long-lived stashes

Stashes that sit for days or weeks become increasingly difficult to apply. The codebase moves on, and conflicts pile up. If work is worth keeping for more than a day, commit it on a branch instead.

Pitfall: Losing the staging distinction

By default, git stash pop restores everything as unstaged, even if some changes were staged before stashing. Use --index to preserve the original staging state:

$ git stash pop --index

Best Practices

FAQ

What does git stash do and when should I use it?

Git stash temporarily saves your uncommitted changes (both staged and unstaged) and reverts your working directory to the last commit. Use it when you need to switch branches quickly, pull remote changes on a dirty working tree, pause current work to fix an urgent bug, or set aside experimental changes you are not ready to commit. Your stashed changes are stored on a stack and can be reapplied later with git stash pop or git stash apply.

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

Both commands reapply stashed changes to your working directory, but they differ in what happens to the stash entry afterward. git stash pop applies the changes and then removes the stash entry from the stack (if no conflicts occur). git stash apply reapplies the changes but keeps the stash entry intact, so you can apply the same stash to multiple branches or keep it as a backup. If pop encounters a merge conflict, the stash is not dropped, behaving like apply.

How do I stash only specific files in Git?

Use git stash push followed by the file paths: git stash push -m "description" path/to/file1 path/to/file2. This stashes only the specified files while leaving all other changes in your working directory. You can also use git stash push -p to interactively select individual hunks within files to stash. Both approaches give you fine-grained control over exactly which changes get stashed.

Continue Learning

This guide is part of our Git deep-dive series. Explore these related guides to master your Git workflow:

Related Tools

Git Diff Viewer
Visualize and compare diffs in the browser
JSON Formatter
Format and validate JSON data instantly
Git Cheat Sheet
One-page quick reference for all Git commands