I had a bunch of staged and unstaged changes and I wanted to quickly switch to another branch and then switch back.
So I staged my changes using:
$ git stash push -a
(In hindsight I probably could have used
--include-untracked instead of
Then when I went to pop the stash I get a whole lot of errors along the lines of:
$ git stash pop foo.txt already exists, no checkout bar.txt already exists, no checkout ... Could not restore untracked files from stash entry
There doesn’t seem to be any changes restored from the stash.
I also tried
$ git stash branch temp but that shows the same errors.
I did figure out a way around this which was to use:
$ git stash show -p | git apply
Disaster averted for now but this raises some questions.
Why did this error happen in the first place and how do I avoid it next time?
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
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.
What’s inside a stash
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
...--o--o--o <-- branch (HEAD) |\ i-w <-- stash
If you add
--all, Git makes an extra commit,
u, in between making
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).
Restoring a stash
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
git stash apply completely ignores the
If the stash has only two commits,
git stash apply can now apply the
w commit. It does this by calling
git merge2 (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
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. 🙂
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
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.
Answered By – torek
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0