76

我♥ 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

为什么?

4

4 回答 4

29

每当我尝试将一个大块拆分为靠得太近的小块(更改之间少于 3 行)时,都会发生这种情况。简短的解释是补丁中的上下文行与您的本地更改冲突。下面有更完整的解释。


假设我有一个包含这些未提交更改的 git 存储库:

--- 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 的大块拆分支持中的错误。如果它知道将更改拆分得太近,它可能会从补丁中删除几行上下文,或者将补丁添加到修改后的上下文行而不是原始上下文行。或者,如果官方不支持拆分这种关闭的块,它实际上应该拒绝拆分那种关闭的块。

于 2012-10-06T21:08:57.603 回答
14

git stash -pGit 2.17(2018 年第二季度)的失败应该更少。
在此之前,“ git add -p”(与 共享逻辑git stash)在将结果传递给底层“”之前一直懒惰地合并拆分补丁git apply,从而导致极端情况错误;准备在大块选择后应用的补丁的逻辑已经收紧。

请参阅Phillip Wood 的提交 3a8522f提交 b3e0fcf提交 2b8ea7f(2018 年 3 月 5 日)、提交fecc6f3提交 23fea4c提交 902f414(2018 年 3 月 1 日)和提交 11489a6提交 e4d671c提交 492e60c phillipwood 2018 年 2 月 19 日
(由Junio C Hamano 合并 -- gitster--436d18f 提交中,2018 年 3 月 14 日)

add -p: 跳过一个块时调整后续块的偏移量

(添加,但同样,可以应用于存储)

由于提交 8cbd431 (" git-add--interactive: replace hunk recounting with apply --recount", 2008-7-2, Git v1.6.0-rc0) 如果跳过一个大块,那么我们依靠上下文行在正确的位置应用后续大块。

虽然这在大多数情况下都有效,但大块头最终可能被应用在错误的地方。

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

你可以在这里看到一些测试


Git 2.19 改进git add -p:当用户在 " git add -p" 中编辑补丁并且用户的编辑器设置为不加选择地去除尾随空格时,补丁中未更改的空行将变为完全空(而不是上面有唯一 SP 的行)。
Git 2.17 timeframe 中引入的代码无法解析这样的补丁,但现在它学会了注意情况并应对它。

请参阅Phillip Wood ( ) 的提交 f4d35a6(2018 年 6 月 11 日(由Junio C Hamano 合并 -- --提交 5eb8da8中,2018 年 6 月 28 日)phillipwood
gitster

add -p:修复计数编辑补丁中的空上下文行

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

然而,这引入了对 ' git add -p' 的回归,因为在编辑补丁时,编辑器通常会从空上下文行中去除尾随空格,从而引入应该计算的空行。
'git apply' 知道如何处理这样的空行,并且 POSIX 声明空上下文行上是否有空格是实现定义的(参见diff 命令)。

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


Git 2.23(2019 年第三季度)改进了git add -p, 由 " git checkout -p" 使用,它需要选择性地反向应用补丁:它以前不能很好地工作。

请参阅Phillip Wood ( ) 的提交 2bd69b9(2019 年 6 月 12 日(由Junio C Hamano 合并 -- --提交 1b074e1中,2019 年 7 月 9 日)phillipwood
gitster

add -p: 修复checkout -p病态背景

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

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

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


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

结果,重新实施了上述修复。

See commit 2e40831 , commit 54d9d9b , commit ade246e , commit d6cf873 , commit 9254bdf , commit bcdd297 , commit b38dd9e , commit 11f2c0d , commit 510aeca , commit 0ecd9d2 , commit 5906d5d , commit 47dc4fd , commit 80399ae , commit 7584dd3 , commit 12c24cf , commit 25ea47a , commit e3bd11b提交 1942ee4提交 f6aa7ec(2019 年 12 月 13 日)由约翰内斯辛德林(dscho
(由Junio C Hamano 合并gitster——提交 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 --合并)。

而这个补丁在 C 版本的git add -p.

与 Perl 版本相比,我们尝试保持块头上的额外文本(通常包含代码在块中更改的函数的签名)完好无损。

注意:虽然 C 版本在此阶段不支持暂存模式更改,但如果新旧偏移量均为 0,我们已经通过简单地跳过 hunk 标头来为此做好准备(这对于常规 hunk 不会发生,我们将使用它作为指示我们正在寻找一个特殊的大块)。

同样,我们已经通过优雅地处理大块标题中没有额外文本来准备大块分割:只有第一个分割大块会有该文本,其他不会(由空的额外文本开始/结束范围表示)。在这个阶段已经准备好大块拆分避免了以后整个大块标题打印块的缩进更改,并且几乎与没有该处理一样容易查看。


git stash -p在 Git 2.27 (Q2 2020) 之前,允许用户在 " " 不能正常工作的情况下拆分补丁块;添加了创可贴以使(部分)更好地工作。

请参阅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

并且该命令将无法存储工作树更改的所需部分(即使stashref 实际上已正确更新)。

我们甚至有一个测试用例证明了这种失败,它已经运行了四年。

解释:当拆分一个大块时,更改的行不再分隔超过 3 行(这是 Git 的差异默认使用的上下文行的数量),但少于此。

因此,当仅将 diff hunk 的一部分暂存以进行存储时,我们想要反向应用到工作树的结果 diff 将包含那些将被三个上下文行包围的更改,但由于 diff 是相对于 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 works with pathological context lines' 测试用例中所展示的那样,diff 格式存在歧义。当然,在实践中很少会遇到这样重复的台词。

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


Git 2.29(2020 年第四季度)为git add -p(由 使用stash -p)带来了泄漏修复

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

add -p: 修复内存泄漏

签字人:Phillip Wood
签字人:Johannes Schindelin

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

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

于 2018-03-16T23:54:54.970 回答
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。我猜是因为添加适用于索引而不是创建补丁文件?

于 2014-10-17T00:34:32.867 回答
5

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

如果像我一样,你花了很多精力来构建完美的存储并且不想放弃这些努力,那么仍然有可能获得你想要的大部分内容:

git stash show -p | patch -p1 -R

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

于 2018-04-07T20:46:34.713 回答