206

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 --all)

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?

steinybot
  • 5,491
  • 6
  • 37
  • 55
  • 79
    I had to use: `git stash show -p | git apply --3` – xmedeko Jan 17 '19 at 09:02
  • 9
    The only thing worked for me is ABOVE COMMENT!! – Mehraj Malik Jun 17 '19 at 11:30
  • 6
    Thanks @xmedeko, can anyone tell the difference between git stash show -p | git apply and git stash show -p | git apply --3 ? – Deepak Aug 21 '19 at 06:15
  • 13
    If you panic, just list your stashed files with `git stash show` and than rescue files one by one: `$ git show stash@{0}:your/stashed/file.txt > your_rescued_file.txt`. This will get file from stash and save it under a different name. Now you are safe to experiment with proper rescue methods (see below answers). If things go astray, you always have your rescued files as a last resource. – Danijel Nov 28 '19 at 09:18
  • Wow, thanks @xmedeko! Once again your comment was the only thing that worked, and it was so simple. +1! – pcdev May 06 '20 at 06:26
  • `git stash show -p` didn't show the untracked file in my case. [Erik Koopmans' answer](https://stackoverflow.com/a/55799386/2054512) helped by pointing out `git checkout stash^3 -- .` +1 – orzechow Dec 16 '21 at 16:08
  • 8
    You can use `git stash show -p -u` to show the entire stash (including untracked files) and `git stash show -p -u | git apply --3` to restore the entire stash (including untracked files) – Finn Jan 12 '22 at 16:06
  • I have the same issue. of 1000 source files, I had heavily edited 6 and created (untracked) 10. I did git stash --all, then git stash pop with no git commands or file edits between (only did a build to see if worked without my new code). It reported all 1000 files as "already exist, no checkout" and ended with "error: could not restore untracked files from stash". Looking at the resultant mess, the untracked (new) files seemed to have been restored (so error message is wrong), but none of the edits I did to the tracked files were. This is a disaster - a lot of work lost. – John Little Sep 10 '22 at 20:00

8 Answers8

219

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.

Ganesh Jadhav
  • 2,830
  • 1
  • 20
  • 32
Daniel Smith
  • 2,334
  • 1
  • 6
  • 9
  • 2
    My working tree was definitely clean although there could have been changes to ignored files. – steinybot Jul 11 '18 at 01:14
  • 1
    Looks like using `--all`/`-a` [will include ignored files](https://git-scm.com/docs/git-stash#_options), so that may be relevant. – Daniel Smith Jul 11 '18 at 01:17
  • 2
    I'm going to assume that the same logic applies to ignored files and mark this as the answer (although I think the `git merge --squash --strategy-option=theirs stash` approach is better in this case). – steinybot Jul 11 '18 at 01:19
  • Agreed, I like that second approach! Good luck with your work! – Daniel Smith Jul 11 '18 at 01:21
  • 1
    This was helpful but it didn't restore *untracked* files - if you have the same problem (`already exists, no checkout`), check my answer below. – Erik Koopmans Apr 22 '19 at 18:42
  • not clear what exacly is checked out in the command, the **last** stash, **all** the stashes? – serge Jan 21 '21 at 13:12
  • Not sure how many times I come to look for your answer – Black Mamba Sep 15 '21 at 10:01
167

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.

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

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

torek
  • 448,244
  • 59
  • 642
  • 775
  • 4
    Wow! You deserve a medal! Is there any book which you would recommend for such detailed git explanations? Awesome! ‍♂️ – Ilia Liachin Oct 18 '19 at 09:38
  • 3
    @seelts Unfortunately, Git is sort of continuously evolving, so books get out of date quickly. But Git should be understood as a set of tools—commands that manipulate a commit-ful of files, or manipulate the commit graph, or whatever—that you assemble into whatever is useful to *you*, and not too many books seem to approach it that way either. – torek Oct 18 '19 at 15:27
  • 5
    I fail to understand the solution to the problem. Is it just adding `--index`: `git stash apply --index`? – Danijel Nov 28 '19 at 09:05
  • @Danijel: No. The solution depends on the current state of your work-tree. Given a third commit in a three-commit stash—one that contains untracked files—Git cannot unstash the untracked files if the work-tree has existing files in the way of these untracked files. The files in the work-tree that cause this problem may themselves be either tracked or untracked. It is their existence in the work-tree that presents the problem. – torek Nov 28 '19 at 09:22
  • (The description gets messy because *untracked*, in general, means *present in the work-tree but not in the index*. However, the *stash* holds *three* commits, one of which represents *an* index, so suddenly we have to consider two different index states.) – torek Nov 28 '19 at 09:24
  • 3
    Thanks @torek. I first did `git stash save --all`, then I immediately did `git stash apply`, but some files were missing because I renamed them and then created again (before stashing). What helped was: `git checkout stash@{0} -- .` I won't even bother with `git checkout stash^3 -- .` because all seems OK now. It's a pitty I don't have time to really understand what was going on. Thanks. – Danijel Nov 28 '19 at 09:43
  • @torek what is the meaning of "Git cannot unstash the untracked files if the work-tree has existing files in the way of these untracked files."? – user1169587 Dec 28 '20 at 13:11
  • 1
    @user1169587: it means what it says. Maybe an example would help. Suppose the third `u` commit contains a file named `path/to/file` (with some content). Suppose further that your work-tree *already has a file named `path/to/file`* in it. Extracting the content of the `u` commit would *replace* your existing file contents with new contents, destroying whatever work you had in the file named `path/to/file`. So Git won't do that. You'll have to destroy these contents yourself first (by removing the file entirely), or move them out of harm's way (by moving the file). – torek Dec 28 '20 at 20:00
  • @torek "Extracting the content of the u commit would replace your existing file contents with new contents, destroying whatever work you had in the file named path/to/file." why git cannot merge the content of _path/to/file_ in u commit to work-tree _path/to/file_ content? because work-tree _path/to/file_ local change has not committed yet? so the error message is "Could not restore untracked files from stash entry", "untracked files" means "change but not yet committed file in working tree"? – user1169587 Dec 29 '20 at 02:39
  • The reason Git can't merge the `u` commit with your existing untracked files is that a merge operation requires *three* sets of files, not just two. See [Why is a 3-way merge advantageous over a 2-way merge?](https://stackoverflow.com/q/4129049/1256452) for background. Untracked files are, in general, not in Git at all; these extra `u` commits are an exception to that rule. – torek Dec 29 '20 at 04:15
63

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:

  • Be careful - this will overwrite everything with your stash contents!
  • Restoring the files with git checkout causes them all to become staged automatically, so I've added git reset to unstage everything.
  • Some resources use stash@{0} and stash@{0}^3, in my testing it works the same with or without @{0}

Sources:

Matthew Sielski
  • 1,107
  • 1
  • 12
  • 14
Erik Koopmans
  • 2,292
  • 2
  • 21
  • 30
  • 3
    Why would you delete the stash, why not just leave it there for a while for safety reasons? – Danijel Nov 28 '19 at 09:07
  • @Danijel Sure you could keep the stash - depends on your use case I guess. For my purposes I was done with the stash once it was restored. – Erik Koopmans Dec 01 '19 at 07:50
14

apart of other answers, I did a little trick

  • Deleted all new files (already existing files, e.g. foo.txt and bar.txt in the question)
  • git stash apply (can use any command e.g. apply, pop etc.)
Sanjay Nishad
  • 1,537
  • 14
  • 26
5

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.

Wenfang Du
  • 8,804
  • 9
  • 59
  • 90
3

I just had this issue. Since the answers above didn't work without losing files from the stash, I improvised.

My stash, made using git stash -u, had untracked newly added files. Some of these files came from code generation, and got committed separately to the master branch before I tried to pop the stash. When I did pop my stash, I got the following:

> git stash pop
web-app/src/app/rest-services/api/models/model1.ts already exists, no checkout
web-app/src/app/rest-services/api/models/model2.ts already exists, no checkout
web-app/src/app/rest-services/api/models/model3.ts already exists, no checkout
web-app/src/app/rest-services/api/models/model4.ts already exists, no checkout
error: could not restore untracked files from stash

Under the theory that git was unable to merge 2 different new files, I deleted each of the named files in my working directory. I was then able to apply the full stash without missing files or errors.

J Scott
  • 781
  • 9
  • 12
1

Here are the steps I have done to make it work:

  1. I deleted each file mentioned in the message in my working directory.
  2. I re-applied the stash as unstaged.

All files in the stash should be applied without any errors.

Kevy Granero
  • 643
  • 5
  • 17
  • I like this approach, but for me it lists around 1000 files as" already exists, no checkout" for no reason. In my stash I only had 6 changed files and 10 new (untracked files). – John Little Sep 10 '22 at 21:13
0

Once you stash your branch named as (Local Branch) with your local changes. Lets assume File A, File B and newly added File C. File C is in untrack stage while stashing the code on your branch.

Later, if one do git pull to get latest code form master and File A is updated. Afterwards, one tries to apply the stash in the branch but it wouldn't give untracked files i.e. File C because of conflict in File A as it was updated while doing pull request. This is a limitation in new version of GIT.

Workaround or solution : point your Local branch head where File A is not updated and apply stash code and now you have control of all three files convert untrack to track file or take a copy of it whatever suits you. update your branch head with git pull command and you have your own code too (can get it from stash or can paste manually if you copied)