Git Stash

Work-in-progress management: save, retrieve, and manage uncommitted changes.

Stash Fundamentals

# BASIC STASH
git stash                                # Stash tracked changes
git stash push                           # Same, explicit
git stash -m "WIP: feature work"         # Stash with message

# STASH OPTIONS
git stash -u                             # Include untracked files
git stash --include-untracked            # Same, verbose
git stash -a                             # Include ignored files too
git stash --all                          # Same, verbose
git stash -k                             # Keep staged changes
git stash --keep-index                   # Same, verbose

# STASH SPECIFIC FILES
git stash push -- path/to/file.txt       # Stash specific file
git stash push -- '*.adoc'               # Stash by pattern
git stash push -m "WIP" -- dir/          # Stash directory with message

# INTERACTIVE STASH
git stash push -p                        # Patch mode (hunk by hunk)
# y = stash this hunk
# n = skip this hunk
# s = split hunk
# q = quit (stash selected)

# WHAT GETS STASHED
# - Modified tracked files
# - Staged changes (merged with modifications)
# NOT stashed by default:
# - Untracked files (use -u)
# - Ignored files (use -a)
# - Index/staging (staged and unstaged merged)

Retrieving Stashes

# APPLY STASH
git stash pop                            # Apply most recent + remove from list
git stash apply                          # Apply most recent, keep in list
git stash apply stash@{2}                # Apply specific stash

# POP VS APPLY
# pop   = apply + drop (removes stash)
# apply = just apply (keeps stash)
# USE pop for: one-time retrieval
# USE apply for: applying same stash to multiple branches

# APPLY PRESERVING INDEX
git stash apply --index                  # Restore staged/unstaged separately
# Default: all changes become unstaged
# --index: staged changes stay staged

# CREATE BRANCH FROM STASH
git stash branch new-branch              # Create branch, apply stash, drop
git stash branch feature stash@{1}       # From specific stash

# PARTIAL RESTORE (via patch)
git stash show -p stash@{0} | git apply  # Apply as patch (can edit)

# DROP STASH
git stash drop                           # Remove most recent
git stash drop stash@{2}                 # Remove specific
git stash clear                          # Remove ALL stashes (careful!)

Inspecting Stashes

# LIST STASHES
git stash list                           # All stashes
git stash list --oneline                 # Compact format

# STASH LIST OUTPUT
# stash@{0}: WIP on main: abc1234 Last commit message
# stash@{1}: On feature: def5678 Another commit
# stash@{2}: WIP on main: abc1234 Last commit message

# SHOW STASH CONTENT
git stash show                           # Stat summary (default)
git stash show -p                        # Full diff (patch)
git stash show stash@{1}                 # Specific stash
git stash show -p stash@{2}              # Specific stash with diff

# SHOW UNTRACKED FILES IN STASH
git stash show --include-untracked -p stash@{0}
git stash show -u -p                     # Short form

# STASH DETAILS WITH LOG
git log --oneline -1 stash@{0}           # Stash commit info
git log -g stash                         # All stash entries with dates

# COUNT STASHES
git stash list | wc -l

# SEARCH STASHES
git stash list | grep "WIP"              # Find by message
for i in $(seq 0 $(($(git stash list | wc -l) - 1))); do
  echo "=== stash@{$i} ==="
  git stash show -p stash@{$i} | head -20
done

Stash Workflows

# CONTEXT SWITCH WORKFLOW
# Working on feature, need to fix urgent bug:
git stash -m "WIP: feature/oauth halfway done"
git checkout main
git checkout -b hotfix/critical
# ... fix bug ...
git add -A && git commit -m "fix: critical bug"
git checkout main && git merge hotfix/critical
git push origin main
git checkout feature/oauth
git stash pop                            # Resume work

# CLEAN PULL WORKFLOW
# Have local changes, need to pull:
git stash
git pull origin main
git stash pop
# If conflicts: resolve, then git stash drop

# MOVE CHANGES TO NEW BRANCH
# Started work on main, should be on feature:
git stash
git checkout -b feature/new-work
git stash pop

# TEST ON CLEAN STATE
# Want to test without current changes:
git stash
# ... run tests on clean state ...
git stash pop                            # Restore changes

# SELECTIVE STASH WORKFLOW
# Only stash specific files:
git stash push -m "WIP: auth changes" -- src/auth.py
# Continue working on other files
# Later:
git stash pop                            # Get auth.py back

# MULTIPLE STASH MANAGEMENT
# Tag stashes clearly:
git stash push -m "FEAT: oauth - login flow"
git stash push -m "FIX: race condition debug"
git stash push -m "TEMP: logging for troubleshooting"

# Find and apply specific:
git stash list | grep "oauth"
git stash apply stash@{2}                # Apply the oauth one

# STASH FOR REBASE
# Can't rebase with dirty tree:
git stash
git fetch origin
git rebase origin/main
git stash pop

Infrastructure Repository Stash Patterns

# DOCUMENTATION WIP PATTERN
# Working on runbook, need to build/test another:
cd ~/atelier/_bibliotheca/domus-infra-ops
git stash -m "WIP: k3s-prometheus runbook incomplete"

# Test another repo's build
cd ~/atelier/_bibliotheca/domus-docs
make

# Resume
cd ~/atelier/_bibliotheca/domus-infra-ops
git stash pop

# MULTI-REPO STASH STATUS
for repo in domus-infra-ops domus-captures domus-ise-linux; do
  count=$(git -C ~/atelier/_bibliotheca/$repo stash list 2>/dev/null | wc -l)
  if [ "$count" -gt 0 ]; then
    echo "$repo: $count stash(es)"
    git -C ~/atelier/_bibliotheca/$repo stash list
  fi
done

# STASH BEFORE SYNC
# When running domus-push or similar:
for repo in ~/atelier/_bibliotheca/domus-*/; do
  if [ -n "$(git -C "$repo" status --porcelain)" ]; then
    name=$(basename "$repo")
    git -C "$repo" stash -m "auto-stash before sync"
    echo "Stashed: $name"
  fi
done

# RESTORE AFTER SYNC
for repo in ~/atelier/_bibliotheca/domus-*/; do
  stash_count=$(git -C "$repo" stash list 2>/dev/null | wc -l)
  if [ "$stash_count" -gt 0 ]; then
    name=$(basename "$repo")
    echo "=== $name has $stash_count stash(es) ==="
    git -C "$repo" stash list --oneline
  fi
done

# STASH FOR ANTORA BUILD TESTING
# Test clean build:
cd ~/atelier/_bibliotheca/domus-infra-ops
git stash -u                             # Include untracked
cd ~/atelier/_bibliotheca/domus-docs
make                                     # Build with committed content only
# Check if issues from uncommitted changes:
cd ~/atelier/_bibliotheca/domus-infra-ops
git stash pop

# EMERGENCY STASH
# About to pull/rebase, forgot about changes:
git stash                                # Quick save
git pull --rebase origin main
git stash pop

Advanced Stash Techniques

# STASH UNTRACKED ONLY
# Stash new files without touching modified:
git stash push -u --keep-index
# Result: staged changes preserved, untracked stashed

# STASH WITH PATHSPEC
git stash push -m "just tests" -- tests/
git stash push -m "configs" -- '*.yml' '*.yaml'

# APPLY AS PATCH (for flexibility)
git stash show -p stash@{0} > /tmp/stash.patch
# Edit patch if needed
git apply /tmp/stash.patch

# CREATE STASH WITHOUT APPLYING
git stash create "WIP message"           # Returns SHA, doesn't save
git stash store -m "manual stash" <sha>  # Store the SHA as stash

# STASH ENTRY ANATOMY
# Stash is a merge commit with 3 parents:
# - Working tree state (stash@{0}^0)
# - HEAD at time of stash (stash@{0}^1)
# - Index state (stash@{0}^2)
# - Untracked files if -u (stash@{0}^3)

# VIEW STASH INTERNALS
git log --graph --oneline stash@{0}^1..stash@{0}

# DIFF BETWEEN STASHES
git diff stash@{0} stash@{1}

# APPLY SPECIFIC FILE FROM STASH
git checkout stash@{0} -- path/to/file
# Or:
git show stash@{0}:path/to/file > path/to/file

# STASH REFLOG
git reflog show stash                    # Stash history
# Useful if you accidentally dropped a stash recently
# (within gc expiry period)

# RECOVER DROPPED STASH (if recent)
git fsck --unreachable | grep commit | awk '{print $3}' | while read sha; do
  if git log -1 --format=%s "$sha" 2>/dev/null | grep -q "WIP on\|On branch"; then
    echo "Possible stash: $sha"
    git log -1 "$sha"
  fi
done

Stash Gotchas

# WRONG: Expecting untracked files to be stashed
git stash
# New files NOT stashed!

# CORRECT: Include untracked with -u
git stash -u
# Now new files are included

# WRONG: Stash with merge conflicts
git merge feature
# CONFLICT...
git stash
# Error: cannot stash with conflicts!

# CORRECT: Resolve or abort first
git merge --abort
# Then stash

# WRONG: Pop with conflicts, losing stash
git stash pop
# CONFLICT...
# Stash is REMOVED even though pop failed!

# CORRECT: Use apply for safety
git stash apply
# CONFLICT...
# Stash still exists! Fix conflicts, then:
git stash drop

# WRONG: Assuming stash preserves staged state
git add file.txt
git stash
git stash pop
git status
# file.txt is UNSTAGED now!

# CORRECT: Use --index to preserve staging
git stash --index
git stash pop --index
# Staged state preserved

# WRONG: Stashing on wrong branch
git checkout feature
git stash
git checkout main
git stash pop
# Changes applied to main, not feature!

# CORRECT: Create branch from stash
git stash branch feature-restored stash@{0}

# WRONG: Relying on stash for long-term storage
# Stashes are local and can be lost

# CORRECT: Commit WIP to branch for safety
git checkout -b wip/my-feature
git add -A && git commit -m "WIP: save progress"
# Can always reset if needed

# WRONG: Clearing stash without checking
git stash clear
# All stashes gone! No recovery!

# CORRECT: Review before clearing
git stash list
git stash show -p stash@{0}
# Then clear if sure:
git stash clear

Quick Reference

# BASIC STASH
git stash                                # Stash tracked changes
git stash -u                             # Include untracked
git stash -m "message"                   # With description
git stash push -- path/                  # Specific files

# RETRIEVE
git stash pop                            # Apply + remove
git stash apply                          # Apply, keep stash
git stash apply stash@{2}                # Specific stash
git stash apply --index                  # Preserve staged state

# INSPECT
git stash list                           # All stashes
git stash show                           # Summary
git stash show -p                        # Full diff
git stash show -p stash@{1}              # Specific stash diff

# MANAGE
git stash drop                           # Remove latest
git stash drop stash@{2}                 # Remove specific
git stash clear                          # Remove all (careful!)
git stash branch feature stash@{0}       # Create branch from stash

# WORKFLOW
git stash                                # Save WIP
git checkout other-branch
# ... do work ...
git checkout original-branch
git stash pop                            # Resume

# OPTIONS
-u, --include-untracked                  # Include new files
-a, --all                                # Include ignored too
-k, --keep-index                         # Don't stash staged
-p, --patch                              # Interactive selection
-m "message"                             # Descriptive message

See Also

  • Branches — context switching with branches

  • Worktrees — alternative to stash for parallel work

  • Rewriting — reflog can recover dropped stashes