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

发布于 2024-10-18 06:34:08 字数 313 浏览 8 评论 0 原文

我♥git stash -p。但有时,在一次令人满意的 y、n 和 s 会话之后,我会明白:

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

为什么?

I ♥ git stash -p. But sometimes, after a satisfying session of y, n, and s, I get this:

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

Why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

铜锣湾横着走 2024-10-25 06:34:08

每当我尝试将一个大块拆分为距离太近的较小块(更改之间的行数少于 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 会放弃

error: patch failed: pangram:1
error: pangram: patch does not apply
Cannot remove worktree changes

并将所有更改保留在我的工作目录中,并且存储变得几乎毫无价值。


我认为这是 git 块分割支持中的一个错误。如果它知道它把更改分割得太近,它可能会从补丁中删除几行上下文,或者吉米补丁以获得修改后的上下文行而不是原始的上下文行。或者,如果官方不支持如此接近的分裂帅哥,那么它实际上应该拒绝如此接近的分裂帅哥。

This happens for me any time I try to split a hunk into smaller hunks that are too close together (less than 3 lines between changes). The short explanation is that the patch has context lines in it that conflict with your local changes. More complete explanation below.


Suppose I have a git repo with these uncommitted changes:

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

If I stash the first change, I get:

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

The git stash command actually does succeed in saving the patch (check git stash list), but then git uses that patch in reverse to remove the stashed changes from my working dir. The context after the hunk has "jumps", which doesn't match the "walks" still in my working dir. So git bails out with

error: patch failed: pangram:1
error: pangram: patch does not apply
Cannot remove worktree changes

and leaves all the changes in my working dir, and the stash becomes pretty much worthless.


I would call this a bug in git's hunk splitting support. If it knows it's splitting the changes too close, it could shave off a few lines of context from the patch, or jimmy the patch to have the modified context lines instead of the pristine ones. Alternatively, if splitting hunks this close is officially unsupported, it should actually refuse to split hunks that close.

如此安好 2024-10-25 06:34:08

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

请参阅 提交 3a8522f提交 b3e0fcf, 提交 2b8ea7f(2018 年 3 月 5 日),提交 fecc6f3提交 23fea4c提交 902f414(2018 年 3 月 1 日),以及提交 11489a6提交 e4d671c提交 492e60c(2018 年 2 月 19 日),作者:Phillip Wood (菲利普伍德
(由 Junio C Hamano -- gitster -- 合并于 提交 436d18f,2018 年 3 月 14 日)

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 时间范围中引入的代码无法解析这样的补丁,但现在它学会了注意到这种情况并应对它。

请参阅 提交 f4d35a6(2018 年 6 月 11 日),作者:菲利普·伍德 (phillipwood)
(由 Junio C Hamano -- gitster -- 合并于 提交 5eb8da8,2018 年 6 月 28 日)

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

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

然而,这引入了“git add -p”的回归,因为编辑器在编辑补丁时似乎通常会从空上下文行中删除尾随空格,从而引入应该被删除的空行。
已计算。
'git apply' 知道如何处理此类空行,并且 POSIX 规定空上下文行上是否有空格是实现定义的(请参阅 diff 命令)。

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


Git 2.23(2019 年第 3 季度)改进了“git checkout -p”使用的 git add -p,它需要选择性地反向应用补丁:效果不佳前。

请参阅 提交 2bd69b9(2019 年 6 月 12 日),作者:菲利普·伍德 (phillipwood)
(由 Junio C Hamano -- gitster -- 合并于 提交 1b074e1,2019 年 7 月 9 日)

add -p:使用病态上下文修复checkout -p

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

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

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


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

因此,重新实施了上述修复。

请参阅 提交 2e40831提交 54d9d9b, 提交 ade246e提交 d6cf873提交 9254bdf, 提交 b38dd9e提交 11f2c0d, 提交 510aeca提交 0ecd9d2提交 5906d5d提交 47dc4fd提交 80399ae提交7584dd3提交 25ea47a提交 e3bd11b, 提交 1942ee4提交 f6aa7ec(2019 年 12 月 13 日) )作者:Johannes Schindelin (dscho)
(由 Junio C Hamano -- gitster -- 合并于 提交 45b96a6,2019 年 12 月 25 日)

内置add -p:调整块根据需要标头

签字人:Johannes Schindelin

当跳过添加的行数与删除的行数不同的块时,我们需要调整非跳过块的后续块标头:在病态情况下,上下文不足以准确确定应应用补丁的位置.

此问题已在 23fea4c240 ("t3701add 病理上下文行测试失败”,2018-03-01,Git v2.17.0-rc0 -- 合并 ) 并在 rel="noreferrer"> 中的 Perl 版本中修复fecc6f3a68< /a> ("add -p: 当跳过一个块时调整后续块的偏移量", 2018-03-01, Git v2.17.0-rc0 -- 合并)。

这个补丁在 git add -p 的 C 版本中修复了这个问题。

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

注意:虽然 C 版本现阶段不支持暂存模式更改,但我们已经为此做好准备,如果旧的和新的偏移量均为 0,则只需跳过 hunk 标头(这对于常规 hunk 不会发生,我们将使用这表明我们正在寻找一个特殊的帅哥)。

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


在 Git 2.27(2020 年第 2 季度)之前,允许用户拆分补丁块,而“git stash -p”效果不佳;添加了创可贴以使其(部分)发挥更好的作用。

请参阅 提交 7723436提交 121c0d4(2020 年 4 月 8 日),作者:Johannes Schindelin (dscho)
(由 Junio C Hamano -- gitster -- 合并于 提交 e81ecff,2020 年 4 月 28 日)

stash -p:(部分)修复错误关于分裂帅哥

签字人:Johannes Schindelin

当尝试通过分割一个大块然后仅部分接受分割的片段来存储部分工作树更改时,用户会遇到一个相当神秘的错误:

错误:补丁失败:<文件>:<行>
错误:测试:补丁不适用
无法删除工作树更改

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

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

解释:当分割一个块时,更改的行不再被超过 3 行分隔(这是 Git 的 diff 默认使用的上下文行数),而是少于 3 行。

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

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

<前><代码>我们

人们

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

<前><代码>我们


种类
人们

并且用户尝试存储包含“are”的行,然后该命令将在内部将此行暂存在临时索引文件中,并尝试恢复 HEAD 和该索引文件之间的差异。
git stash 尝试恢复的 diff 块看起来有点像这样:

<前><代码>@@ -1776,3 +1776,4
我们
+是

人们

现在很明显,尾随上下文行与用户不想隐藏的原始差异块的部分重叠。

请记住,差异中的上下文行的主要目的是当差异不精确应用时查找确切位置(但是当要修补的文件中的确切行号与差异中指示的行号不同时) ,我们通过减少上下文行的数量来解决这个问题:差异刚刚生成。

注意:这不是问题的完整修复。
正如 t3701 的“add -p 适用于病态上下文行”测试用例所示,diff 格式存在歧义。当然,在实践中很少会遇到这样的重复行。

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


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

请参阅 提交 324efcf(2020 年 9 月 7 日),作者:Phillip Wood (<代码>菲利普伍德
(由 Junio C Hamano -- gitster -- 合并于 提交 3ad8d3e,2020 年 9 月 18 日)

添加-p:修复内存泄漏

签字人:Phillip Wood
确认人:约翰内斯·辛德林

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

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

git stash -p should fail less with Git 2.17 (Q2 2018).
Before that, "git add -p" (which shares logic with git stash) has been lazy in coalescing split patches before passing the result to underlying "git apply", leading to corner case bugs; the logic to prepare the patch to be applied after hunk selections has been tightened.

See commit 3a8522f, commit b3e0fcf, commit 2b8ea7f (05 Mar 2018), commit fecc6f3, commit 23fea4c, commit 902f414 (01 Mar 2018), and commit 11489a6, commit e4d671c, commit 492e60c (19 Feb 2018) by Phillip Wood (phillipwood).
(Merged by Junio C Hamano -- gitster -- in commit 436d18f, 14 Mar 2018)

add -p: adjust offsets of subsequent hunks when one is skipped

(add, but again, can be applied to stash)

Since commit 8cbd431 ("git-add--interactive: replace hunk
recounting with apply --recount", 2008-7-2, Git v1.6.0-rc0) if a hunk is skipped then we rely on the context lines to apply subsequent hunks in the right
place.

While this works most of the time it is possible for hunks to
end up being applied in the wrong place.

To fix this adjust the offset
of subsequent hunks to correct for any change in the number of
insertions or deletions due to the skipped hunk. The change in offset
due to edited hunks that have the number of insertions or deletions
changed is ignored here, it will be fixed in the next commit.

You can see some tests here.


Git 2.19 improves git add -p: when user edits the patch in "git add -p" and the user's editor is set to strip trailing whitespaces indiscriminately, an empty line that is unchanged in the patch would become completely empty (instead of a line with a sole SP on it).
The code introduced in Git 2.17 timeframe failed to parse such a patch, but now it learned to notice the situation and cope with it.

See commit f4d35a6 (11 Jun 2018) by Phillip Wood (phillipwood).
(Merged by Junio C Hamano -- gitster -- in commit 5eb8da8, 28 Jun 2018)

add -p: fix counting empty context lines in edited patches

recount_edited_hunk() introduced in commit 2b8ea7f ("add -p:
calculate offset delta for edited patches", 2018-03-05, Git v2.17.0) required all context lines to start with a space, empty lines are not counted.
This was intended to avoid any recounting problems if the user had introduced empty lines at the end when editing the patch.

However this introduced a regression into 'git add -p' as it seems it is common for editors to strip the trailing whitespace from empty context lines when patches are edited thereby introducing empty lines that should be
counted.
'git apply' knows how to deal with such empty lines and POSIX states that whether or not there is an space on an empty context line is implementation defined (see diff command).

Fix the regression by counting lines that consist solely of a newline
as well as lines starting with a space as context lines and add a test
to prevent future regressions.


Git 2.23 (Q3 2019) improves the git add -p, used by "git checkout -p" which needs to selectively apply a patch in reverse: it did not work well before.

See commit 2bd69b9 (12 Jun 2019) by Phillip Wood (phillipwood).
(Merged by Junio C Hamano -- gitster -- in commit 1b074e1, 09 Jul 2019)

add -p: fix checkout -p with pathological context

Commit fecc6f3 ("add -p: adjust offsets of subsequent hunks when one is
skipped", 2018-03-01, Git v2.17.0-rc0) fixed adding hunks in the correct place when a previous hunk has been skipped.

However it did not address patches that are applied in reverse.

In that case we need to adjust the pre-image offset so that when apply reverses the patch the post-image offset is adjusted correctly.
We subtract rather than add the delta as the patch is reversed (the easiest way to think about it is to consider a hunk of deletions that is skipped - in that case we want to reduce offset so we need to subtract).


With Git 2.25 (Q1 2020), The effort to move "git-add--interactive" Perl script to C continues.

As a result, the fixes mentioned above are re-implemented.

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, commit 1942ee4, commit f6aa7ec (13 Dec 2019) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit 45b96a6, 25 Dec 2019)

built-in add -p: adjust hunk headers as needed

Signed-off-by: Johannes Schindelin

When skipping a hunk that adds a different number of lines than it removes, we need to adjust the subsequent hunk headers of non-skipped hunks: in pathological cases, the context is not enough to determine precisely where the patch should be applied.

This problem was identified in 23fea4c240 ("t3701: add failing test for pathological context lines", 2018-03-01, Git v2.17.0-rc0 -- merge ) and fixed in the Perl version in fecc6f3a68 ("add -p: adjust offsets of subsequent hunks when one is skipped", 2018-03-01, Git v2.17.0-rc0 -- merge).

And this patch fixes it in the C version of git add -p.

In contrast to the Perl version, we try to keep the extra text on the hunk header (which typically contains the signature of the function whose code is changed in the hunk) intact.

Note: while the C version does not support staging mode changes at this stage, we already prepare for this by simply skipping the hunk header if both old and new offset is 0 (this cannot happen for regular hunks, and we will use this as an indicator that we are looking at a special hunk).

Likewise, we already prepare for hunk splitting by handling the absence of extra text in the hunk header gracefully: only the first split hunk will have that text, the others will not (indicated by an empty extra text start/end range). Preparing for hunk splitting already at this stage avoids an indentation change of the entire hunk header-printing block later, and is almost as easy to review as without that handling.


Before Git 2.27 (Q2 2020), allowing the user to split a patch hunk while "git stash -p" does not work well; a band-aid has been added to make this (partially) work better.

See commit 7723436, commit 121c0d4 (08 Apr 2020) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit e81ecff, 28 Apr 2020)

stash -p: (partially) fix bug concerning split hunks

Signed-off-by: Johannes Schindelin

When trying to stash part of the worktree changes by splitting a hunk and then only partially accepting the split bits and pieces, the user is presented with a rather cryptic error:

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

and the command would fail to stash the desired parts of the worktree changes (even if the stash ref was actually updated correctly).

We even have a test case demonstrating that failure, carrying it for four years already.

The explanation: when splitting a hunk, the changed lines are no longer separated by more than 3 lines (which is the amount of context lines Git's diffs use by default), but less than that.

So when staging only part of the diff hunk for stashing, the resulting diff that we want to apply to the worktree in reverse will contain those changes to be dropped surrounded by three context lines, but since the diff is relative to HEAD rather than to the worktree, these context lines will not match.

Example time. Let's assume that the file README contains these lines:

We
the
people

and the worktree added some lines so that it contains these lines instead:

We
are
the
kind
people

and the user tries to stash the line containing "are", then the command will internally stage this line to a temporary index file and try to revert the diff between HEAD and that index file.
The diff hunk that git stash tries to revert will look somewhat like this:

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

It is obvious, now, that the trailing context lines overlap with the part of the original diff hunk that the user did not want to stash.

Keeping in mind that context lines in diffs serve the primary purpose of finding the exact location when the diff does not apply precisely (but when the exact line number in the file to be patched differs from the line number indicated in the diff), we work around this by reducing the amount of context lines: the diff was just generated.

Note: this is not a full fix for the issue.
Just as demonstrated in t3701's 'add -p works with pathological context lines' test case, there are ambiguities in the diff format. It is very rare in practice, of course, to encounter such repeated lines.

The full solution for such cases would be to replace the approach of generating a diff from the stash and then applying it in reverse by emulating git revert (i.e. doing a 3-way merge). However, in git stash -p it would not apply to HEAD but instead to the worktree, which makes this non-trivial to implement as long as we also maintain a scripted version of add -i.


Git 2.29 (Q4 2020) brings a leakfix to git add -p (used by stash -p)

See commit 324efcf (07 Sep 2020) by Phillip Wood (phillipwood).
(Merged by Junio C Hamano -- gitster -- in commit 3ad8d3e, 18 Sep 2020)

add -p: fix memory leak

Signed-off-by: Phillip Wood
Acked-by: Johannes Schindelin

asan reports that the C version of add -p is not freeing all the memory it allocates.

Fix this by introducing a function to clear struct add_p_state`` and use it instead of freeing individual members.

请帮我爱他 2024-10-25 06:34:08

git stash -p 以同样的方式失败后,我很幸运地使用了这个解决方法(git 2.0.2):

  • git add -p,拆分完全相同的内容帅哥,但答案相反(“y”到add“保留”更改,“n”到stash保留更改。)
  • git stash -k保留索引并存储其他所有内容
  • git重置以继续处理我的文件

我不确定为什么git add -p没有以与git add -p相同的方式失败code>git stash -p 做到了。我想是因为添加与索引一起工作而不是创建补丁文件?

After just having a git stash -p fail in this same way, I had luck with this workaround (git 2.0.2):

  • git add -p, splitting the exact same hunks but with inverse answers ("y" to add "keeps" changes, "n" to stash keeps changes.)
  • git stash -k to keep the index and stash everything else
  • git reset to continue working on my files

I'm not sure why git add -p didn't fail in the same way that git stash -p did. I guess because adding works with the index rather than creating a patch file?

纸短情长 2024-10-25 06:34:08

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

如果像我一样,您花费了大量精力来构建完美的存储,并且不想放弃这些努力,那么仍然可以通过以下方式大部分获得您想要的东西:

git stash show -p | patch -p1 -R

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

The accepted answer at the moment can still unfortunately fail, even in Git 2.17.

If, like me, you spent a lot of effort constructing the perfect stash and don't want to throw that effort away, it is still possible to mostly get what you want with:

git stash show -p | patch -p1 -R

This will fail with rejects, but odds are good most of the hunks will apply correctly and at least save you the time of reviewing all the files again.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文