作为进一步的解释,需要注意的是git stash
会创建两个或三个提交记录。默认情况下是两个提交记录;如果使用--all
或--include-untracked
选项中的任何一个,就会创建三个提交记录。
这两个或三个提交记录在一个重要方面非常特殊:它们不属于任何分支。Git通过特殊的名称stash
1来定位它们。最重要的是,Git允许你——并且会强制让你——对这两个或三个提交记录进行操作。要理解这一点,我们需要查看这些提交记录中包含了哪些内容。
藏匿记录中包含了什么
每个提交记录可以列出一个或多个父节点。它们构成了一个图形,其中较新的提交记录指向较早的提交记录。stash通常保存两个提交记录,我喜欢称其为i
表示暂存区的内容和w
表示工作树的内容。同时,请记住每个提交记录都保存了快照。在普通的提交记录中,此快照是从暂存区的内容中生成的。因此,i
提交记录实际上是一个完全正常的提交记录!它只是不属于任何分支:
...--o--o--o <-- branch (HEAD)
|
i
如果您正在创建普通的stash,
git stash
代码现在通过复制您所有已跟踪的工作树文件(到一个临时辅助索引)来进行操作。Git将此
w
提交的第一个父节点设置为指向
HEAD
提交,第二个父节点指向提交
i
。最后,它将
stash
设置为指向此
w
提交。
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
如果您添加
--include-untracked
或
--all
,Git 会在执行
i
和
w
之间创建一个额外的提交
u
。对于提交
u
的快照内容是那些未被跟踪但未被忽略的文件(
--include-untracked
),或者即使它们被忽略也是未被跟踪的文件(
--all
)。这个额外的提交
u
没有父节点,然后当
git stash
创建
w
时,它会将
w
的第三个父节点设置为这个提交
u
,以便你得到:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
此时,Git 还会使用 git clean
删除任何在 u
提交中进入工作树的文件。
恢复一个存储区
当您要恢复一个存储区时,可以选择使用--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
提交记录。
如果stash仅有两个提交记录,则git stash apply
现在可以应用w
提交记录。 这是通过调用git merge
2(而不允许其提交或将结果视为正常合并)来完成的,
使用创建stash时的原始提交记录(i
的父项和w
的第一个父项)作为合并基础,w
作为--theirs
提交记录,以及当前(HEAD)提交记录作为合并的目标。
如果合并成功,一切都好——至少Git认为如此——git stash apply
本身就会成功。如果您使用git stash pop
应用了stash,则代码现在将删除stash。3
如果合并失败,则Git宣布应用失败。 如果您使用git stash pop
,则代码保留stash,并提供与git stash apply
相同的失败状态。
但是,如果有第三个提交记录——如果正在应用的stash中存在u
提交记录——那么事情就会有所改变!没有选项可以假装u
提交记录不存在。4
Git坚持要从该u
提交记录中提取所有文件,并将它们提取到当前的工作树中。 这意味着文件必须要么根本不存在,要么与u
提交记录中的内容相同。
为了实现这一点,您可以自己使用git clean
,但请记住未跟踪的文件(无论是否被忽略)在Git存储库中没有其他存在方式,因此确保这些文件都可以被删除! 或者,您可以创建一个临时目录,将文件移动到该目录进行安全保管,
甚至可以运行另一个git stash save -u
或git stash save -a
,因为这些操作将为您运行git clean
。 但是,这只会让您面对另一个类似于u
的stash,稍后还需要处理。
1实际上是refs/stash
。 如果您创建了一个名为stash
的分支:分支的完整名称为refs/heads/stash
,
因此这些不会冲突。 但不要这样做:Git不会介意,但是您会混淆自己。 :-)
2实际上,git stash
代码在此处直接使用了git merge-recursive
。 多种原因需要这样做,
还有一个副作用是确保Git在解决冲突并提交时不将其视为合并。
3这就是我建议避免使用git stash pop
,而是使用git stash apply
的原因。 您有机会查看应用了什么,并决定它是否被正确地应用。
如果没有,您仍然拥有您的stash,这意味着您可以使用git stash branch</
git stash show -p | git apply --3
。 - xmedekogit stash show
列出您隐藏的文件,然后逐个恢复这些文件:$ git show stash@{0}:your/stashed/file.txt > your_rescued_file.txt
。这将从隐藏中获取文件并保存在不同的名称下。现在您可以安全地尝试正确的恢复方法(请参见下面的答案)。如果事情出了差错,您始终可以将已恢复的文件作为最后的资源。 - Danijelgit stash show -p -u
命令来展示整个暂存区(包括未追踪的文件),使用git stash show -p -u | git apply --3
命令来恢复整个暂存区(包括未追踪的文件)。 - Finn