Stashing - Temporary Work Preservation

Stashing - Temporary Work Preservation

Architectural Context: The Stash Stack

Git’s stash mechanism provides a temporary storage system for uncommitted changes, enabling context switching without losing work-in-progress. Unlike commits, stashes exist outside the normal branch structure, functioning as a last-in-first-out (LIFO) stack for rapid state preservation and restoration.

Git’s Flexibility Principle

Git’s design embraces flexibility in how you manage work-in-progress. Just as Git allows selective staging of files or even parts of files, it doesn’t require you to commit everything at once—nor does it force you to lose uncommitted work when switching contexts. The stash mechanism exemplifies this philosophy: temporary storage without permanent commitment.

Design Insight: Stashing treats work-in-progress as legitimate repository state worth preserving, not as “incomplete work” to be discarded.


Conceptual Model

Problem Scenario: You’re mid-implementation when urgent production issues require immediate attention. Your working tree contains half-finished features and experimental code that shouldn’t be committed.

Traditional Workarounds (suboptimal):

  • Commit incomplete work with “WIP” message (pollutes history)
  • Copy files elsewhere (manual, error-prone)
  • Use separate clones (disk space intensive)

Git’s Solution: Stash mechanism provides clean, reversible state preservation.


Basic Stash Operations

Stash Creation

# Stash all tracked file modifications
git stash

# Equivalent explicit form
git stash push

# Stash with descriptive message
git stash push -m "WIP: authentication refactor"

Technical Behavior:

  1. Captures current working tree and staging area state
  2. Reverts working tree to HEAD state (clean working tree)
  3. Stores changes in stash stack with unique reference
  4. Enables immediate branch switching or pulling

Verification:

# Confirm clean state
git status
# Should show: "nothing to commit, working tree clean"

Stash Inspection

# List all stashes
git stash list

# Output format:
# stash@{0}: WIP on main: abc1234 Last commit message
# stash@{1}: On feature: Authentication refactor
# stash@{2}: WIP on main: def5678 Previous work

Stash Reference Format: stash@{n} where n=0 is most recent, incrementing for older stashes.

Detailed Stash Examination:

# Show changes in latest stash
git stash show

# Statistical summary
git stash show --stat

# Full diff
git stash show -p
git stash show -p stash@{1}  # Specific stash

Stash Application

# Apply latest stash (keeps stash in stack)
git stash apply

# Apply specific stash
git stash apply stash@{2}

# Apply and remove from stack (pop)
git stash pop

# Pop specific stash
git stash pop stash@{1}

Apply vs. Pop Decision Matrix:

  • Use apply: When you might need the stash again (experimental application)
  • Use pop: When stash is single-use (standard workflow restoration)

Stash Removal

# Remove latest stash
git stash drop

# Remove specific stash
git stash drop stash@{2}

# Remove all stashes
git stash clear

Warning: git stash clear is irreversible. Use with caution in repositories with valuable experimental work.


💡 Useful Aliases

git config --global alias.save 'stash push -m'
git config --global alias.pop 'stash pop'

Usage: git save "WIP: feature"

These aliases streamline your stash workflow with shorter, more memorable commands.


Advanced Stash Techniques

Selective Stashing

Problem: You have both production-ready changes and experimental modifications. You want to stash only the experiments.

Solution: Path-limited stashing

# Stash specific files
git stash push -m "Experimental logging" src/logger.py tests/logger_test.py

# Stash by pattern
git stash push -m "Debug artifacts" -- "*.log" "*.tmp"

Use Case: Isolate debug instrumentation while preserving core changes for commit.


Interactive Stashing

# Interactively select changes to stash
git stash push -p
git stash push --patch

Interactive Session:

diff --git a/src/auth.py b/src/auth.py
index abc1234..def5678 100644
--- a/src/auth.py
+++ b/src/auth.py
@@ -10,6 +10,8 @@ def authenticate(user):
     if not user:
         return None
+    # Debug logging
+    print(f"Authenticating: {user.email}")

Stash this hunk [y,n,q,a,d,e,?]?

Commands: Same as git add -p (y=yes, n=no, s=split, e=edit)


Stashing Untracked Files

Default Behavior: Standard stash ignores untracked files.

# Include untracked files in stash
git stash push -u
git stash push --include-untracked

# Include even ignored files
git stash push -a
git stash push --all

Scenario: You created new files during exploration that aren’t yet tracked. Standard stash would leave them in working tree.


Stash with Keep-Index

Problem: You’ve carefully staged specific changes but need to stash unstaged modifications.

Solution:

# Stash unstaged changes, keep staged intact
git stash push --keep-index

Workflow:

# Stage production-ready changes
git add feature.py

# Stash experimental changes
git stash push --keep-index

# Commit staged work
git commit -m "Add production feature"

# Restore experiments
git stash pop

Stash Recovery and Conflict Resolution

Stash Application Conflicts

Scenario: Working directory has changed since stash creation, causing conflicts during application.

# Attempt stash application
git stash pop

# Conflict occurs
CONFLICT (content): Merge conflict in src/auth.py
The stash entry is kept in case you need it again.

Resolution Process:

Step 1: Identify Conflicts

git status
# both modified:   src/auth.py

Step 2: Resolve Conflicts

# Edit file, resolve conflict markers
vim src/auth.py

# Stage resolution
git add src/auth.py

Step 3: Stash Cleanup

# Manually remove applied stash
git stash drop

Note: Unlike git stash pop, conflicts prevent automatic stash removal, requiring explicit drop.


Stash to Branch Pattern

Use Case: Stashed work represents significant feature requiring dedicated branch.

# Create branch from stash state
git stash branch feature-stashed-work

# Or specify stash
git stash branch feature-x stash@{2}

Technical Behavior:

  1. Creates new branch at commit where stash was created
  2. Checks out new branch
  3. Applies stash changes
  4. Removes stash from stack (if successful)

Advantage: Eliminates conflicts by applying stash to exact original commit state.


Stash Stack Management

Viewing Complete Stash State

# List with full context
git stash list --date=local

# Output:
# stash@{0}: WIP on main: abc1234 Refactor auth (Mon Jan 15 14:30:00 2024)
# stash@{1}: On feature: def5678 Add tests (Mon Jan 15 10:15:00 2024)

Reordering and Cleanup

Scenario: Stash stack contains multiple entries; you need to reorganize.

Strategy: Apply specific stashes, then drop obsolete entries.

# Apply stash in different order
git stash apply stash@{3}
git commit -m "Incorporate earlier work"

# Remove obsolete stashes
git stash drop stash@{3}
git stash drop stash@{2}

Automation Opportunity: Script stash cleanup for stashes older than N days.


Practical Workflow Patterns

Pattern 1: Emergency Context Switch

# Mid-feature, production issue arises
git stash push -m "WIP: user dashboard implementation"

# Switch to hotfix
git checkout main
git checkout -b hotfix/critical-auth-bug

# Fix, test, deploy
git commit -am "Fix authentication bypass"
git checkout main
git merge hotfix/critical-auth-bug
git push

# Resume feature work
git checkout feature-dashboard
git stash pop

Pattern 2: Experimental Code Isolation

# Add debug logging throughout codebase
# (extensive modifications)

# Need to commit production changes
git stash push -u -m "Debug instrumentation"

# Stage only production-ready changes
git add src/feature.py
git commit -m "Implement feature X"

# Restore debug logging for continued investigation
git stash pop

Pattern 3: Pull with Uncommitted Changes

# Local modifications present
git status
# modified:   src/config.py

# Attempt pull
git pull
# error: Your local changes would be overwritten by merge

# Solution: Stash, pull, restore
git stash
git pull
git stash pop

Alternative: Rebase pattern

git stash
git pull --rebase
git stash pop

Stash Limitations and Alternatives

When NOT to Use Stash

Stashing Reality Check: Stashing isn’t a substitute for proper branching strategy. If you find yourself:

  • Creating dozens of stashes
  • Losing track of what each stash contains
  • Stashing work for days or weeks

…you probably need a branch instead. Stashing excels at temporary, short-term storage (minutes to hours), not long-term work management.

Rule of Thumb: If you can’t remember what’s in a stash without git stash show, it’s been stashed too long.


What Stash Doesn’t Handle Well

Long-term storage: Stash is temporary; use branches for work spanning days/weeks

Shared state: Stash is local only; can’t share with teammates

Complex experimental work: Multiple related experiments better managed as branches

Partial file states: Stash operates on complete file changes, not line-level granularity


Alternative Patterns

For Long-Term WIP:

# Use WIP branch instead
git checkout -b wip/feature-exploration
git commit -m "WIP: Initial exploration"

For Sharing Incomplete Work:

# Push WIP branch to remote
git push -u origin wip/shared-investigation

For Complex Experimentation:

# Use worktrees for parallel investigation
git worktree add ../experiment-a
git worktree add ../experiment-b

Performance and Safety Considerations

Stash Storage Characteristics

Storage Location: .git/refs/stash (reference log)

Performance: O(1) for push/pop operations regardless of stash stack size

Retention: Stashes persist until explicitly removed or repository deleted

Recovery: Stashed changes recoverable via reflog even after drop:

# Find dropped stash
git fsck --unreachable | grep commit

# Create branch from stash commit
git branch recovered-stash <commit-sha>

Configuration and Customization

Stash Behavior Configuration

Configure how Git handles stashes for improved workflow:

# Show patch when stashing
git config --global stash.showPatch true

# Show stash statistics
git config --global stash.showStat true

# Include untracked files by default
git config --global stash.showIncludeUntracked true

Effect: When you run git stash, you’ll automatically see what’s being stashed, making it easier to verify you’re stashing the right changes.

Stash Aliases

# Quick stash shortcuts
git config --global alias.save 'stash push -m'
git config --global alias.pop 'stash pop'
git config --global alias.peek 'stash show -p'
git config --global alias.list 'stash list'

# Stash with untracked files
git config --global alias.stash-all 'stash push --include-untracked -m'

# Usage
git save "WIP: feature implementation"
git stash-all "Complete workspace state"
git peek  # Preview without applying

Stash Display Customization

# Enhanced stash list format
git config --global alias.stashes "stash list --pretty=format:'%C(yellow)%gd%C(reset): %C(green)%s%C(reset) %C(blue)(%cr)%C(reset)'"

# Detailed stash information
git config --global alias.stash-info '!git stash show -p'

Recommended Stash Configuration

Complete stash setup for optimal workflow:

# Core stash behavior
git config --global stash.showPatch false  # Don't show patch on stash (can be verbose)
git config --global stash.showStat true    # Show file statistics

# Helpful aliases
git config --global alias.stash-list "stash list --date=relative"
git config --global alias.stash-save 'stash push --include-untracked -m'
git config --global alias.stash-peek 'stash show -p'

# Verify configuration
git config --global --get-regexp stash
git config --global --get-regexp alias.stash

Common Pitfalls and Solutions

Pitfall 1: Forgetting About Stashes

Problem: Stash stack accumulates; context for each stash lost.

Solution: Always use descriptive messages

git stash push -m "Detailed description of stashed work"

Maintenance: Regular stash list review and cleanup

# Weekly review
git stash list

# Remove obsolete stashes
git stash drop stash@{n}

Pitfall 2: Stashing Without Message

# Creates generic "WIP on branch" message
git stash

# Better: Always include context
git stash push -m "Experimental query optimization"

Pitfall 3: Losing Track of Stash Order

Problem: Multiple stashes create confusion about which is which.

Solution: Apply specific stash by number

# Review before applying
git stash show stash@{2}

# Apply specific stash
git stash apply stash@{2}

Summary: Strategic Stash Application

Core Capabilities:

  1. Temporary Storage: Preserve work-in-progress without committing
  2. Context Switching: Enable rapid branch switching for urgent work
  3. Selective Preservation: Stash specific files or hunks
  4. Conflict Resolution: Handle stash application in changed contexts

Best Practices:

  • Always include descriptive messages with git stash push -m
  • Use git stash apply for experimental stash application
  • Regular stash list review and cleanup
  • Prefer branches over stash for work spanning multiple days

Strategic Decision Framework:

  • Use Stash: Quick context switches, temporary experiments, pulling with local changes
  • Use Branches: Long-term WIP, shared work, complex feature exploration

Technical Excellence: Stash provides tactical flexibility for interrupt-driven development without polluting commit history or requiring manual file management.


Next Steps: Craft Meaningful Commits