我有一堆分阶段和非分阶段的更改,我想快速切换到另一个分支,然后再切换回来。
因此,我使用以下方法进行了更改:
$ git stash push -a
(事后看来,我可能可以使用--include-untracked而不是--all)
--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 branch temp
我确实想出了一个解决方法,那就是使用:
$ git stash show -p | git apply
灾难暂时避免了,但这引发了一些问题。
为什么首先会发生此错误,下次如何避免它?
作为一些额外的解释,请注意git stash进行两次提交或三个提交。默认为两个;如果您使用--allor--include- untracked选项的任何拼写,您将得到三个。
git stash
--include- untracked
这两个或三个提交在一个重要方面是特殊的:它们 不在 分支上。Git 通过特殊名称定位它们stash。1 不过,最重要的是,Git 让你——以及 让 你——通过这两个或三个提交。要理解这一点,我们需要查看这些提交中的内容。
stash
每个提交都可以列出一个或多个 父 提交。这些形成了一个图表,之后的提交指向之前的提交。存储通常包含两个提交,我喜欢调用i索引/暂存区域内容和w工作树内容。还要记住,每个提交都包含一个快照。在正常提交中,此快照是 根据 索引/暂存区内容制作的。所以i提交实际上是一个完全正常的提交!它只是不在任何分支上:
i
w
...--o--o--o <-- branch (HEAD) | i
如果您要进行普通存储,则git stash代码w现在通过复制所有跟踪的工作树文件(到临时辅助索引中)来生成。Git 将此w提交的第一个父级设置为指向该HEAD提交,并将第二个父级设置为指向 commit i。最后,它stash指向这个w提交:
HEAD
...--o--o--o <-- branch (HEAD) |\ i-w <-- stash
如果添加--include-untrackedor --all,Git 会u在 makei和w. 的快照内容u是那些未被跟踪但未被忽略的--include-untracked文件 ( ),或者即使它们被忽略但未被跟踪的文件 ( --all)。这个额外的u提交 没有 父级,然后当git stashmake时w,它将w的 第三个 父级设置为这个u提交,这样你就得到了:
u
...--o--o--o <-- branch (HEAD) |\ i-w <-- stash / u
此时,Git 还会 删除 提交中结束的所有工作树文件u(git clean用于执行此操作)。
git clean
当您去 恢复 存储时,您可以选择使用--index或不使用它。这告诉git stash apply(或任何内部使用的命令,apply例如pop)它应该 使用i提交来尝试修改您当前的索引。此修改通过以下方式完成:
--index
git stash apply
apply
pop
git diff <hash-of-i> <hash-of-i's-parent> | git apply --index
(或多或少;有很多细节阻碍了这里的基本思想)。
如果省略--index,git stash apply则完全忽略i提交。
如果存储只有两个提交,git stash apply现在可以应用w提交。它通过调用git merge2(不允许它提交或将结果视为正常合并)来执行此操作,使用进行存储的原始提交(i的父级和w的第一个父级)作为合并基础,w作为--theirs提交,并将您当前的(HEAD)提交作为合并的目标。如果合并成功,那么一切都很好——好吧,至少 Git 是这么认为的——git stash apply它本身就成功了。如果您曾经git stash pop应用存储,则代码现在 会删除 存储。3 如果合并失败,Git 声明申请失败。如果你用过git stash pop,代码保留存储并提供与 for 相同的失败状态git stash apply。
git merge
--theirs
git stash pop
但是,如果你有 第三次 提交——如果u你正在应用的存储中有一个提交——那么事情就会发生变化! 没有选项可以假装u提交不存在。4 Git 坚持 从 该u提交中提取所有文件到当前工作树中。u这意味着文件要么根本不存在,要么与提交中的内容相同。
要做到这一点,您可以git clean自己动手——但请记住,未跟踪的文件(无论是否忽略)在 Git 存储库中没有其他存在,因此请确保这些文件都可以被销毁!或者,您可以创建一个临时目录,并将文件移到那里以妥善保管——甚至可以再做一个git stash save -uor git stash save -a,因为它们会git clean为您运行。但这只会给您留下另一种u风格的藏匿处以供以后处理。
git stash save -u
git stash save -a
1这其实是refs/stash。如果您创建一个名为 的分支,这很重要stash:该分支的全名是refs/heads/stash,因此它们不会冲突。但不要那样做: Git 不会介意,但你会让自己感到困惑。:-)
refs/stash
refs/heads/stash
2git stash代码实际上git merge-recursive直接在这里使用。出于多种原因,这是必要的,并且还具有确保 Git 在您解决冲突和提交时不会将其视为合并的副作用。
git merge-recursive
3这就是为什么我建议避免git stash pop,赞成git stash apply。您有机会查看已应用的内容,并确定它是否 实际 应用正确。如果没有,你 仍然有你的存储 ,这意味着你可以用来git stash branch完美地恢复一切。好吧,假设没有那个讨厌的u提交。
git stash branch
4确实应该有:git stash apply --skip-untracked或者什么。还应该有一个变体,意味着 _将所有这些u提交文件放到一个新目录_中,例如git stash apply --untracked-into <dir>,也许。
git stash apply --skip-untracked
git stash apply --untracked-into <dir>