为什么`git stash -p`有时会失败?


73

我♥ git stash -p。但有时,一个满足会议结束后yns,我得到这个:

Saved working directory and index state WIP on foo: 9794c1a lorum ipsum
error: patch failed: spec/models/thing_spec.rb:65
error: spec/models/thing_spec.rb: patch does not apply
Cannot remove worktree changes

为什么?


看起来像是一个相当确定的错误……它在我第一次尝试拆分时失败了(并选择仅保留两个结果块中的一个)。至于为什么……很明显,它在操作的某个时候没有生成适当的补丁(可能是在回滚所选更改时),但是对于细节,我可能会转到邮件列表,除非您有时间潜入进入源头。
卡斯卡贝尔2011年

1
我正在深入研究;最终,我将向邮件列表发送一些内容,并在此发布。我想看看我是否真的能找到解决办法,而不仅仅是告诉他们git add--interactive --patch=stash工作不正确。
卡斯卡贝尔2011年

3
哎呀,一个星期五晚上的索引太乱了。我的邮件列表在这里
卡斯卡贝尔2011年

13
同时,您可以通过简单地使用add -p将要保留的所有内容保存到索引中,然后stash --keep-index隐藏其他部分来解决此问题。
卡斯卡贝尔2011年

这是同样的问题git add -pgist.github.com/nh2/…–
nh2

Answers:


8

git stash -p应该在Git 2.17(2018年第二季度)上减少失败。
在此之前,“ git add -p(与共享逻辑git stash)”在将结果传递给底层的“ git apply”之前一直懒于合并拆分补丁,从而导致极端的案例错误;紧缩选择之后准备要应用的补丁的逻辑。

参见commit 3a8522fcommit b3e0fcfPhillip Wood()的提交2b8ea7f(2018年3月5日),提交fecc6f3提交23fea4c提交902f414(2018年3月1日)和提交11489a6提交e4d671c提交492e60c(2018年2月19日(通过合并JUNIOÇ滨野- -提交436d18f,2018年3月14日)phillipwood
gitster

add -p:跳过一个后的块的偏移量

(添加,但同样可以应用于隐藏)

由于提交8cbd431(“ git-add--interactive:用apply --recount替换大块重计”,2008-7-2,Git v1.6.0-rc0)如果大块被跳过,则我们依赖上下文行在正确的位置应用后续大块。

尽管大多数情况下这都是可行的,但很可能最终将块应用在错误的位置。

要解决此问题,请调整后续块的偏移量,以纠正由于跳过的块导致的插入或删除次数的任何变化。由于编辑的块改变了插入或删除的数量而导致的偏移量更改在此处被忽略,它将在下一次提交中修复。

您可以在此处看到一些测试


Git 2.19进行了改进git add -p:当用户在“ git add -p”中编辑补丁,并且用户的编辑器设置为不加区分地去除尾随空白时,补丁中未更改的空行将变为完全空(而不是上面带有唯一SP的行)。
Git 2.17时间框架中引入的代码未能解析此类补丁,但是现在它学会了注意情况并加以解决。

参见Phillip Wood()的commit f4d35a6(2018年6月11日(通过合并JUNIOÇ滨野- -提交5eb8da8 6月28日2018)phillipwood
gitster

add -p:修复了对已编辑补丁中的空白上下文行进行计数的问题

recount_edited_hunk()commit 2b8ea7f中引入(“ add -p:计算已编辑补丁的偏移量增量”,2018-03-05,Git v2.17.0)要求所有上下文行以空格开头,不计算空行。
如果用户在编辑补丁时在末尾引入了空行,则可以避免重新计数的问题。

但是,这将回归引入到' git add -p'中,这似乎是编辑人员在编辑补丁时从空白上下文行中删除尾随空白,从而引入应计数的空白行的常见现象。
'git apply'知道如何处理这些空行,并且POSIX指出在空上下文行上是否有空格是实现定义的(请参见diff命令)。

通过对仅包含换行符的行以及以空格开头的行作为上下文行进行计数来修复回归,并添加测试以防止将来回归。


Git 2.23(Q3 2019)改进了git add -pgit checkout -p”所使用的,它需要有选择地反向应用补丁:之前效果不佳。

参见Phillip Wood()的commit 2bd69b9(2019年6月12日phillipwood
(由Junio C gitsterHamano合并--commit 1b074e1中,2019年7月9日)

add -pcheckout -p配合病理背景

提交fecc6f3(“ add -p:跳过时,调整后续块的偏移量”,2018-03-01,Git v2.17.0-rc0)已修复在跳过前一个块时将块添加到正确位置的问题。

但是,它没有解决反向应用的补丁。

在这种情况下,我们需要调整图像前偏移,以便当应用反转贴片时,可以正确调整图像后偏移。
当补丁反转时,我们减去而不是增加增量(考虑它的最简单方法是考虑被跳过的大量删除-在这种情况下,我们要减少偏移量,因此我们需要减去)。


在Git 2.25(2020年第一季度)中,继续将git-add--interactivePerl脚本移至C的工作。

结果,上述修复程序得以重新实现。

提交2e40831提交54d9d9b提交ade246e提交d6cf873提交9254bdf提交bcdd297提交b38dd9e提交11f2c0d提交510aeca提交0ecd9d2提交5906d5d提交47dc4fd提交80399ae提交7584dd3提交12c24cf提交25ea47a提交e3bd11b提交1942ee4提交f6aa7ec(13 Dec 2019)通过Johannes Schindelin(dscho
(由Junio C gitsterHamano合并--commit 45b96a6中,2019年12月25日)

built-in add -p:根据需要调整大块头

签字人:约翰内斯·辛德尔林

跳过添加的行数与删除的行数不同的块时,我们需要调整未跳过的块的后续块头:在病理情况下,上下文不足以精确确定应在何处应用补丁。

23fea4c240中发现了此问题(“ t3701add病理上下文线测试失败”,2018-03-01,Git v2.17.0-rc0- merge),并在fecc6f3a68的Perl版本中修复了该问题(“ add -p:一个被跳过“,2018-03-01,Git v2.17.0-rc0- merge)。

这个补丁可以在C的C版本中修复它git add -p

与Perl版本相反,我们尝试保持多余的文本在块头(通常包含其代码在块中更改的函数的签名)上。

注意:虽然C版本在此阶段不支持登台模式更改,但我们已经为此做好了准备,只需在新旧偏移量均为0时跳过大块头(对于常规大块就不会发生,我们将其用作表示我们正在查看特殊块)。

同样,我们已经通过适当地处理大块标题中缺少多余文本的方式来准备大块拆分:只有第一个拆分大块具有该文本,而其他大块没有(通过空的额外文本开始/结束范围来表示)。在这个阶段已经准备好进行块分割,可以避免以后整个块头打印块的缩进更改,并且几乎像没有处理那样容易进行检查。


在Git 2.27(2020年第二季度)之前,允许用户在“git stash -p ”不能很好地工作;添加了创可贴,以使其(部分)更好地工作。

参见Johannes Schindelin()的提交7723436提交121c0d4(2020年4月8日(由Junio C Hamano合并--e81ecff提交中,2020年4月28日)dscho
gitster

stash -p:(部分)修复有关拆分大块头的错误

签字人:约翰内斯·辛德尔林

当试图通过拆分块来隐藏工作树的部分更改,然后仅部分接受拆分后的片段时,会向用户显示一个相当神秘的错误:

error: patch failed: <file>:<line>
error: test: patch does not apply
Cannot remove worktree changes

并且该命令将无法隐藏工作树更改的所需部分(即使stash参考实际上已正确更新)。

我们甚至有一个测试案例证明了这种失败,已经持续了四年。

解释:分割大块时,更改的行不再被多于3行(这是默认情况下Git的diff使用的上下文行的数量)隔开,但小于此值

因此,当仅暂存部分diff块进行存储时,我们想要反向应用于工作树的结果diff将包含那些要删除的更改,并由三个上下文行包围,但是由于diff是相对于HEAD而不是相对于HEAD工作树,这些上下文行将不匹配。

时间示例。假设文件README包含以下几行:

We
the
people

并且工作树添加了一些行,以便包含这些行:

We
are
the
kind
people

并且用户尝试隐藏包含“ are”的行,那么该命令将在内部将此行暂存为临时索引文件,并尝试还原HEAD与该索引文件之间的差异。尝试还原
的diff块git stash看起来像这样:

@@ -1776,3 +1776,4
 We
+are
 the
 people

很明显,现在,这后上下文行与原来差异大块用户也部分重叠,希望藏匿。

请记住,差异中的上下文行主要用于找到差异不完全适用时的确切位置(但当要打补丁的文件中的确切行号与差异中指示的行号不同时),我们可以通过减少上下文行的数量来解决此问题:仅生成差异。

注意:这不是此问题的完整解决方案。
就像在t3701的“ add -p与病理上下文线一起工作”测试用例中所证明的那样,diff格式中存在歧义。当然,在实践中很少会遇到这种重复的现象。

对于这种情况,完整的解决方案将是替换从存储中生成差异然后通过仿真git revert(即进行三向合并)反向应用差异的方法。但是,git stash -p它不适用于工作HEAD树,而只适用于工作树,只要我们还维护的脚本版本,这将使实现这个工作很简单add -i


Git 2.29(Q4 2020)为git add -p(由...使用stash -p

参见Phillip Wood(提交的324efcf(2020年9月7日(由Junio C Hamano合并--commit 3ad8d3e中,2020年9月18日)phillipwood
gitster

add -p:修复内存泄漏

签名人:Phillip Wood
签名人:Johannes Schindelin

asan报告说C版本的 add -p没有释放它分配的所有内存。

通过引入清除struct add_p_state的函数并使用它而不是释放单个成员来解决此问题。


对版本2.17和2.19之间行为差异的解释非常有帮助。
Axel

28

每当我尝试将一个大块分割成多个彼此靠近的小块(每次更改之间少于3条线)时,就会发生这种情况。简短的解释是,补丁中包含与本地更改冲突的上下文行。下面有更完整的解释。


假设我有一个git repo,其中包含这些未提交的更改:

--- a/pangram
+++ b/pangram
@@ -1,8 +1,8 @@
 The
-quick
+relatively quick
 brown
 fox
-jumps
+walks
 over
 the
 lazy

如果我进行第一个更改,则会得到:

--- a/pangram
+++ b/pangram
@@ -1,5 +1,5 @@
 The
-quick
+relatively quick
 brown
 fox
 jumps

git stash命令实际上确实成功保存了补丁(检查git stash list),但是git反向使用该补丁从我的工作目录中删除隐匿的更改。大块后面的上下文带有“跳转”,与仍在我的工作目录中的“行走”不匹配。所以git摆脱了困境

错误:补丁失败:pangram:1
错误:pangram:补丁不适用
无法删除工作树更改

并将所有更改留在我的工作目录中,并且存储变得几乎一文不值。


我将其称为git的大块拆分支持中的错误。如果它知道将更改划分得太近,则可以从补丁中删除几行上下文,或者将补丁吉米化以具有修改后的上下文行,而不是原始的上下文行。或者,如果正式不支持拆分该大块,则它实际上应拒绝拆分该大块。


我不认为这是块拆分中的错误:我只是git add -p在从未选择拆分块的地方遇到了这个问题,只说了y/ n
nh2 2014年

2
当然,它不仅限于显式拆分块。该错误的事实在于,git选择根据原始状态而不是当前的工作状态任意创建补丁,而当帅哥与其他更改过于接近时,该补丁将失败。最常见的情况是显式拆分块,但是显然这不是唯一的情况。
Mu Mind

6

git stash -p以同样的方式失败之后,我有了这种变通方法(git 2.0.2):

  • git add -p,分割完全相同的块,但答案相反(“ y”表示add“保持”变化,“ n”表示stash保持变化)。
  • git stash -k 保持索引并隐藏其他所有内容
  • git reset 继续处理我的文件

我不确定为什么 git add -p没有以同样的方式失败git stash -p。我猜是因为添加可以使用索引而不是创建补丁文件?


可悲的是,逆向答案并不总是导致差异相距甚远。
约翰·巴希尔

4

不幸的是,即使在Git 2.17中,目前接受的答案仍然可能失败。

如果像我一样,您花费大量的精力来构建完美的储藏室,又不想放弃这些精力,那么仍然有可能在很大程度上获得所需的东西:

git stash show -p | patch -p1 -R

这将因拒绝而失败,但是很有可能大多数应用程序都可以正确应用,并且至少可以节省您再次检查所有文件的时间。


有趣的方法。+1。令我失望的是,在您的情况下2.17仍然失败。
VonC

-2

应用状态可能会因冲突而失败;在这种情况下,它不会从存储列表中删除。您需要手动解决冲突,然后手动调用git stash drop


5
此答案适用于git stash pop失败,而不是git stash -p失败。
Max Nanasy 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.