You are halfway through a login form. A hotfix needs to land on main. git switch refuses because your working tree is dirty. The tempting escape is a fake commit titled WIP — clutter you will forget to undo, and a snapshot you never meant to keep.

Snapshot basics live in Git Without the Panic. Creating and publishing a feature branch lives in Stay Off Main. This page is the companion that post promised: park uncommitted edits, switch, restore them when you come back.

Warm-up: stash lab

Reuse the bare-remote pattern so the lab matches the rest of this series. From a scratch folder:

mkdir -p git-stash-lab
cd git-stash-lab
git init --bare remote.git
git clone remote.git app
cd app

echo 'App v1' > README.md
git add README.md
git commit -m "Initial README"
git branch -M main
git push -u origin main

git switch -c feature/login
echo 'Login form placeholder' > login.txt
git add login.txt
git commit -m "Add login placeholder"
echo 'WIP: password field' >> login.txt

You now have a feature branch with a dirty tracked file and no extra commit. Everything below runs inside app/ unless noted. Delete git-stash-lab when you finish.

Note: Prefer git switch for changing branches. Older docs use git checkout; the switch forms below are the everyday ones.

Mental model: a stash is a stack, not a branch

A stash is a stack of snapshots of uncommitted work — not a branch name and not a commit on your feature line. The newest entry is stash@{0}. Older ones shift to stash@{1}, stash@{2}, and so on. Branch pointers stay put; only the working tree (and the index, if you had staged files) is parked.

List what Git thinks is dirty:

git status
On branch feature/login
Changes not staged for commit:
        modified:   login.txt

That modified login.txt is what a stash will hold. After a successful stash, git status should report a clean tree so git switch can proceed.

Note: Stashes live only in this clone. They are not pushed to origin. A new clone, or another machine, will not see them.

Hero use case: stash, switch, restore

You need main for a moment. Do not invent a WIP commit. Park the dirty file, switch, then bring the edits back.

git stash
git status
Saved working directory and index state WIP on feature/login: ...
On branch feature/login
nothing to commit, working tree clean

login.txt is back to the last commit. The WIP line is on the stash stack, not in history. Switch, do the other work, then return and restore:

git switch main
git switch feature/login
git stash pop
git status

login.txt is dirty again — the password-field line is back. pop restores the snapshot and removes it from the stack. git stash list should now be empty.

Note: If the restored patch conflicts with the current files, Git keeps the stash on the stack (it did not finish the pop). Fix the overlapping lines the same way as in When Push Fails, then git add and git stash drop if the entry is still listed.

Name, list, and inspect stashes

A default stash message is easy to forget once you have two. Park with a label:

echo 'WIP: remember-me checkbox' >> login.txt
git stash push -m "login remember-me checkbox"
git stash list
stash@{0}: On feature/login: login remember-me checkbox

git stash push -m is the same park as git stash, plus a name you can read later. Preview without restoring:

git stash show
git stash show -p

git stash show prints a diffstat for stash@{0}. -p prints the patch. Pass stash@{1} (or another index from git stash list) to inspect an older entry.

Note: After pop, apply, or drop, the {n} numbers shift. Always git stash list before you target a specific entry.

pop vs apply, drop, and untracked files

pop is restore-and-remove. apply restores and leaves the stash on the stack — useful when you want the same patch on more than one branch, or you are not sure the restore will stick.

git stash apply
git stash list

The remember-me stash is still stash@{0}. Remove it without touching files:

git stash drop
# or: git stash drop stash@{0}
git stash list

git stash clear deletes every stash in this clone. Use it only when you intend to throw the whole stack away.

apply left edits in the tree. Park them and drop that stash so the next demo is only about a new file:

git stash
git stash drop

Default stash ignores brand-new files. Create one and park without -u:

echo 'scratch' > scratch.tmp
git stash
git status
No local changes to save

On branch feature/login
Untracked files:
        scratch.tmp

Plain git stash does not take untracked files. Include them explicitly:

git stash push -u -m "untracked scratch"
git status

The untracked file is gone from the tree until you git stash pop (or apply). -u is --include-untracked. Use it when the WIP includes new files you have not git added yet.

Note: pop that stash (or drop it) before the drills so scratch.tmp does not linger.

When a real commit is clearer

Stash is a short detour: dirty tree, need another branch, come back the same day. A commit on your feature branch is clearer when the work should survive overnight, another clone, or a teammate’s review.

echo 'WIP: password field' >> login.txt
git add login.txt
git commit -m "WIP: password field on login form"
git switch main
git switch feature/login

That WIP commit is honest history on feature/login. Keep building on it, or undo it locally later with the reset options in When Push Fails if it never left your machine. Prefer a commit when you might push the branch, or when losing the work would hurt. Prefer a stash when you only need git switch to succeed for a moment.

Quick reference card

GoalCommand
Park tracked editsgit stash
Park with a namegit stash push -m "message"
Include untracked filesgit stash push -u -m "message"
List stashesgit stash list
Preview newest (stat / patch)git stash show / git stash show -p
Restore and removegit stash pop
Restore, keep the stashgit stash apply
Delete one stashgit stash drop or git stash drop stash@{n}
Switch after a clean treegit switch main

Practice drills

Inside git-stash-lab/app (recreate the bare remote and clone if needed):

  1. On feature/login, dirty login.txt, stash, confirm git status is clean, switch to main, switch back, and git stash pop.
  2. Create two named stashes with git stash push -m, run git stash list, and git stash show the newest without applying.
  3. Stash a change, git stash apply, confirm the entry is still listed, then git stash drop it.
  4. Add an untracked file, confirm plain git stash leaves it, then git stash push -u -m "untracked" and confirm the file is gone until you pop.
  5. Dirty the feature again. Instead of stashing, commit a WIP message, switch to main, and switch back — the commit path for work you will resume later.

Solid answers — clear and portable:

# 1
echo 'WIP: password field' >> login.txt
git stash
git status
git switch main
git switch feature/login
git stash pop

# 2
echo 'a' >> login.txt && git stash push -m "stash a"
echo 'b' >> login.txt && git stash push -m "stash b"
git stash list
git stash show

# 3
echo 'apply-me' >> login.txt
git stash
git stash apply
git stash list
git stash drop

# 4
echo 'scratch' > scratch.tmp
git stash
git status
git stash push -u -m "untracked"
git status
git stash pop

# 5
echo 'more wip' >> login.txt
git add login.txt && git commit -m "WIP: password field"
git switch main
git switch feature/login

If those five feel routine, you already have the switch-with-a-dirty-tree habit: park with stash, or commit on the feature branch when the work should last. Day-one snapshots remain in Git Without the Panic. Branch create, publish, and merge remain in Stay Off Main. Next skill when you need it is Git Rebase — replay a feature branch onto origin/main without a merge commit.

Next optional step Replay a feature branch onto origin/main without a merge commit. Git Rebase: Replay Your Branch Without a Merge Commit