As an experienced full stack developer, I often find myself juggling multiple features or bug fixes simultaneously. The ability to easily set aside incomplete work to pick back up later is invaluable. Git‘s "stash" feature fits that need perfectly by sheltering local changes so I can switch contexts at will.
But stash creation is only half of the puzzle – to restore my shelved changes, I need to pop the exact right stash at the right time. Here I‘ll unpack advanced management of multiple stashes, with a deep emphasis on techniques to pop targeted stashes precisely and avoid pitfalls.
Stash Listing and Identification
The first step is getting visibility into what stashes I have saved currently. git stash list
renders that for me:
$ git stash list
stash@{0}: WIP on new-feature
stash@{1}: WIP on bug-fix
This shows two stashes:
stash@{0}
: Changes related to my new featurestash@{1}
: Changes aimed at a specific bug fix
The stash@{N}
syntax identifies each stash by creation order – stash@{0}
pointing to the most recently made stash. I‘ll leverage that index later to pop stashes based on recency.
Restoring Stashed Changes
Once I‘ve listed my shelters work, I can rehydrate changes using git stash apply
:
$ git stash apply stash@{1}
This extracts the stash named stash@{1}
and applies the changes back into my working tree.
But there‘s a catch – the stash still remains in my list after applying it:
$ git stash list
stash@{0}: WIP on new-feature
stash@{1}: WIP on bug-fix
For true stash removal, I need git stash pop
instead.
The Power of git stash pop
git stash pop
fuses applying changes and deleting the referenced stash:
$ git stash pop stash@{0}
Now with the changes back and no leftover stash, I‘m set to resume work:
$ git stash list
stash@{0}: WIP on bug-fix
This makes pop
ideal when I know I won‘t need to re-apply a given shelved change. It also avoids clutter from one-off or outdated stashes.
Why Target Specific Stashes?
With the power to apply and destroy stashes instantly, why carefully choose which to pop? Two main factors drive my decision making:
1. Mitigate Conflict Risk
If multiple stashes touch the same files, popping one then another may introduce merge conflicts. The code changes hunks could clash, unable to integrate cleanly.
By selectively popping only the single stash I need based on context, I sidestep this hazard entirely.
2. Preserve Related Shelved Work
Often times my stashes capture progress across a range of associated features or fixes. Popping the wrong one by accident can erase relevant context I still need access to.
Again, precisely targeting the appropriate stash via reference avoids this loss, keeping related stashes bundled.
Real-World Stash Popping
Let‘s walk through a practical example highlighting why and how I‘d pop a single specific stash from many.
$ git status
On main branch, mid-feature
Changes not staged for commit:
modified: index.js
I‘m on my main
branch building out a new commenting feature. So far I‘ve modified my main index.js
page handler, but haven‘t committed changes yet.
Now a high priority bug report comes in that I must act on quickly. So I shelve my work in progress:
$ git stash
Saved working directory state: "WIP comments"
With my changes safely stashed, I can switch to the problematic code area without risk of overwriting anything:
$ git checkout notifications
Switched to notifications branch
After diagnosing the notifications bug via inspection and debugging, I have a fix identified. But as I start editing, I realize I need context from my recent commenting feature work to ensure compatibility between components.
I deliberately stashed those changes so I can now restore just that specific stash before writing my bug fix code:
$ git stash list
stash@{0}: WIP comments
stash@{1}: Old debug logging
$ git stash pop stash@{0}
This returns my relevant in-progress commenting work into my working tree, fueling creation of a clean cross-compatible bug fix.
Restoring Relative to Recency
Beyond referencing a stash directly by ID like stash@{1}
, I can leverage the ordered index to target relative to how recently stashes were created.
stash@{0}
always refers to my most recent stash, with the number increasing for progressively older shelters:
$ git stash list
stash@{2}: Old debug code
stash@{1}: UI tweak
stash@{0}: Menu styling
Here stash@{2}
is the oldest, and stash@{0}
the newest.
With this, I can pop the most recent using a constant:
$ git stash pop stash@{0} # Removes menu styling
And I know the higher the index number, the older and less likely to need restoring that stash is. This index makes easy pickings for when I just need the latest change set back.
Understanding Stash Internals
Beyond simply using stashes, understanding what‘s actually going on under the hood helps me wield them most effectively.
On a lower level, stashing:
- Records the current state of:
- The index (staging area)
- Any untracked and ignored files
- Performs a hard reset to
HEAD
- Stores indexed content in a commit not on any branch
This explains why uncommitted edits vanish on stash
– they get wiped away by the reset before storage.
It also clarifies why stashes don‘t belong to any branch – they float in limbo until restoration after being recorded to the refs namespace.
Knowing this internals, I‘m careful not to assume too much about stashed content without inspecting further.
Comparing Stashes to Alternatives
While stashing changes works very well for short-term, I also leverage some other Git mechanisms for keeping more permanent contextual work in progress copies:
Branching
Creating a feature branch shelters my edits the same way, but keeps them part of a branch I can push up and collaborate on. I favor this for longer-running efforts.
Worktrees
Alternative worktrees let me have multiple working directories checked out off the same commit. It‘s like branching without actually branching! Perfect when I just need to flip contexts initiated from the exact same starting point every time.
Patch files
Using git diff
or git format-patch
, I can export changesets as patch files at any point. But these require manual application with git am
and don‘t auto-shelve anything, so I reach for fully-fledged stashes first.
For fast and lightweight WIP saving and restoring, stashing fits the bill perfectly in day to day development.
Inspecting Stash Contents
Once I‘ve created multiple stashes, I sometimes lose track of exactly what‘s saved where. Rather than popping stashes randomly seeking what I want, I can inspect the changes first using:
$ git stash show stash@{1}
This shows me the committed diffs encapsulating a stash without actually applying anything.
Alternatively, I can generate and examine patch files for a stash:
$ git stash show -p stash@{1} > my_stash.patch
$ git apply --stat my_stash.patch
Now with concrete understanding of what my shelters look like inside, I can plan my pops strategically.
Configuring Stashing Policy
I often customize stash behavior in my development environments to reduce surprises:
$ git config stash.autostash true
$ git config stash.showStat true
With autostash
, any dirty commits automatically generate a stash entry rather than failing outright. Helpful when I need to force push but can‘t commit yet.
And showStat
makes status details print every time I create or apply stashes – super handy context!
For even more control, I configure stash names:
$ git config stash.ui.showMessage true
$ git config stash.showPatch true
This autogenerates descriptors from commit messages on save and shows full diffs on apply. Both huge clarity boosts!
Best Practices for Stash Pros
Over years of active Git development, I‘ve distilled stash management best practices any team can apply:
Name stashes explicitly
Rather than stock WIP
messages, I describe stash purpose directly like feature-cookie-API-prep
. This gives perfect clarity when listing later.
Pop early, pop often
As soon as I resume work on a feature, I pop
to eliminate no-longer-needed stashes. Watching them accumulate spells trouble.
Limit per-feature stashing
Complex features with lots of changes are hard to untangle. I avoid mammoth stashes by committing incrementally when possible.
Applying these principles leads to smooth sailing when juggling multiple efforts in parallel!
Debugging Stash Application Failures
With changes scattered across files and commits, stash merging can sometimes cause conflicts. Rather than plowing ahead or discarding work though, I debug smartly:
$ git stash pop
Error: Merge conflicts detected...
$ git status # Shows conflict details
$ git stash show -p > pop_stash.patch # Isolate the problematic changes
$ git apply --reject pop_stash.patch # Stage just failed chunks
$ git diff # Resolve small conflicts locally
$ git add . # Finalize adjusted merge
$ git commit # All fixed!
By analyzing precisely which stash content caused issues, I resolve conflicts smoothly and salvage development flow.
Stashes Meet GUIs
While fast and fluent on the command line, I still leverage visual tools to enhance my stash-driven workflows.
GitHub Desktop
GitHub‘s official desktop client visually surfaces stashes among branches with intuitive apply and popping built-in. Perfect for managing a handful of shelters.
GitKraken
For advanced use, GitKraken adds graphical stash ref manipulation plus drop-and-merge abilities. With complex dependency mapping, I stay oriented in my work.
Integrated GUI views make juggling multiple stashes far more accessible to newer developers on my team as well.
Pop With Precision
As a senior full-stack developer, stashes form the foundation of my continuous context shifting and multi-tasking. No other mechanism lets me instantly shelve work at a moment‘s notice.
But careless accumulation of stashed changes leads only to confusion down the road. By targeting particular stashes directly for apply or pop, I work cleanly without disruption.
Now armed with advanced stash commands, custom configuration techniques, troubleshooting tips, and my top wisdom for managing stashes at scale, you too can work smoothly across a spectrum of efforts in parallel!