Skip to main content
Git & GitHub · lesson 4 of 12
Git & GitHub beginner Lesson 4 of 12

Stashing Work in Progress

Shelve an unfinished edit and an untracked file with git stash, make an unrelated commit on a clean tree, then reapply the stash with a real auto-merge.

By SkillByExample · Published

git stash

git stash shelves uncommitted work so you can switch context — pull a hotfix, review something else — without committing half-finished changes. This lesson shelves a real edit and an untracked file, commits something unrelated in between, then brings the work back.

1) Two kinds of uncommitted change: modified and untracked

$ echo "DEBUG = True" > config.py
$ # ...edit app.py mid-thought...
$ git status -s
 M app.py
?? config.py

app.py is a modified tracked file; config.py is a brand-new untracked one. Plain git stash would only shelve the first.

2) Stash both, including the untracked file

$ git stash push -u -m "WIP: greeting formatting"
$ git status -s
Saved working directory and index state On main: WIP: greeting formatting

-u pulls untracked files into the stash too. git status -s afterward prints nothing — the working directory is back to matching the last commit.

3) Stashes are a named list, not a single slot

$ git stash list
stash@{0}: On main: WIP: greeting formatting

You can stash multiple times; each entry gets an index. stash@{0} is always the most recently stashed.

4) The tree moves on while the stash sits

$ # ...append test_farewell() to app.py...
$ git commit -am "Urgent: add farewell test"
$ git log --oneline -2
c489307 Urgent: add farewell test
d7da0e1 Revert "Change greeting to Hi (oops)"

This is the actual point of stashing: a real commit landed on app.py while the WIP edit was shelved, unrelated to it.

5) Popping replays the stash against the new tree

$ git stash pop
Auto-merging app.py
On branch main
Changes not staged for commit:
	modified:   app.py

Untracked files:
	config.py

Dropped refs/stash@{0} (8c38a562...)

Git doesn’t just overwrite app.py — it re-applies the stashed diff on top of the current tree, which now includes c489307. Auto-merging means it succeeded without a conflict; config.py reappears as untracked, exactly as it was stashed.

Practice

  1. Run git stash push (without -u) on a tree with both a modified file and an untracked file. Confirm only the modified file gets stashed and the untracked file is left sitting in the working directory.

  2. Stash twice in a row with different messages, then run git stash list to see both entries. Use git stash pop stash@{1} to pop a specific one instead of the most recent.

    Solution
    $ git stash list
    stash@{0}: On main: second WIP
    stash@{1}: On main: first WIP
    $ git stash pop stash@{1}

    Popping an older stash by name leaves the newer one in place.

  3. Force a stash-pop conflict: stash an edit to a specific line, then commit a different edit to that same line before popping. Read the conflict markers and resolve them the same way you would a merge conflict.

  4. Run git stash show -p on a stash (before popping it) to preview its diff without applying it yet.

  5. Use git stash drop to discard a stash you no longer need, and confirm with git stash list that it’s gone — unlike pop, drop never reapplies the changes.

Frequently Asked Questions

Why does git stash need -u to include config.py?
By default git stash only shelves changes to tracked files. Untracked files (new files you haven't git add-ed yet) are left alone unless you pass -u/--include-untracked, which is what let config.py travel with the rest of the WIP.
What's the difference between git stash pop and git stash apply?
Both reapply a stash's changes to your working directory. pop also removes the stash from the list afterward (like a stack pop); apply leaves it in the list so you can reapply it again elsewhere. Use apply when you want to try the same stash against more than one branch.
Why did git stash pop print 'Auto-merging app.py' instead of just restoring the file?
The tree had moved on — a new commit landed on app.py while the stash existed. Popping a stash replays it as a diff against the current tree, not a plain file overwrite, so Git has to merge the stashed edit with whatever changed since.
Can a stash pop conflict, the same way a merge can?
Yes. If the stashed diff and the commits made since overlap on the same lines, git stash pop leaves conflict markers in the file, just like git merge does, and the stash is not dropped until you resolve and finish it.