Retrieve the Stashed File Here, the “ls” command is used to show the list of files and folder of the repository folder, and the “git stash apply” command is used to restore the untracked files.
Stashing untracked or ignored files By default, running git stash will stash: changes that have been added to your index (staged changes) changes made to files that are currently tracked by Git (unstaged changes)
Think of the git stash pop command as a two-step process. It pulls the most recent stash from history, makes the appropriate changes to files in the local workspace and then deletes that entry from the stash history.
Stash entries can be made with untracked files via git stash push --include-untracked (man). However, because the untracked files are stored in the third parent of the stash entry and not the stash entry itself, running git stash show (man) does not include the untracked files as part of the diff.
I managed to recreate your issue. It seems if you stash untracked files and then you create those files (in your example, foo.txt
and bar.txt
), then you have local changes to untracked files that would be overwritten when you apply git stash pop
.
To get around this issue, you can use the following command. This will override any unsaved local changes so be careful.
git checkout stash -- .
Here is some further information I found on the previous command.
As a bit of additional explanation, note that git stash
makes either two commits, or three commits. The default is two; you get three if you use any spelling of the --all
or --include-untracked
options.
These two, or three, commits are special in one important way: they are on no branch. Git locates them through the special name stash
.1 The most important thing, though, is what Git lets you—and makes you—do with these two or three commits. To understand this we need to look at what's in those commits.
Every commit can list one or more parent commits. These form a graph, where later commits point back to earlier ones. The stash normally holds two commits, which I like to call i
for the index / staging-area contents, and w
for the work-tree contents. Remember also that each commit holds a snapshot. In a normal commit, this snapshot is made from the index / staging-area contents. So the i
commit is in fact a perfectly normal commit! It's just not on any branch:
...--o--o--o <-- branch (HEAD)
|
i
If you're making a normal stash, the git stash
code makes w
now by copying all your tracked work-tree files (into a temporary auxiliary index). Git sets the first parent of this w
commit to point to the HEAD
commit, and the second parent to point to commit i
. Last, it sets stash
to point to this w
commit:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
If you add --include-untracked
or --all
, Git makes an extra commit, u
, in between making i
and w
. The snapshot contents for u
are those files that are untracked but not ignored (--include-untracked
), or files that are untracked even if they are ignored (--all
). This extra u
commit has no parent, and then when git stash
makes w
, it sets w
's third parent to this u
commit, so that you get:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
Git also, at this point, removes any work-tree files that wound up in the u
commit (using git clean
to do that).
When you go to restore a stash, you have the option of using --index
, or not using it. This tells git stash apply
(or any of the commands that internally use apply
, such as pop
) that it should use the i
commit to attempt to modify your current index. This modification is done with:
git diff <hash-of-i> <hash-of-i's-parent> | git apply --index
(more or less; there are a bunch of nitty details that get in the way of the basic idea here).
If you omit --index
, git stash apply
completely ignores the i
commit.
If the stash has only two commits, git stash apply
can now apply the w
commit. It does this by calling git merge
2 (without allowing it to commit or treat the result as a normal merge), using the original commit on which the stash was made (i
's parent, and w
's first parent) as the merge base, w
as the --theirs
commit, and your current (HEAD) commit as the target of the merge. If the merge succeeds, all is good—well, at least Git thinks so—and the git stash apply
itself succeeds. If you used git stash pop
to apply the stash, the code now drops the stash.3 If the merge fails, Git declares the apply to have failed. If you used git stash pop
, the code retains the stash and delivers the same failure status as for git stash apply
.
But if you have that third commit—if there is a u
commit in the stash you are applying—then things change! There is no option to pretend that the u
commit does not exist.4 Git insists on extracting all the files from that u
commit, into the current work-tree. This means the files must either not exist at all, or have the same contents as in the u
commit.
To make that happen, you can use git clean
yourself—but remember that untracked files (ignored or not) have no other existence inside a Git repository, so be sure these files can all be destroyed! Or, you can make a temporary directory, and move the files there for safekeeping—or even do another git stash save -u
or git stash save -a
, since those will run git clean
for you. But that just leaves you with another u
-style stash to deal with later.
1This is in fact refs/stash
. This matters if you make a branch named stash
: the branch's full name is refs/heads/stash
, so these are not in conflict. But don't do that: Git won't mind, but you will confuse yourself. :-)
2The git stash
code actually uses git merge-recursive
directly here. This is necessary for multiple reasons, and also has the side effect of making sure Git does not treat it as a merge when you resolve conflicts and commit.
3This is why I recommend avoiding git stash pop
, in favor of git stash apply
. You get a chance to review what got applied, and decide whether it was actually applied correctly. If not, you still have your stash which means you can use git stash branch
to recover everything perfectly. Well, assuming the lack of that pesky u
commit.
4There really should be: git stash apply --skip-untracked
or something. There should also be a variant that means drop all those u
commit files into a new directory, e.g., git stash apply --untracked-into <dir>
, perhaps.
To expand on Daniel Smith's answer: that code only restores the tracked files, even if you used --include-untracked
(or -u
) when creating the stash. The full code required is:
git checkout stash -- .
git checkout stash^3 -- .
git stash drop
# Optional to unstage the changes (auto-staged by default).
git reset
This fully restores the tracked contents (in stash
) and untracked contents (in stash^3
), then deletes the stash. A few notes:
git checkout
causes them all to become staged automatically, so I've added git reset
to unstage everything.stash@{0}
and stash@{0}^3
, in my testing it works the same with or without @{0}
Sources:
stash^3
commit)apart of other answers, I did a little trick
git stash apply
(can use any command e.g. apply, pop etc.)An easy way to get past this issue is to rename the existing conflicting file to a non-conflicting one.
For example, if you run git stash apply/pop
and get:
foo.md already exists, no checkout
error: could not restore untracked files from stash
Try rename foo.md
to foo.new.md
and rerun git stash apply/pop
, you won't get the error this time and will have both foo.md
and foo.new.md
at your disposal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With