Greg Hewgill的回答是正确的(并且得到了赞同,原帖作者应该接受它),但在这里还有一些额外的注意事项,以防有人想要更广泛地使用这个答案。首先让我们看一下使用的特定命令序列:
git stash
git checkout bar
git stash pop
现在,让我们列出一些注意事项:
- 重要的是
git stash pop
失败了。(Greg已经指出这个问题。)
- 创建存储时未使用
git stash --keep-index
。
- 运行
git stash
后,你没有对工作目录进行任何更改。
git checkout
命令成功执行,因此它可能已经对你的工作目录进行了更改——实际上,为了使pop
失败,它必须已经这样做了——但是你的工作目录仍然是“干净”的,就像git status
所说的那样。
最后一个点是关键,即在尝试git stash pop
之前,git status
会告诉你你的工作目录是clean
的。如果你在git checkout bar
之前或之后对工作目录进行了更改,你将会有更大的麻烦。
由于你没有做这些事情,git reset --hard
就是答案。
为什么要这样做
通常,git stash
做的事情是创建两个提交。其中一个保存当前的index,另一个保存当前的work-tree1。这些提交在一些方面是稍微特殊的;最重要的是它们都不在任何分支上2。创建完提交之后,git stash
通常会运行git reset --hard
3。
git reset --hard
步骤所做的事情是将索引和工作目录与当前提交匹配。也就是说,我们已经将(整个)索引和(跟踪部分的)工作目录保存在存储中;因此,无论当前的HEAD
提交和索引之间有什么不同,都可以重新设置为相同的内容;无论当前的HEAD
提交和工作目录之间有什么不同,也可以重新设置为相同的内容。
在git reset
之后,索引和工作目录都是“干净的”,就像git status
所说的那样:它们都与HEAD
提交匹配。然后你可以git checkout
其他分支,因为你没有未保存的工作。然后你可以尝试git stash apply
,甚至git stash pop
,将你的更改“移动”到这个其他分支。
当
git stash apply
失败时,就像这种情况一样,备份会保存在已保存的备份中。当前的索引和工作树现在充满了合并冲突。如果你运行
git reset --hard
,Git 将像往常一样重新设置索引和工作树以匹配
HEAD
提交,因此你将回到执行
git checkout
步骤后所处的相同情况。由于你没有未保存的工作(你的已保存工作仍然在备份提交中),所以你在这里是安全的。
(如果你有未保存的工作,
git stash apply
步骤将尝试合并备份的工作树更改,从而破坏了那些未保存的工作。通常情况下,这非常难以撤消。)
虽然
git stash
通常会创建两个提交,但如果你使用
--all
或
--include-untracked
运行它,它将创建三个提交。我喜欢称它们为
i
(索引)、
w
(工作树)和
u
(未跟踪文件)提交。
当使用
--all
或
--include-untracked
时,
save
或
push
步骤将不仅运行
git reset --hard
:它还将运行
git clean
来删除第三个提交中的内容(仅未跟踪文件或包括被忽略的未跟踪文件)。在应用这样的备份之前,你可能需要重复执行
git clean
,这很棘手而且很烦人。
稍后,当你运行
git stash apply
时,如果存在
u
提交,Git 将(尝试)应用它。它将始终(尝试)应用
w
提交。它将仅在你给出
--index
标志时(尝试)应用
i
提交。许多版本的
git stash
存在一些小错误,涉及到整个“单独索引恢复”问题。它们倾向于影响那些想要在例如预提交挂钩中使用
--keep-index
和
--index
标志的人。
请注意,
git stash pop
只是
git stash apply && git stash drop
:也就是说,尝试应用备份,然后,
如果 Git 认为 apply
进行得很好,也会 drop
备份。我发现最好先使用
git stash apply
,以避免即使 Git 认为它进行得很好,也放弃备份,因为 Git 和我有时对“进行得很好”这个概念存在分歧。 :-)
2Git使用名称refs/stash
来记住当前的暂存内容,并且滥用reflog来维护“暂存栈”的其余部分。分支名称在内部都采用refs/heads/name
的形式,因此refs/stash
不是分支名称。
3如果您使用git stash --keep-index
,它会运行更多的命令而不仅仅是git reset --hard
:它将保存的索引状态提取到工作树中。这里的目标是使工作树设置与索引设置相同,以便您可以测试即将提交的内容。正如脚注1中所述,许多版本的git stash
存在一个小但相当严重的错误,即如果正确的工作树版本与HEAD
版本匹配,则暂存的工作树状态会意外地采用索引版本而不是工作树版本。
git stash
来实现此操作。 - axiacgit stash
,因为文件在bar
之上处于冲突状态。 - gmangit stash
没有像git merge
和git rebase
那样的--abort
选项;这将是很好的。请点赞:https://github.com/git/git.github.io/issues/599 - trusktr