为什么git stash pop说它无法从stash entry恢复未跟踪的文件?

mpgws1up  于 2023-01-19  发布在  Git
关注(0)|答案(8)|浏览(358)

我有一堆临时的和非临时的更改,我想快速切换到另一个分支,然后切换回来。
因此,我使用以下工具来暂存我的更改:

$ git stash push -a

(In事后看来,我可能本可以使用--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

灾难暂时避免了,但这引发了一些问题。
为什么这个错误会在第一时间发生,我如何避免它的下一次?

sxpgvts3

sxpgvts31#

我设法重新创建了您的问题。似乎如果您隐藏未跟踪的文件,然后创建这些文件(在您的示例中,foo.txtbar.txt),那么您对未跟踪文件的本地更改将在您应用git stash pop时被覆盖。
要解决此问题,您可以使用以下命令。这将覆盖任何未保存的本地更改,因此要小心。

git checkout stash -- .

Here is some further information I found on the previous command .

oalqel3c

oalqel3c2#

作为一点额外的解释,请注意git stash可以提交两次,也可以提交三次。如果您使用--all--include-untracked选项的任何拼写,则会得到三个。
这两个或三个提交在一个重要方面是特别的:它们位于 no 分支上。Git通过特殊的名称stash来定位它们。1然而,最重要的是Git允许你--以及 * 使 * 你--对这两个或三个提交做什么。为了理解这一点,我们需要看看这些提交中有什么。

藏起来的东西

每个提交可以列出一个或多个 parent 提交。它们形成了一个图,其中稍后的提交指向较早的提交。stash通常保存两个提交,我喜欢称之为i(索引/暂存区内容)和w(工作树内容)。还请记住,每个提交保存一个快照。在正常提交中,这个快照是从索引/暂存区的内容中创建的,所以i提交实际上是一个非常正常的提交!它只是不在任何分支上:

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

如果您正在创建一个普通的藏匿点,那么git stash代码现在通过复制所有跟踪的工作树文件来创建wGit将这个w提交的第一个父提交设置为指向HEAD提交,第二个父提交设置为指向i提交。它将stash设置为指向该w提交:

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

如果添加--include-untracked--all,Git会在iw之间额外提交一次uu的快照内容是那些未被跟踪但未被忽略的文件(--include-untracked),或未跟踪的文件,即使它们被忽略(--all).这个额外的u提交 * 没有 * 父提交,然后当git stash生成w时,它将w的 * 第三 * 父提交设置为这个u提交,从而得到:

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

此时,Git还会 * 移除 * 任何在u提交中结束的工作树文件(使用git clean来执行此操作)。

恢复藏匿

当你去 restore 一个存储时,你可以选择使用--index,或者不使用它。这告诉git stash apply(或者任何内部使用apply的命令,比如pop)它应该 * 使用 * i提交来尝试修改你的当前索引。

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

(more或者更少;这里有一堆琐碎的细节妨碍了基本思想)。
如果省略--indexgit stash apply将完全忽略i提交。
如果stash只有两次提交,git stash apply现在可以应用w提交。(不允许提交或将结果视为正常合并),使用在其上进行隐藏的原始提交(i的父级,以及w的第一个父级)作为合并基,w作为--theirs提交,并且您的当前(HEAD)commit作为合并的目标。如果合并成功,那么一切都很好-至少 Git 是这么认为的-并且git stash apply本身也成功了。如果使用git stash pop来应用stash,如果合并失败,Git会声明apply失败。如果使用的是git stash pop,代码会保留stash,并显示与git stash apply相同的失败状态。
但如果你有第三次提交--如果你正在应用的缓存中有一个u提交--那么事情就改变了!**没有办法假装u提交不存在。**4 Git坚持将所有来自u提交的文件提取到当前的工作树中。这意味着这些文件要么根本不存在,或者具有与X1 M51 N1 X提交中相同的内容。
要实现这一点,您可以自己使用git clean-但请记住,未跟踪的文件(不管是否忽略)在Git仓库中没有其他存在,所以要确保这些文件都可以被销毁!或者,你可以创建一个临时目录,并将文件移到那里进行安全保存-或者甚至再做一个git stash save -ugit stash save -a,因为这些将为您运行git clean。但这只是留给您另一个u风格的隐藏要稍后处理。
1这实际上是refs/stash。如果你创建一个名为stash的分支,这一点很重要:分支的全名是refs/heads/stash,所以它们不冲突。但是不要这样做:Git 不会介意,但你会把自己搞糊涂。:-)
2 git stash代码实际上直接使用了git merge-recursive,这是有必要的,因为有很多原因,而且还有一个副作用,那就是要确保Git在你解决冲突和提交时不会将其视为合并。
3这就是为什么我建议避免使用git stash pop,而使用git stash apply。你有机会检查应用了什么,并决定它是否 * 实际上 * 应用正确。如果没有,你 * 仍然有你的隐藏 * 这意味着你可以使用git stash branch来完美地恢复所有内容。好吧,假设没有讨厌的u提交。

4确实应该:git stash apply --skip-untracked之类的。还应该有一个变体,意思是 * 把所有那些u提交文件放到一个新目录 *,例如,git stash apply --untracked-into <dir>

ru9i0ody

ru9i0ody3#

要在Daniel Smith's answer上展开:该代码仅恢复跟踪的文件,即使您在创建隐藏时使用了--include-untracked(或-u)。

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

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

这将完全恢复跟踪的内容(在stash中)和未跟踪的内容(在stash^3中),然后删除隐藏内容。

*小心-这将覆盖所有与您的藏匿内容!

4uqofj5v

4uqofj5v4#

除了别的答案外,我还耍了个小花招

*已删除所有新文件(已存在的文件,例如问题中的foo.txt和bar.txt)

  • git stash apply(可以使用任何命令,例如应用、弹出等)
wljmcqd8

wljmcqd85#

解决此问题的一个简单方法是将现有冲突文件重命名为不冲突文件。
例如,如果运行git stash apply/pop并获得:

foo.md already exists, no checkout
error: could not restore untracked files from stash

尝试将foo.md重命名为foo.new.md,然后重新运行git stash apply/pop,这次不会出现错误,并且foo.mdfoo.new.md都可供您使用。

kokeuurv

kokeuurv6#

我刚刚遇到了这个问题。由于上面的答案不工作,而不丢失文件从藏匿,我即兴。
我用git stash -u创建的stash没有跟踪新添加的文件。其中一些文件来自代码生成,在我尝试弹出stash之前被单独提交到master分支。当我弹出stash时,我得到了以下内容:

> 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无法合并两个不同的新文件的理论,我删除了工作目录中的每一个指定文件,然后就可以应用完整的隐藏文件,而不会丢失文件或出现错误。

sxpgvts3

sxpgvts37#

以下是我为使其发挥作用而采取的步骤:
1.我删除了工作目录中邮件中提到的每个文件。
1.我重新申请了未设置的藏匿地点。
应该应用隐藏中的所有文件,没有任何错误。

bejyjqdl

bejyjqdl8#

一旦你把你的分支命名为(本地分支)与你的本地更改。让我们假设文件A,文件B和新添加的文件C。文件C是在untrack阶段,而把代码藏在你的分支。
稍后,如果你使用git pull来获取master文件中的最新代码,文件A被更新了。之后,你尝试在分支中应用stash,但它不会给予未跟踪的文件,例如文件C,因为在进行pull请求时,文件A被更新了。这是新版本GIT的一个限制。

变通方法或解决方案:将你的本地分支头指向文件A没有更新的地方,并应用stash代码,现在你可以控制所有三个文件,将untrack转换为track文件,或者复制一份适合你的文件。用git pull命令更新你的分支头,你也有了自己的代码(可以从stash获取,或者如果你复制了,可以手动粘贴)

相关问题