为什么 git stash pop 说它无法从 stash 条目中恢复未跟踪的文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/51275777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does git stash pop say that it could not restore untracked files from stash entry?
提问by steinybot
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
)
(事后看来,我可能可以使用--include-untracked
而不是--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.
我也尝试过,$ git stash branch temp
但显示相同的错误。
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?
为什么首先会发生此错误,下次我该如何避免?
回答by Daniel Smith
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
.
我设法重现了您的问题。似乎如果您存储未跟踪的文件,然后创建这些文件(在您的示例中,foo.txt
和bar.txt
),那么您对未跟踪的文件进行了本地更改,这些更改将在您应用git stash pop
.
To get around this issue, you can use the following command:
要解决此问题,您可以使用以下命令:
git checkout stash -- .
This will override any unsaved local changes so be careful. Here is some further information I found on the previous command.
这将覆盖任何未保存的本地更改,所以要小心。这是我在上一个命令中找到的一些进一步信息。
回答by torek
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.
作为一些额外的解释,请注意git stash
进行两次提交或三次提交。默认为两个;如果您使用--all
或--include-untracked
选项的任何拼写,您会得到三个。
These two, or three, commits are special in one important way: they are on nobranch. Git locates them through the special name stash
.1The most important thing, though, is what Git lets you—and makesyou—do with these two or three commits. To understand this we need to look at what's in those commits.
这两个或三个提交在一个重要方面是特殊的:它们不在分支上。Git 通过特殊名称来定位它们stash
。1不过,最重要的是 Git 允许你——并且让你——用这两个或三个提交来做什么。要理解这一点,我们需要查看这些提交中的内容。
What's inside a stash
储藏室里有什么
Every commit can list one or more parentcommits. 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 fromthe index / staging-area contents. So the i
commit is in fact a perfectly normal commit! It's just not on any branch:
每个提交都可以列出一个或多个父提交。这些形成了一个图表,之后的提交指向更早的提交。存储通常包含两个提交,我喜欢i
为索引/暂存区内容和w
工作树内容调用它们。还要记住,每个提交都保存一个快照。在一个正常的提交,该快照是由从索引/分段区域的内容。所以i
提交实际上是一个完全正常的提交!它不在任何分支上:
...--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:
如果您正在制作一个普通的 stash,git stash
代码w
现在通过复制您所有跟踪的工作树文件(到临时辅助索引中)来制作。Git 将此w
提交的第一个父项设置为指向该HEAD
提交,并将第二个父项设置为指向 commit i
。最后,它设置stash
指向这个w
提交:
...--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 noparent, and then when git stash
makes w
, it sets w
's thirdparent to this u
commit, so that you get:
如果添加--include-untracked
或--all
,Git 会u
在 makei
和之间进行额外的提交w
。快照内容u
是那些未被跟踪但未被忽略的--include-untracked
文件(),或者即使它们被忽略也未被跟踪的文件(--all
)。这种额外的u
承诺有没有父母,然后当git stash
品牌w
,它设置w
的第三父母这个u
承诺,让你得到:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
Git also, at this point, removesany work-tree files that wound up in the u
commit (using git clean
to do that).
此时,Git 还会删除u
提交中结束的任何工作树文件(git clean
用于执行此操作)。
Restoring a stash
恢复存储
When you go to restorea 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 usethe i
commit to attempt to modify your current index. This modification is done with:
当您要恢复存储时,您可以选择使用--index
或不使用它。这告诉git stash apply
(或任何内部使用的命令apply
,如pop
),它应该使用的i
承诺,试图修改当前的指数。此修改是通过以下方式完成的:
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.
如果省略--index
,则git stash apply
完全忽略i
提交。
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 Gitthinks so—and the git stash apply
itself succeeds. If you used git stash pop
to apply the stash, the code now dropsthe stash.3If 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
.
如果存储只有两次提交,git stash apply
现在可以应用w
提交。它通过调用git merge
2(不允许它提交或将结果视为正常合并)来做到这一点,使用进行存储的原始提交(i
的父级和w
第一个父级)作为合并基础,w
作为--theirs
提交,并将您当前的 (HEAD) 提交作为合并的目标。如果合并成功,一切都很好——好吧,至少Git是这么认为的——而且它git stash apply
本身也成功了。如果您曾经git stash pop
应用过存储,代码现在会删除存储。3如果合并失败,Git 会声明申请失败。如果你使用git stash pop
,代码保留存储并提供与 相同的失败状态git stash apply
。
But if you have that thirdcommit—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.4Git insists on extracting all the files fromthat 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.
但是如果你有第三次提交——如果u
你正在申请的存储中有一个提交——那么事情就会改变! 没有选项可以假装u
提交不存在。4混帐坚持提取的所有文件从该u
承诺,到当前的工作树。这意味着这些文件要么根本不存在,要么与u
提交中的内容相同。
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.
要实现这一点,您可以使用git clean
自己——但请记住,未跟踪的文件(无论是否被忽略)在 Git 存储库中都不存在,因此请确保这些文件都可以被销毁!或者,您可以创建一个临时目录,然后将文件移到那里以进行妥善保管——或者甚至执行另一个git stash save -u
或git stash save -a
,因为它们会git clean
为您运行。但这只会给你留下另一种u
风格的藏匿处,以便稍后处理。
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: Gitwon't mind, but you will confuse yourself. :-)
1这其实是refs/stash
。如果您创建一个名为stash
的分支refs/heads/stash
,这很重要:分支的全名是,因此它们不会发生冲突。但不要那样做:Git不会介意,但你会迷惑自己。:-)
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.
2的git stash
代码实际上使用git merge-recursive
直接在这里。出于多种原因,这是必要的,并且还具有确保 Git 在您解决冲突和提交时不会将其视为合并的副作用。
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 actuallyapplied correctly. If not, you still have your stashwhich means you can use git stash branch
to recover everything perfectly. Well, assuming the lack of that pesky u
commit.
3这就是为什么我建议避免git stash pop
使用git stash apply
. 您有机会查看所应用的内容,并确定它是否实际正确应用。如果没有,你仍然有你的藏匿处,这意味着你可以用来git stash branch
完美地恢复一切。好吧,假设没有那个讨厌的u
提交。
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.
4真的应该有:git stash apply --skip-untracked
或某事。还应该有一个变体,这意味着将所有这些u
提交文件放到一个新目录中,例如git stash apply --untracked-into <dir>
,也许。
回答by Erik Koopmans
To expand on Daniel Smith's answer: that code only restores the trackedfiles, even if you used --include-untracked
(or -u
) when creating the stash. The full code required is:
扩展丹尼尔史密斯的答案:该代码仅恢复跟踪的文件,即使您在创建存储时使用了--include-untracked
(或-u
)。所需的完整代码是:
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:
这将完全恢复跟踪的内容(in stash
)和未跟踪的内容(in stash^3
),然后删除存储。一些注意事项:
- 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 addedgit reset
to unstage everything. - Some resources use
stash@{0}
andstash@{0}^3
, in my testing it works the same with or without@{0}
- 小心- 这会用您的藏匿内容覆盖所有内容!
- 恢复文件
git checkout
会导致它们全部自动暂存,所以我添加git reset
了取消暂存所有内容。 - 一些资源使用
stash@{0}
andstash@{0}^3
,在我的测试中,无论有没有@{0}
Sources:
资料来源:
- Force git stash to overwrite added files(linked in Daniel Smith's answer)
- How can I checkout an untracked file in a git stash?(info about the magic
stash^3
commit) - Atlassian guide to git stash(check "How git stash works" for details on the internal commits)
- 强制 git stash 覆盖添加的文件(链接在 Daniel Smith 的回答中)
- 如何在 git stash 中检出未跟踪的文件?(有关魔术
stash^3
提交的信息) - Atlassian git stash 指南(有关内部提交的详细信息,请查看“git stash 的工作原理”)
回答by Sanjay Nishad
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.)
- 删除了所有新文件(已经存在的文件,例如问题中的 foo.txt 和 bar.txt)
git stash apply
(可以使用任何命令,例如 apply、pop 等)