How does git stash
treat untracked files?
Git official doc (link) defines git-stash
as
git-stash - Stash the changes in a dirty working directory away
But to me (a beginner of git), it is not very obvious what is the exact definition of "dirty working directory". Are untracked files included?
To be more precise, I notice that in a "dirty" working directory, we can have three types of dirty states:
git add
(changes are in index)git add
(changes are not in index but the file is tracked aka "Changes to be committed")git add
has never been run (the file is untracked)When we run git stash
, what would happen to #1, #2, and #3?
I am trying to provide my own answer here.
I am new to git
. But to me, the one-line describing the command should be revised to this
git-stash - Stash away the tracked changes in a dirty working directory
(untracked changes are ignored)
In other words, git stash
(without the option -u
) would stash #1 and #2 but not #3. And if #1 and #2 are happening to the same file, then #2 will take precedence over #1.
With the option -u
, git stash -u
can include also the untracked files.
See this SO article (link) for more details.
Experiment
The following experiment shows how git stash
(with -u
) handles
the three cases (a based case, file t1.txt
in history) is also included.
Set up four different states
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ echo "Hello" > t1.txt ; git add t1.txt ; git commit -m "Add t1.txt"
[f2 7367b85] Add t1.txt
1 file changed, 1 insertion(+)
create mode 100644 tmp/t1.txt
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ echo "Hello in Index" > t1.txt ; git add t1.txt
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ echo "Hello in Working Directory (tracked)" > t1.txt
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ echo "Bonjour in Working Directory (untracked)" > t2.txt
Examine the four states
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ git status
On branch f2
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: t1.txt
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: t1.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
t2.txt
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ git diff --cached t1.txt
diff --git a/tmp/t1.txt b/tmp/t1.txt
index e965047..a863c48 100644
--- a/tmp/t1.txt
+++ b/tmp/t1.txt
@@ -1 +1 @@
-Hello
+Hello in Index
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ git diff t1.txt
diff --git a/tmp/t1.txt b/tmp/t1.txt
index a863c48..a0c6962 100644
--- a/tmp/t1.txt
+++ b/tmp/t1.txt
@@ -1 +1 @@
-Hello in Index
+Hello in Working Directory (tracked)
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ cat t2.txt
Bonjour in Working Directory (untracked)
Now perform the stash
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ git stash
Saved working directory and index state WIP on f2: 7367b85 Add t1.txt
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ git status
On branch f2
Untracked files:
(use "git add <file>..." to include in what will be committed)
t2.txt
nothing added to commit but untracked files present (use "git add" to track)
Note that t2.txt was not handled by git stash
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ git stash show
tmp/t1.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
If you lost t2.txt; git stash won't be able to restore it for you
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ rm t2.txt
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ git stash pop
On branch f2
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: t1.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (6a004ffe723ae18d9d5a314c0f5460622b27e300)
Index vs Tracked (Changes not staged for commit)
By the way, note also that what happened to the state of
#1 "Hello in Index" (of t1.txt in index before `git stash`)
#2 "Hello in Working Directory (tracked)" (of t1.txt in worktree before `git stash`)
#1
was also lost after a pair of git stash
and git stash pop
operations.
What was retained was #2
. In other words, the working directory state has taken precedence in the stash operation.
MINGW64 ~/proj/tmp/sandbox/tmp (f2)
$ cat t1.txt
Hello in Working Directory (tracked)