如何中止 stash pop 操作?

558

我弹出了一个存储,但出现了合并冲突。与重复的问题不同,我已经在目录中有一些未提交的更改需要保留。我不仅想让合并冲突消失,还要将目录恢复到弹出之前的状态。

我尝试了git merge --abort,但是git声称没有进行任何合并。有没有一种简单的方法可以中止“stash pop”操作而不破坏我最初在目录中所做的更改?


你能否发布一下你正在使用的 Git 版本? - Tinman
相关问题中获取的相关信息:如果发生冲突,则不会从存储列表中删除存储。 - here
3
最佳答案看起来很复杂。我认为从一般实践角度来看,最好不要尝试在未清理的工作目录上执行类似于git stash pop的操作。这种情况下,您可以简单地使用git reset --hard,而您的存储仍然完好无损。(这更或多或少是@BradKoch链接主题所建议的)。 - Steven Lu
1
@StevenLu,我同意,但是如果您要将更改存储以将其移动到不同的分支中,则问题可能会发生在干净的工作目录中。存储会与存在于新分支上但不存在于旧分支上的提交发生冲突。 - Jake Stevens-Haas
显示剩余5条评论
16个回答

742

简单的一行命令

我一直在使用

git reset --merge

我不记得它曾经失败过。


注: git reset --merge 将放弃任何已暂存的更改。另外,正如评论中@Saroopashree Kumaraguru所指出的,备份内容不会丢失并且可以在以后重新应用。


124
授予这个人一枚奖牌。 - Phil Young
6
最好的一个,我不知道为什么人们没有给出这个简短的答案。 - Saransh Dhyani
4
如果你这样做,之前存储的内容会发生什么?它会被放回到暂存区还是丢失了? - Adam Parkin
13
当使用git stash pop命令时因为合并冲突而导致操作不成功时,存储的内容将被自动保存在stash中,而不是被删除。 - Saroopashree Kumaraguru
18
这个便签真的需要放大。不失去暂存的更改是我寻找它的全部原因 =/ - Simon Kohlmeyer
显示剩余4条评论

367

我的使用情况:刚尝试切换到错误的分支并出现了冲突。我只需要撤销切换但保留在存储列表中,以便在正确的分支上弹出它。我做了这个:

git reset HEAD --hard
git checkout my_correct_branch
git stash pop

简单。


34
所以,你想要的隐藏物品会一直留在隐藏列表中,直到你成功地弹出它? - Ryan Clark
91
这不是原始问题的答案,因为它会清除尚未提交的本地更改。 - Dmitry
18
请参考以下DavidG的答案:@RyanClark,基本上是的,它会留在存储列表中。 - fkorsa
6
我认为大部分遇到这种情况的用户都会觉得这个解决方案是理想的。在尝试将其stash pop回目录之前,我无法想象我有未提交的更改的情况,因为那听起来像是灾难的配方。 - Shadoninja
8
好的。阅读完后,我查看了git stash pop文档,上面写着“应用状态可能会产生冲突,如果出现这种情况,该状态将不会从存储列表中删除”。这就是为什么在重置/检出后可以重新弹出存储内容的原因。 - Brady Holt
显示剩余2条评论

67

好的,我认为我已经理解了"git stash unapply"。它比git apply --reverse更复杂,因为你需要反向合并操作以便恢复git stash apply所做的任何合并。

反向合并要求将所有当前更改推送到索引中:

  • git add -u

然后反转由git stash apply执行的merge-recursive

  • git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1

现在你只剩下非stash更改了,它们在索引中。如果需要,可以使用git reset取消暂存更改。

考虑到你最初的git stash apply失败了,我认为反向操作可能也会失败,因为某些需要撤消的操作未完成。

下面是一个示例,展示了如何使工作副本(通过git status)重新变为干净状态:

 $ git status
# On branch trunk
nothing to commit (working directory clean)
 $ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
 $ git add -u
 $ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
 $ git status
# On branch trunk
nothing to commit (working directory clean)

3
做完这个操作之后,之前被存储的修改内容会永久丢失,还是会重新回到存储区里? - Brian H.
9
git stash apply 命令不会丢弃存储的内容,当合并失败时,使用 git stash pop 命令也会保留存储的内容。 - xeruf
1
如果在原始存储弹出期间发生合并冲突,将会发生糟糕的事情。 - 3ocene
3
请继续向下滚动以获取更好的答案。 - Eric Wiener

53

编辑:来自git help stash文档中的pop部分:

如果应用状态时出现冲突,则此操作可能会失败,并且此时该状态不会从隐藏列表中被移除。您需要手动解决冲突,并在之后手动调用git stash drop。

如果使用了--index选项,则尝试恢复工作树和索引的更改。但是,当存在冲突时(这些冲突存储在索引中,因此您无法像最初那样应用更改),这将失败。

尝试将你所有的repo复制到一个新的目录中(这样你就有了一个副本),然后运行:

git stash show并将其输出保存在某个地方,如果您关心它的话。

然后:git stash drop以删除具有冲突的stash
然后:git reset HEAD

这将使您的repo恢复到先前的状态。(希望如此,我仍然无法重现您的问题)

===

我正在尝试重现您的问题,但是当使用git stash pop时,我得到的是:

error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting
在一个干净的目录中:
git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop

我看不到git试图合并我的更改,它只是失败了。你有任何可以遵循的重现步骤来帮助你吗?


尝试将更改存储到另一个文件中。 - asmeurer
尝试将文件存储到不同的文件中没有成功(请参见新的再现步骤)。我只是无法再现这个问题... - DavidGamba
1
如果工作目录中存在未提交的更改,则此解决方案无法正常工作。 - here
@DavidG -- 也许这个可以?,欢迎评论 :)。 - here
1
实际情况是:1)更改文件A。2)隐藏更改。3)在文件A中进行冲突更改并提交(例如,更改同一行)。4)更改文件B。5)执行“git stash pop”。现在您有冲突和本地更改。通常它们将位于不同的文件中,但您永远不会知道哪些来自隐藏的非冲突修改文件,哪些是本地未暂存的更改。 - Dmitry
显示剩余2条评论

8

如果您不需要担心其他更改并且只想回到最后一次提交,则可以执行以下操作:

git reset .
git checkout .
git clean -f

5

好的,我想我已经找到了一个工作流程,可以让你回到你需要的地方(就像你没有执行过弹出操作一样)。

在此之前,请备份!!我不知道这是否适用于你,所以为了万无一失,请复制整个repo。

1)解决合并问题,并通过选择来自补丁的所有更改(在tortoisemerge中,这会显示为one.REMOTE(theirs))来解决所有冲突。

git mergetool

2) 提交这些更改(它们已经通过合并工具命令添加了)。给它一个提交消息,例如“merge”或您记得的其他内容。

git commit -m "merge"

3) 现在您仍然拥有最初开始的本地未暂存更改,以及来自补丁的新提交(我们稍后可以摆脱它)。现在提交您的未暂存更改。

git add .
git add -u .
git commit -m "local changes"

4) 撤销补丁。可以使用以下命令完成:

git stash show -p | git apply -R

5) 提交这些更改:

git commit -a -m "reversed patch"

6) 摆脱补丁/还原提交

git rebase -i HEAD^^^

从中删除包含“merge”和“reversed patch”的两行。

7)取回未暂存的更改并撤销“本地更改”提交。

git reset HEAD^

我用一个简单的例子演示了一遍,它可以让你回到想要的位置——正好在弹出stash之前,保留了你的本地更改并且stash仍然可用于弹出。


如果那不太行得通,希望它能让你走得更远! :-) - agentgonzo
git stash show -p | git apply -R如果git stash apply进行了实际合并,则无法正常工作。请参见我的回答... - Ben Jackson

4

我用了一种稍微不同的方法解决了这个问题,以下是我的做法。

首先,我切错了分支并遇到了冲突。储藏区保持不变,但索引处于冲突解决状态,阻止了许多命令。

简单的git reset HEAD中止了冲突解决并留下了未提交(且不需要的)更改。

几次git co <文件名>将索引还原回初始状态。最后,我使用git co <分支名称>切换到目标分支,并运行新的git stash pop,顺利地解决了问题,没有冲突发生。


3
如果只有暂存的更改,请尝试以下一行命令。它将移除所有本地的更改。 git checkout -f

3

一些想法:

  • 使用git mergetool将合并文件拆分为原始部分和新部分。希望其中之一是包含您非存储更改的文件。

  • 反向应用储藏的差异,仅撤消这些更改。您可能需要手动拆分具有合并冲突的文件(希望上述技巧适用于此)。

我没有测试过这两个方法,所以我不确定它们是否有效。


3
仅在合并其他分支代码时发生冲突时才使用“--abort”。
git merge --abort

如果`stash pop`存在冲突,您可以使用以下命令:
git reset --merge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接