实际上撤消git stash pop

时间:2018-02-05 09:26:01

标签: git

This question具有相同的标题,但相同的问题。这个问题实际上是在问“放弃git stash pop的结果”。这个问题实际上是

UNDO git stash pop

换句话说

// in branch foo
git stash
git checkout bar
git stash pop       # ERROR. I didn't want to pop on top of bar, lots of conflicts
git stash undo-pop  # NEED COMMAND TO PUT STASH AND LOCAL FILES BACK AS THEY WERE
git checkout foo
git stash pop

在输入git stash pop之前,有没有办法让所有东西回到状态?换句话说,实际上 UNDO pop并将隐藏的东西放回藏匿处,并将本地文件恢复到我输入git stash pop之前的状态。

这也不是How to recover a dropped stash in Git?的欺骗行为虽然在某些情况下可能会有所帮助

4 个答案:

答案 0 :(得分:12)

在您的示例中,要恢复到<wchar.h>之前的状态,请使用:

git stash pop

这种形式的git reset --hard bar 命令将索引的状态恢复到git reset分支的头部。

由于您在第一个bar上存在冲突,因此存储保留在存储堆栈顶部

从那里,您可以再次git stash popgit checkout foo

答案 1 :(得分:2)

Greg Hewgill's answer是正确的(并且赞成,并且OP应该接受它)但是这里有几个额外的警告,以防有人想以更一般的方式使用答案。让我们首先看一下使用的特定命令序列:

git stash
git checkout bar
git stash pop       # ERROR ... lots of conflicts

现在,让我们列出警告:

  • git stash pop失败很重要。 (格雷格已经注意到这一点。)
  • 创建存储时
  • 运行git stash --keep-index后,没有对工作树进行任何更改。
  • git stash命令成功,因此 it 可能已对您的工作树进行了更改 - 事实上,它必须已经这样做,git checkout失败了 - 但你的工作树仍然是“干净的”,正如pop所说的那样。

这是最后一点,git status会(在尝试git status之前)告诉您工作树是git stash pop,这是关键。如果您在clean之前或之后对工作树进行了更改,那么您将遇到更多麻烦。

因为你没有做这些事情,git checkout bar才是答案。

为什么会这样?

通常,git reset --hard做两次提交。一个保存当前的索引,另一个保存当前的工作树 1 这些提交在某些方面略显特殊;最重要的是他们在没有分支 2 提交后,git stash正常运行git stash 3

git reset --hard步骤的作用是使索引和工作树与当前提交匹配。也就是说,我们已经将(整个)索引和(整个跟踪部分)工作树保存在存储中;所以当前git reset --hard提交和索引之间的不同,可以重新设置为相同;在HEAD提交和工作树之间的不同,可以重新设置为相同。

HEAD之后,索引和工作树都是“干净的”,因为git reset会说:它们都匹配git status提交。然后,您可以HEAD其他分支,因为您没有未保存的工作。然后,您可以尝试git checkout,甚至git stash apply,将您的更改“移动”到另一个分支。

当失败时 - 就像在这种情况下 - 藏匿处仍保存在已保存的藏匿处中。当前索引和工作树现在充满了合并冲突。如果你运行git stash pop,Git将像往常一样重新设置索引和工作树以匹配git reset --hard提交,这样你就会回到{{{}后的同样情况。 1}}步骤。由于您没有未保存的工作(您的已保存的工作仍在隐藏提交中),您可以在这里。

(如果你 有未保存的工作,HEAD步骤会通过尝试合并隐藏的工作树更改来破坏未保存的工作。这很难撤消,一般。)

1 虽然git checkout通常会进行两次提交,但如果您使用git stash applygit stash运行,则会使三次提交。我喜欢将这些称为--all(索引),--include-untracked(工作树)和i(无标记文件)提交。

使用wu时,--all--include-untracked步骤不只是save:它还会运行push删除进入第三次提交的任何内容(未跟踪的文件或未跟踪的包含忽略的文件)。在应用这样的存储之前,您可能需要重复此git reset --hard工作,这非常棘手且烦人。

稍后,当您运行git clean时,Git将(尝试)应用git clean提交(如果存在)。它将始终(尝试)应用git stash apply提交。如果您给它u标志,它将(尝试)仅应用w提交 。在许多版本的i中存在一些小错误,围绕着整个“单独索引恢复”的东西。它们往往会影响想要使用--indexgit stash标志的用户,例如预提交挂钩。

请注意--keep-index只是--index:也就是说,尝试应用存储,然后如果Git认为git stash pop进展顺利,那么{{1}存储。我发现最好首先使用git stash apply && git stash drop,以避免丢弃隐藏即使 Git认为它进展顺利,因为Git和我有时不同意“进展顺畅”意味着什么。 : - )

2 Git使用名称apply来记住当前的存储,而(ab)使用drop的reflog来维护“存储堆栈”的剩余部分。内部的分支名称都具有git stash apply形式,因此refs/stash不是分支名称。

3 如果您使用refs/stash,它运行的不只是refs/heads/name:它将保存的索引状态提取到工作树。这里的目标是让工作树的设置与索引的设置方式相同,这样您就可以测试您要提交的内容。如脚注1所示,这里有一个很小但相当讨厌的错误,其中包含refs/stash的许多版本,如果正确的工作树版本,则隐藏的工作树状态意外地采用索引版本而不是工作树版本匹配git stash --keep-index版本。

答案 2 :(得分:1)

你可以反向修补存储(已经提到的应该仍然存在,因为git不会丢弃存储,如果它不能干净地应用)

git stash show -p stash@{0} | git apply -R

答案 3 :(得分:0)

所以,在所有情况下,我都不是100%肯定答案。在我的情况下,当我输入git stash pop并且我在分支bar时,存在冲突。因此,虽然它应用了藏匿处,但它没有放下藏匿物。因此,要撤消并正确将存储应用于分支foo,它只是

git reset --hard      # resets bar to before the stash pop
git checkout -b foo
git stash pop         

如果与分支bar没有冲突,则只是

git stash             # restash
git checkout -b foo
git stash pop