What is Git Stash?
Git stash is a powerful mechanism that allows developers to temporarily set aside their current uncommitted changes—both staged and unstaged modifications—without committing them. Think of it as a clipboard for your working directory. When you run git stash, Git records the current state of your working directory and index (staging area), then resets both to match the HEAD commit, giving you a clean slate to switch contexts, pull updates, or tackle an urgent bug fix.
Under the hood, a stash is actually a commit object that is not referenced by any branch. Git stores it in a special stack-like structure, where the most recent stash is always referenced by the pseudonym stash@{0}. Each stash entry contains metadata including the commit hash of the HEAD at the time the stash was created, a commit hash for the staged changes, and a commit hash for the untracked changes (if requested). This three-commit structure allows Git to reconstruct your working state with remarkable precision.
Why Git Stash Matters
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →In real-world development workflows, interruptions are the norm rather than the exception. Imagine you are deep into implementing a feature, with files modified across several modules, some changes staged for commit and others still messy in the working tree. Suddenly, a critical production bug is reported and you need to switch to the main branch immediately to deploy a hotfix. Committing half-baked code would pollute the commit history and potentially break the build. Discarding the work would waste potentially hours of effort. This is exactly where git stash shines.
Stashing enables seamless context switching without the overhead of creating throwaway commits. It keeps your commit history clean and meaningful, while preserving every line of code you have written. It also serves as a safety net when pulling remote changes that conflict with your local modifications, or when you want to test a colleague's branch quickly without merging unfinished work. Mastering stash operations transforms you from a developer who fights their version control system into one who collaborates fluidly with it.
Basic Stash Operations
Creating a Stash
The most fundamental operation is simply saving your current work to the stash stack. By default, git stash saves only tracked modified files and staged changes. Untracked files and files listed in .gitignore are left behind. Here is the basic command:
git stash
Or equivalently, using the explicit save subcommand:
git stash save
To include untracked files in the stash (new files that have not yet been added to the index), use the -u or --include-untracked flag:
git stash -u
git stash --include-untracked
To stash absolutely everything, including ignored files, use the -a or --all flag. This captures even those files that are explicitly excluded by .gitignore:
git stash -a
git stash --all
Adding a Descriptive Message
A stash without a message becomes a cryptic entry that is hard to identify later. Always provide a meaningful description using the push command with the -m flag:
git stash push -m "WIP: refactor authentication module, half-done token validation"
Note that git stash push is the modern, recommended syntax that replaces the older git stash save command. The push subcommand supports all the same options and is more explicit about what is happening.
Listing Stashes
To see all stashed entries in the stack, along with their branch context and messages:
git stash list
Example output:
stash@{0}: WIP on feature-login: 3a4b5c6 refactor authentication module, half-done token validation
stash@{1}: On main: quick backup before switching to hotfix branch
stash@{2}: WIP on dev: e7f8g9h experimental UI layout changes
For a more detailed view showing the diff of each stash, use:
git stash list --stat
git stash list -p
Inspecting a Stash Without Applying It
Before restoring a stash, you often want to peek at its contents to confirm it is the right one:
git stash show stash@{0}
To see the full diff (the actual code changes):
git stash show -p stash@{0}
You can also inspect only a specific file within a stash:
git stash show -p stash@{0} -- src/auth/login.ts
Restoring a Stash (Apply vs Pop)
There are two primary ways to bring stashed changes back into your working directory. The first, git stash apply, restores the changes but keeps the stash entry in the stack, allowing you to apply the same stash to multiple branches if needed:
git stash apply stash@{0}
The second, more common approach, git stash pop, applies the stash and immediately removes it from the stack:
git stash pop stash@{0}
If you omit the stash reference, both commands default to stash@{0} (the most recent stash). Be aware that pop will fail with merge conflicts if the stash cannot be cleanly applied. In that case, the stash is not deleted—Git keeps it safely in the stack, and you resolve conflicts as you would with any merge. After resolving, you can drop the stash manually.
Applying a Stash Without Touching the Staged Area
By default, both apply and pop attempt to restore the state of both the working directory and the staging area. If you want to apply only the working directory changes and ignore what was staged, use the --index flag's counterpart: simply omit it. To explicitly restore the staged state as well, use --index:
git stash apply --index stash@{0}
Without --index, everything lands in your working tree as unstaged modifications, and you can re-stage what you need manually.
Deleting Stashes
To remove a single stash entry from the stack:
git stash drop stash@{1}
To clear the entire stash stack (use with caution—this is irreversible):
git stash clear
Advanced Stash Operations
Stashing Only Specific Files
The push command accepts pathspecs, allowing you to stash only a subset of modified files rather than the entire working tree. This is invaluable when you have multiple unrelated changes and only want to set aside a particular set:
git stash push -m "Stash only the CSS fixes" -- src/styles/main.css src/styles/buttons.css
You can also combine pathspecs with the -u flag to include untracked files that match the specified paths:
git stash push -u -- src/components/NewDialog.tsx
Note the double dash -- that separates the command options from the file paths. This syntax prevents Git from misinterpreting file names as flags.
Stashing in Interactive Mode
For fine-grained control, you can launch an interactive patch mode that walks through each hunk of changes and asks whether you want to stash it. This is perfect when you have made multiple logical changes to a single file but only want to stash some of them:
git stash push -p -m "Partial stash: only the validation logic"
Git will present each diff hunk and prompt you with options: y to stash the hunk, n to skip it, s to split the hunk into smaller pieces, and q to quit the interactive session. The changes you select go into the stash; everything else remains in your working tree.
Creating a Stash Without Changing the Working Tree
Sometimes you want to capture a snapshot of your current state for later reference without actually cleaning the working directory. The --keep-index flag combined with a specific workflow allows this. However, the more direct way to create a stash while keeping all changes intact in the working tree is to use the create subcommand and store the resulting commit hash manually, but this is an advanced plumbing technique. For practical purposes, you can achieve a similar effect by branching:
git checkout -b temp-snapshot-branch
git commit -am "Snapshot of work in progress"
git checkout original-branch
# Later: git merge --no-commit temp-snapshot-branch
However, for pure stash purposes, the standard push remains the cleanest approach.
Applying a Stash to a Different Branch
Stashes are not tied to the branch where they were created. You can pop or apply any stash onto any branch, as long as the file structure is compatible. This enables powerful workflows where you start work on one branch, stash it, switch to another branch, and apply the changes there:
# On branch feature-auth
git stash push -m "Auth module refactor in progress"
git checkout feature-api
git stash pop stash@{0}
If there are merge conflicts, Git will halt and allow you to resolve them interactively. The stash itself remains safe in the stack until you either successfully pop it or manually drop it after resolving conflicts.
Creating a Branch Directly From a Stash
If a stash contains significant work that deserves its own dedicated branch, Git provides a shortcut that creates a new branch and applies the stash to it in one command:
git stash branch feature-auth-refactor stash@{0}
This command does the following: it checks out a new branch starting from the commit where the stash was originally created, applies the stash to that branch, and then drops the stash. If the stash applies cleanly, you now have a proper branch with all the stashed changes committed (they remain in the working tree as unstaged, so you can commit them properly). This is an excellent way to convert temporary shelved work into a permanent, shareable branch.
Working With the Stash Stack Like a Stack
The stash is fundamentally a stack data structure. You can manipulate entries by their index using the stash@{n} notation. To see the full list with dates for chronological context:
git stash list --date=relative
git stash list --date=local
You can apply or pop from the middle of the stack, but be aware that popping a non-top entry does not shift the entries above it—the indices remain stable. After popping stash@{1}, there will simply be a gap, and stash@{0} and stash@{2} remain at their same indices. To compact the stack, you can manually drop entries and re-stash as needed.
Best Practices
- Always add a message. A stash without a description is essentially anonymous. Six months later,
stash@{3}tells you nothing. Usegit stash push -m "concise description"every single time. Your future self will thank you. - Prefer
pushoversave. Thesavesubcommand is legacy syntax.pushis the modern, consistent interface that supports pathspecs, messages via-m, and all other options in a predictable order. Makegit stash pushyour muscle memory. - Keep the stash stack short. Treat the stash as a temporary shelf, not a permanent storage system. A stack with 15 entries is a sign that work is fragmented and needs proper branching. Regularly run
git stash listand prune entries you no longer need withgit stash droporgit stash clear. - Stash before pulling, but after fetching. If you have local changes and need to incorporate remote updates, stash first, then pull, then pop. This prevents messy merge conflicts that mix your unfinished work with upstream changes. If conflicts occur on pop, you handle them in isolation.
- Use
--include-untrackedconsciously. By default,git stashignores untracked files. This is often desirable because new files might be large binaries, logs, or generated artifacts. Explicitly opt in with-uonly when you know the untracked files are meaningful source code that belongs with the stash. - Test before popping. Run
git stash show -pto inspect the changes before applying them. This prevents the surprise of introducing unexpected code into your working tree, especially if you are on a different branch than where the stash was created. - Convert substantial stashes into branches. If a stash represents a coherent unit of work that you intend to keep long-term, use
git stash branchto promote it to a real branch. Branches are visible to collaborators, can be pushed to remotes, and are backed up—stashes are purely local and easily lost. - Resolve pop conflicts diligently. When
git stash pophits conflicts, Git stops and leaves conflict markers in your files. The stash is not deleted. After resolving all conflicts withgit addto mark resolution, manually drop the stash withgit stash drop. Never assume a failed pop deleted the entry—it did not. - Do not stash merge commits or complex states. Stashing works best for straightforward working tree modifications. If your working directory involves a partially completed merge, cherry-picks in progress, or bisect sessions, commit or abort those operations cleanly before stashing. Stashing in the middle of a merge conflict can produce unexpected results.
- Integrate stash awareness into your shell prompt. Many shell prompt frameworks (like
oh-my-zsh's git plugin orstarship) display the stash count directly in the terminal prompt. A small indicator like⚑ 3reminds you that work is shelved and prevents the "where did my code go?" panic.
Common Workflow Patterns
Pattern 1: The Quick Context Switch
# You are mid-feature on feature/payment with dirty working tree
git stash push -m "WIP payment integration: stripe webhook handler"
# Switch to main, create hotfix branch
git checkout main
git checkout -b hotfix/critical-typo
# Fix the bug, commit, merge back to main
echo "Fixed typo in landing page" >> index.html
git add index.html
git commit -m "Fix critical typo on landing page"
git checkout main
git merge hotfix/critical-typo
# Return to original branch and restore work
git checkout feature/payment
git stash pop stash@{0}
Pattern 2: Stash Before Pulling Remote Changes
# Local changes exist on feature/team-collab
git stash push -m "Pre-pull backup: local edits on team-collab"
# Fetch and merge remote changes
git fetch origin
git pull origin feature/team-collab --rebase
# Reapply your stashed work on top of the updated branch
git stash pop stash@{0}
# If conflicts arise, resolve them, then:
git add .
# The stash has already been popped (it failed but stays),
# so manually drop it after resolution:
git stash drop stash@{0}
Pattern 3: Partial Stash for Granular Control
# You modified three files but only want to stash changes in api.ts
git stash push -p -m "Only the API client changes"
# Git walks you through hunks interactively
# You select only the api.ts hunks
# The remaining changes stay in your working tree
# Now commit the leftover changes cleanly
git add src/ui/dashboard.ts src/utils/format.ts
git commit -m "Complete UI dashboard and formatting utilities"
Pattern 4: Promoting a Stash to a Permanent Branch
# You realize stash@{2} contains a complete feature worth keeping
git stash list
# stash@{2}: WIP: full OAuth2 implementation with PKCE flow
# Create a dedicated branch from this stash
git stash branch feature/oauth2-pkce stash@{2}
# The branch is created, stash is applied and dropped
# Now you are on feature/oauth2-pkce with all changes in working tree
git add .
git commit -m "Initial implementation of OAuth2 PKCE flow"
# Push to remote for backup and collaboration
git push -u origin feature/oauth2-pkce
Understanding Stash Conflict Resolution
When git stash pop encounters conflicts, the process is identical to resolving a merge conflict. Git marks the conflicting regions in the affected files with the standard markers (<<<<<<<, =======, >>>>>>>). The stash reference remains in the stack—it is never automatically deleted on failure. Here is the step-by-step resolution workflow:
# Attempt to pop the stash
git stash pop stash@{0}
# Auto-merging src/auth/login.ts
# CONFLICT (content): Merge conflict in src/auth/login.ts
# The stash entry is kept in case you need it again.
# Inspect the conflict
cat src/auth/login.ts
# You see the standard conflict markers
# Resolve the conflict manually in your editor
# Then mark the file as resolved
git add src/auth/login.ts
# Verify the resolution
git diff --staged
# Complete the resolution (no commit needed, just drop the stash)
git stash drop stash@{0}
# If you want to abort the entire pop and restore the pre-pop state:
git reset --hard HEAD
git stash list # stash@{0} is still there, untouched
A crucial detail: after resolving conflicts, there is no "stash pop --continue" command. The stash has already been applied to your working tree (with conflict markers). Your task is to resolve the conflicts, stage the resolutions, and then manually drop the stash entry. The working tree is now in the state you intended, and the stash stack is clean.
Stash Internals: A Brief Look
For developers who enjoy understanding the machinery beneath the surface, a stash is stored in the Git object database as a chain of commits. You can see the raw stash references:
cat .git/refs/stash
# Outputs the commit hash of stash@{0}
# View the full log of stash commits (including the index and untracked commits)
git log --oneline --graph stash@{0} --all
The stash commit itself has at least two parents: one representing the HEAD commit at the time of stashing, and another representing the staged changes. If untracked files were included, a third parent commit represents those. This structure allows Git to reconstruct your exact working state with fidelity, including the distinction between staged and unstaged modifications. Knowing this internals is not necessary for daily use but demystifies why stashes can be applied across branches and why they survive even aggressive garbage collection (since they are reachable from .git/refs/stash).
Conclusion
Git stash is far more than a simple "save for later" button. It is a sophisticated, stack-based mechanism for context management that enables developers to move fluidly between tasks without polluting their commit history or losing work. From the basic push and pop commands to advanced techniques like interactive partial stashing, pathspec-based file selection, and stash-to-branch promotion, mastering these operations equips you with a safety net that transforms interruptions from productivity killers into seamless pivots. The key to long-term stash hygiene lies in consistent messaging, regular pruning, and the discipline to convert substantial stashes into proper branches. By integrating these practices into your daily workflow, you ensure that the stash remains a trusted, lightweight tool rather than a chaotic dumping ground. The next time a colleague asks you to drop everything and fix an urgent bug, you will not hesitate—you will simply git stash push -m "saving progress", handle the crisis with confidence, and return to your work exactly where you left off.