为什么git stash pop表示无法从存储条目恢复未跟踪的文件?

时间:2018-07-11 00:54:16

标签: git git-stash

我进行了许多分段和非分段更改,我想快速切换到另一个分支然后再切换回。

因此,我使用以下命令进行了更改:

$ git stash push -a

(事后看来,我本可以使用--include-untracked代替--all

然后,当我弹出储藏柜时,遇到了很多错误:

$ git stash pop
foo.txt already exists, no checkout
bar.txt already exists, no checkout
...
Could not restore untracked files from stash entry

似乎没有从存储中恢复任何更改。

我也尝试了$ git stash branch temp,但是显示了相同的错误。

我确实找到了解决该问题的方法:

$ git stash show -p | git apply

目前已经避免了灾难,但这引发了一些问题。

为什么首先会发生此错误,下次如何避免呢?

5 个答案:

答案 0 :(得分:21)

我设法重新创建了您的问题。看来,如果您隐藏未跟踪的文件然后创建这些文件(在您的示例中为foo.txtbar.txt),则您对未跟踪的文件进行了本地更改,当您应用{{1}时,这些更改将被覆盖}。

要解决此问题,可以使用以下命令:

git stash pop

这将覆盖所有未保存的本地更改,因此请当心。 Here is some further information I found on the previous command

答案 1 :(得分:13)

作为其他说明,请注意git stash进行两次提交或三次提交。默认值为两个;如果您使用--all--include-untracked选项的任何拼写,您将获得三个。

这两个或三个提交在一种重要的方式上很特殊:它们位于 no 分支上。 Git通过特殊名称stash定位它们。 1 不过,最重要的是Git可以让您-和制造来使用这两个或三个提交。要了解这一点,我们需要查看这些提交中的内容。

藏在里面的东西

每个提交都可以列出一个或多个提交。它们形成一个图,以后的提交指向以前的提交。存储区通常包含两个提交,对于索引/暂存区域的内容,我喜欢调用i,对于工作树的内容,我喜欢调用w。还要记住,每个提交都包含一个快照。在正常提交中,此快照是从索引/暂存区内容中 创建的。因此,i提交实际上是完全正常的提交!它只是不在任何分支上:

...--o--o--o   <-- branch (HEAD)
           |
           i

如果您要进行常规存储,则git stash代码现在可以通过将所有跟踪的工作树文件复制到临时辅助索引中来制作w。 Git将此w提交的第一个父节点设置为指向HEAD提交,第二个父节点将指向提交i。最后,它设置stash指向此w提交:

...--o--o--o   <-- branch (HEAD)
           |\
           i-w   <-- stash

如果添加--include-untracked--all,则Git会在创建ui之间进行一次额外的提交wu的快照内容是那些未跟踪但未被忽略的文件(--include-untracked),或者是即使被忽略也未被跟踪的文件(--all)。这笔额外的u提交没有父项,然后git stash成为w时,它设置了w第三 u提交的父级,这样您就可以得到:

...--o--o--o   <-- branch (HEAD)
           |\
           i-w   <-- stash
            /
           u

此时,Git还删除u提交中结束的所有工作树文件(使用git clean来完成)。

恢复隐藏状态

当您要恢复存储时,可以选择使用--index,也可以不使用它。这告诉git stash apply(或内部使用apply的任何命令,例如pop)应使用 i进行尝试修改当前索引。此修改是通过以下方式完成的:

git diff <hash-of-i> <hash-of-i's-parent> | git apply --index

(或多或少;这里有很多细微的细节妨碍了基本思想)。

如果省略--index,则git stash apply将完全忽略i的提交。

如果存储区只有两个提交,则git stash apply现在可以应用w提交。它通过使用进行存储的原始提交(git merge调用i 2 (不允许将其提交或将结果视为普通合并)来完成此操作的父级,w的第一个父级)作为合并基础,w作为--theirs的提交,而您当前的(HEAD)提交作为合并的目标。如果合并成功,那么一切都很好-至少 Git 如此认为-并且git stash apply本身也会成功。如果您使用git stash pop来应用存储,代码现在会删除存储。 3 如果合并失败,Git会声明应用失败。如果您使用的是git stash pop,则该代码将保留存储并提供与git stash apply相同的故障状态。

但是如果您有 third 次提交-如果您要应用的存储中有u个提交,那么情况就变了! 无法假装u提交不存在。 4 Git坚持要从{em> 提取所有文件。 {1}}提交到当前工作树中。这意味着文件要么根本不存在,要么与u提交中的内容相同。

要实现这一目标,您可以自己使用u,但是请记住,未跟踪的文件(是否被忽略)在Git存储库中没有其他存在,因此请确保这些文件都可以被销毁!或者,您可以创建一个临时目录,然后将文件移动到该目录中以进行保管,甚至可以移动另一个git cleangit stash save -u,因为这些文件将为您运行git stash save -a。但这只会给您留下另一种git clean样式的存储,以便以后处理。


1 这实际上是u。如果您创建一个名为refs/stash的分支,这很重要:该分支的全名是stash,所以它们没有冲突。但是不要那样做: Git 不会介意,但是您会混淆自己。 :-)

2 refs/heads/stash代码实际上直接在这里使用git stash。出于多种原因,这是必要的,并且还具有确保解决冲突和提交时Git不会将其视为合并的副作用。

3 这就是为什么我建议避免使用git merge-recursive而不推荐git stash pop的原因。您将有机会查看已应用的内容,并确定它是否实际上正确地应用了。如果没有,您将保持藏匿状态,这意味着您可以使用git stash apply来完美恢复一切。好吧,假设缺少令人讨厌的git stash branch提交。

4 确实应该有:u之类的东西。还应该有一个变体,意味着将所有git stash apply --skip-untracked提交文件都拖放到新目录中,例如,u

答案 2 :(得分:3)

要在Daniel Smith's answer上进行扩展:即使您在创建存储区时使用了--include-untracked(或-u),该代码也只还原了 tracked 文件。所需的完整代码是:

git checkout stash -- .
git checkout stash^3 -- .
git stash drop

# Optional to unstage the changes (auto-staged by default).
git reset

这将完全还原跟踪的内容(在stash中和未跟踪的内容(在stash^3中),然后删除存储。一些注意事项:

  • 注意-这会用您的隐藏内容覆盖所有内容!
  • 使用git checkout恢复文件会导致所有文件自动转储,因此我添加了git reset来撤消所有文件。
  • 某些资源使用stash@{0}stash@{0}^3,在我的测试中,无论是否使用@{0},它的工作原理都是相同的

来源:

答案 3 :(得分:0)

除了其他答案之外,我还做了一些技巧

  • 删除所有新文件(已存在的文件,例如问题中的foo.txt和bar.txt)
  • git stash apply(可以使用任何命令,例如apply,pop等)

答案 4 :(得分:0)

我刚刚遇到了这个问题。由于上述答案在不丢失存储中的文件的情况下不起作用,因此我即兴创作。

我使用 git stash -u 创建的存储库没有跟踪新添加的文件。其中一些文件来自代码生成,并在我尝试弹出存储之前单独提交到主分支。当我确实弹出我的藏品时,我得到了以下信息:

> 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

根据 git 无法合并 2 个不同的新文件的理论,我删除了工作目录中的每个命名文件。然后,我能够应用完整的存储,而不会丢失文件或错误。