git stash is often described as "temporarily saving your work," but under the hood it's much closer to creating commits that aren't attached to a branch and keeping references to them in a special location.
Here's what actually happens.
The basic idea
Suppose your working tree looks like this:
HEAD (last commit)
|
v
A---B---C <-- main
\
(working directory changes)
When you run:
git stash
Git:
- Records your current changes as commit objects.
- Resets your working tree back to
HEAD.
- Stores a reference to those commits in a special reference called
refs/stash.
Your branch doesn't move:
A---B---C <-- main
\
S <-- refs/stash
What's inside a stash?
A stash is not just a patch.
It's actually one or more real Git commits.
Typically, a stash consists of:
- one commit representing the working tree
- one commit representing the index (staged changes)
- optionally another commit for untracked files (
git stash -u)
You can inspect one:
git stash list
stash@{0}: WIP on main: abc123 Add login page
Then:
git show stash@{0}
or
git cat-file -p stash@{0}
You'll notice it's a genuine commit object.
Where is it stored?
The stash reference lives here:
refs/stash
You can see it:
git rev-parse refs/stash
or
git log refs/stash
Internally, Git stores the actual objects in the normal object database:
.git/
objects/
So stashes live alongside every other commit, tree, and blob.
Nothing special about the storage format.
What gets saved?
Assume:
file1 modified and staged
file2 modified but unstaged
file3 untracked
Running:
git stash
saves
- ✅ staged changes
- ✅ unstaged changes
- ❌ not untracked files
Running
git stash -u
also saves
Running
git stash -a
also saves ignored files.
Why does the working tree become clean?
After creating the stash commit(s), Git effectively checks out the current HEAD again.
So
Before:
HEAD
+ staged changes
+ unstaged changes
becomes
HEAD
Your files match the last commit.
Why is stash fast?
Because Git is content-addressed.
When you stash:
- blobs are created only for changed files
- trees are created
- commit objects are created
Unchanged files are simply referenced.
If only one file changed in a huge repository, Git only writes objects related to that file plus a few metadata objects.
What happens when you git stash pop?
Git:
- Reads the stash commit.
- Applies the saved differences onto your current working tree.
- Attempts to restore staged vs. unstaged state.
- Deletes the stash reference if the apply succeeded.
If there are conflicts, the stash is usually kept so you can resolve them and try again.
git stash apply performs the same restore but does not remove the stash.
Are stashes permanent?
Not necessarily.
The reference
refs/stash
keeps them alive.
If you run
git stash drop
or
git stash clear
the reference is removed.
The underlying commit objects may still exist temporarily as unreachable objects until Git's garbage collection (git gc) eventually prunes them according to its retention policy. Until then, they can sometimes be recovered using commands like:
git fsck --lost-found
or by inspecting unreachable commits.
You can verify this yourself
Run:
echo hello > test.txt
git add test.txt
echo world >> test.txt
git stash
Now inspect the stash:
git log --graph --decorate refs/stash
or:
git cat-file -p stash@{0}
You'll see that the stash is a normal commit object with parent references, not a custom "stash file."
In summary
Under the hood, git stash is essentially:
- Creating one or more regular Git commit objects that capture your working directory, index, and optionally untracked files.
- Storing a pointer to those commits in the special reference
refs/stash.
- Resetting your working tree and index back to
HEAD.
Because it's built on Git's standard object model (blobs, trees, commits, and refs), a stash integrates naturally with the rest of Git's storage and can even be inspected with low-level plumbing commands like git cat-file, git ls-tree, and git log.