I wrote a blog post Git Stash Internals with my understanding of this topic.
I share the gist of it below, hoping this may help you and others.
Feedback is welcome.
git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: CONTRIBUTING.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
LICENSE
There are 3 sections:
Changes to be committed
denotes the content of the
Index.(CONTRIBUTING.md
)
Changes not staged for commit
denotes the tracked files that are
modified in the Working Dir.(README.md
)
Untracked files
denotes the files git does not know about yet (LICENSE
)
In this example, I assume that there is only one stash created and I use stash@{0}
to reference it.
The table below describes what files are stashed and where (commit) depending on
which git stash command is used.
Git Stash Command |
Modified Tracked File (Working Dir) |
Index |
Untracked File |
Ignored |
|
README.md |
CONTRIBUTING.md |
LICENSE |
temp/stash.out |
git stash |
☑️ stash@{0} |
☑️ stash@{0}^2 |
⤬ |
⤬ |
git stash -u |
☑️ stash@{0} |
☑️ stash@{0}^2 |
☑️ stash@{0}^3 |
⤬ |
git stash -a |
☑️ stash@{0} |
☑️ stash@{0}^2 |
☑️ stash@{0}^3 |
☑️ stash@{0}^3 |
Now, let's take a look at what each git stash
command does.
git stash
By default, git stash
sets aside:
- any tracked file that is modified and not ignored:
README.md
- the Index:
CONTRIBUTING.md
It does not set aside files that are either untracked or ignored
like respectively LICENSE
and temp/stash.out
.
git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
LICENSE
nothing added to commit but untracked files present (use "git add" to track)
If instead, I want to stash both tracked and untracked files,
that is all my current work in progress (both README.md
and LICENSE
)
plus the content of the index, (CONTRIBUTING.md
), then I need to use git stash -u
, like so:
git stash -u
git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
To also stash the ignored files, you can use git stash -a
instead.
Stash Stack
When adding a stash, git creates a stash commit, pushes it on top of the stash stack.
This shifts existing stash entries downwards (if any).
The reference stash@{0}
always denotes the top of the stash stack.
Each time you stash something else it is pushed downwards, hence:
stash@{0}
denotes the most recent stash created,
stash@{1}
denotes the second to last stash created,
stash@{2}
denotes the third to last stash created, and so on.
Now that we know what is stashed, let's take a look at the way it is stored internally.
Now that we have set the scene, let's take at a look at how things are working under the hood.
What is in a stash?
Let's figure out what is in our most recent stash.
This section assumes we ran git stash -u
in the example's repository so we end up with this log.

Now let's take a look at the stash@{0} commit.
git log --format=raw -1 stash@{0}
commit 49482afa4ab999deada67c65dc5d38be89aed867
tree 936c8b08ac5a8e91bb6cc38387d2cca93167e0ae
parent 031ca106c13b1603675ea1ce8da8b3da852e27cd
parent b558b9e7621fe508c7c18713cd62c78e80e2017e
parent dfac0d769262fa4b8ea40003d24052c4509a7f3a
author Eric Bouchut <ebouchut@gmail.com> 1627056522 +0200
committer Eric Bouchut <ebouchut@gmail.com> 1627056522 +0200
WIP on master: 031ca10 Add README
Do note that the parents of the stash commit are listed in order (first (031ca10
), then second (b558b9e
) then third (dfac0d7
)).

The stash commit stash@{0}
(49482a
) is a merge commit with 3 parents in this case because we stashed the untracked files, (2 parents by default).
It also contains the non ignored files of the working dir that were modified at the time of the stash.
Let's meet the parents:
stash@{0}^1
(031ca10
) denotes the first parent of the stash commit.
This was the current commit (HEAD
) at the time of the stash.
stash@{0}^2
(b558b9e
) denotes the second parent of the stash commit.
It contains the changesets present in the Index at the time of the stash.
The Index is aka. as the staging area. This is where the files you add with git add
are stored before they can be committed.
stash@{0}^3
(dfac0d7
) denotes the third parent of the stash commit.
It contains the untracked files (-u
) and ignored files (-a
)
present in the working tree at the time of the stash.
git stash
creates it only when you use any of the -u
or -a
options.
Why do we need to dive deep into the inner workings of git stash
?
Up until version 2.32, git did not offer a simple way to list and show the untracked files in a stash commit.
This is why we need to know the git stash
internals to do this.
You are now ready to understand what is next.
Modified Files in the Working Dir of a Stash Commit
Here is how to list modified files in the Working Dir of the most recent stash commit:
git log -m --first-parent -1 --format='' --name-only 'stash@{0}'
Here we drill down on the merge commit (-m
) and focus only on the first commit
(-1
) of the first parent (--first-parent
), that is the stash commit itself.
ℹ️ By default, git log does not display details about any parent of a merge
commit, unless we use -m
and when we do use this option, it displays what is requested for each and every parent. As this is not what we want here, we restrict only to the first parent.
For whatever reason, even with --name-only
, git log
displays non requested information (commit SHA1, date, and author) in addition
to the file names. I noticed this issue in git version 2.32.0.
This is why I use --format=''
as a workaround to remove them.
Now, here is how to view what changed in the modified files of the Working Dir of the most recent stash commit:
git log -m --first-parent -1 -p 'stash@{0}'
# Stashed Files of a Stash Commit
The command below **lists** the **staged files** of the most recent stash commit.
```lang-shell
git log --name-only -1 --format='' 'stash@{0}^2'
In order to get the content of the (changesets in the) Index
in this stash commit:
git log -1 -p 'stash@{0}^2'
Untracked Files of a Stash Commit
Here is how to list the untracked files in the most recent stash commit.
From git version 2.32
onwards git show
now has the --only-untracked
option to list the untracked
files of a stash.
ℹ️
This also lists the ignored files if you used git stash -a
to also stash the ignored files.
git stash show --only-untracked --name-only 'stash@{0}'
Before git version 2.32, we should have used instead one of the following 2 alternatives:
git show --name-only 'stash@{0}^3:'
Please note the colon sign (:
) at the end.
git ls-tree -r 'stash@{0}^3' --name-only
Here is how to view the content of the untracked files (and ignored file(s)
if any) in the most recent stash commit.
From git version 2.32 onwards you can use the --only-untracked
option of git show
.
git stash show --only-untracked -p 'stash@{0}'
Before git version 2.32, use instead:
git log -p 'stash@{0}^3'