Skip to content
Eric Bouchut edited this page Jul 21, 2021 · 17 revisions

Keep files aside (stash)

Say you want to set aside your current work in order to fix a bug, or jump on another branch temporarily then come back to what you were doing later on. The stash git subcommand offers you a way to keep aside the files of the working area.

In the example below, my repository is dirty. There are two files that I'm currently working on README.md (tracked ie. under version control) and LICENSE.md (which is fresh new, not under version control that is an untracked file).

git status

On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	LICENSE.md

By default git stash only sets aside the tracked files like README.md, but not the files that have never been committed yet like LICENSE.md.

git stash

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.md

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.md) I need to use the -u flag, 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

What is in a stash?

Let's figure out what is in a stash.

git show --pretty=raw 'stash@{0}'

commit 117aaf20731f00690333ea9153c40f9b21d27614
tree 43508b511248e29895e0e4b285f1bcb3dd882e94
parent aeb70d42b12bcf1a60fd050ab737f4e826e7c0fc
parent fb98a74bf5d4e3772aae0eef82757935882af9d1
parent 7528cdcf038ee887b6a9a8fa39880ec24617dc2f
author    Eric Bouchut <ebouchut@gmail.com> 1549981976 +0100
committer Eric Bouchut <ebouchut@gmail.com> 1549981976 +0100

    WIP on bugfix/feature-z: aeb70d42 Merge branch 'release/1.10.5' into develop

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.

A stash commit is a merge commit with 2 or 3 parents (See the output of the above git show command above):

  • stash@{0}^1 denotes the first parent of the stash commit (S).
    This was the current commit (HEAD) at the time of the stash.
  • stash@{0}^2 denotes the second parent of the stash commit (I).
    It is a commit with 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 ad with git add are stored before they can be committed.
  • stash@{0}^3 denotes the third parent (U) This commit contains the untracked files present in the working tree at the time of the stash. git stash creates it only when you use the -u option.
    An untracked file is a file git does not know yet. It is not under version control, hence not in the Index, nor in the repository.

The stash commit stash@{0} (W) is a merge commit that also contains the tracked files of the working dir that were modified at the time of the stash.

                    o     stash@{0}  
                   /|\    (W: stash commit = merge + changed tracked files in the working dir)
                  / | \   117aaf2
                 /  |  \
stash@{0}^3     o   |   o stash@{0}^2  (I: Index state)
(U: Untracked files)|   | fb98a74
 7528cdc            |  /
                    | /      
               HEAD o    stash@{0}^1 (S: Current Commit at the time of the stash)
                         aeb70d4

Why do we need to dive deep into the inner workings of the system?

Because 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 what is the internals of git stash to do this. You are now ready to understand the next paragraphs.

List untracked files in a stash

Here is how to list the name of untracked files in a stash commit.

From git version 2.32 onwards git show now has the --only-untracked option to list the untracked files of a stash.

git stash show --only-untracked --name-only 'stash@{0}'

Before git version 2.32, we can use 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

Content of untracked files in a stash

Here is how to view the content of the untracked files in a stash commit.

From git version 2.32 onwards you can use the --only-untracked option of git show.

git stash show --only-untracked 'stash@{0}'

Before git version 2.32, we can use instead one of the following 2 alternatives:

git show 'stash@{0}^3'
git log -p 'stash@{0}^3'