Table of Contents

Introduction

You're halfway through something and need a clean working directory right now: to switch branches, to pull, to check whether a bug exists without your changes. git stash puts your uncommitted work somewhere safe and gives you a clean tree. It's presented as a separate feature, but under the hood a stash is just a commit that no branch points at.

In this article, we'll:

  1. Watch git stash clear the working directory and staging area
  2. See which changes it takes and which it leaves behind
  3. Look at what a stash entry actually is

What is git stash?

git stash records your staged and unstaged changes to tracked files as a commit (two commits, in fact: one for the index and one for the working tree), stores a reference to it in refs/stash, and then resets your working directory and staging area to match HEAD. Untracked files are left alone unless you ask for them.

Watch it happen

Our sample repo has a modified file and a staged file to set aside, plus an untracked file. Here's git stash:

  1. Before: README.md has a staged change and app.py is modified in the working directory. HEAD is attached to main at 8c02d5b.
  2. Git saves both changes in a new stash entry, stash@{0}, and resets the working directory and staging area to match 8c02d5b.
  3. main and HEAD don't move. The stash entry is stored under refs/stash, and the untracked notes.txt (not drawn here) is left where it was.

Before and after

git status --short before:

M  README.md
 M app.py
?? notes.txt

and after:

?? notes.txt

git stash list now shows one entry, named after the commit you were on:

The raw git output, if you want to read along in text

git log --oneline --graph --all

before

* 1117a34 (feature) Add search tests
* e5869f0 Fix typo in search box
* fc19889 Add search box
| * 8c02d5b (HEAD -> main) Update dependencies
| * a0b2db3 Add user settings page
|/  
* 96c4fc2 Fix header layout
* ae65976 Add login page
* 3e1ffe4 Add project skeleton
* 4114b2c Initial commit

after

*   a827e22 (refs/stash) WIP on main: 8c02d5b Update dependencies
|\  
| * e6cd7fc index on main: 8c02d5b Update dependencies
|/  
* 8c02d5b (HEAD -> main) Update dependencies
* a0b2db3 Add user settings page
| * 1117a34 (feature) Add search tests
| * e5869f0 Fix typo in search box
| * fc19889 Add search box
|/  
* 96c4fc2 Fix header layout
* ae65976 Add login page
* 3e1ffe4 Add project skeleton
* 4114b2c Initial commit

what git printed

Saved working directory and index state WIP on main: 8c02d5b Update dependencies

What a stash is

Run git log --oneline -g refs/stash or git show stash@{0} and you'll find an ordinary commit with a message like "WIP on main: 8c02d5b Update dependencies", whose parents are HEAD and a second commit holding the index state. It isn't on any branch, which is why git log doesn't show it, but it's in the object database like everything else.

Realizing that a stash is a commit changed how I use it. I stopped worrying about "losing" stashes, since the reflog for refs/stash keeps them, and I stopped using stash for anything I'd want in a week, because a real commit on a branch with a name is easier to find than stash@{7}.

Is it safe?

Safe git-sim pre-flight

Stashes current changes; recoverable via git stash pop.

The changes are stored as commits before your working directory is reset, so stash can't lose tracked changes. The one thing to remember is that untracked files aren't included unless you add -u (Pro Git's Stashing and Cleaning chapter shows it).

Useful forms

  • git stash -u includes untracked files.
  • git stash push -m "message" names the entry.
  • git stash push <file> stashes only some files.
  • git stash list, show, pop, apply, drop manage the stack. pop has its own page.

How to undo it

git stash pop

restores the changes and removes the entry. (Staged changes come back as unstaged unless you use --index.)

Try it on your repository

pip install git-sim
git-sim stash

git-sim shows which files would move into the stash, from the working directory and the staging area.

Common questions

What does git stash do?

It saves your uncommitted changes to tracked files as a commit under refs/stash and resets your working directory and staging area to HEAD.

Does git stash include untracked files?

Not by default. git stash -u includes them, and -a includes ignored files too.

Where are stashed changes stored?

As commits in the object database, referenced from .git/refs/stash and its reflog. git stash list reads that reflog.

Can I lose a stash?

Only by dropping it and then waiting for garbage collection. Even a dropped stash's commit can be recovered by hash for a while.

Summary

In this article, we watched git stash take a staged and an unstaged change into a hidden commit while leaving an untracked file behind, and looked at what a stash entry actually is.

Next steps

git stash pop brings the changes back. If you find yourself stashing to switch branches often, git worktree add may fit better.