【问题标题】:Why git apply fails when /usr/bin/patch succeeds?当 /usr/bin/patch 成功时,为什么 git apply 失败?
【发布时间】:2021-04-21 16:41:12
【问题描述】:

我经常遇到这种情况:我有一个使用git format-patch 生成的补丁,我正在使用git apply 应用它,但它失败并显示“补丁不适用”消息。

然后我很惊讶,尝试用patch -p1 -i … 应用它,它成功了,没有任何问题,只有几个大块偏移量(但大到例如:300 行)。

我想知道为什么会这样?我已经将--recount 选项与apply 一起使用,所以它也应该接受大块偏移。我什至使用-3 进行三路合并,这对 50% 的情况有帮助。

我还想说明这个补丁是多么简单(附后)——这是它的摘要:

 lib/keybind.c          |  1 +
 lib/keybind.h          |  1 +
 misc/mc.default.keymap |  1 +
 misc/mc.emacs.keymap   |  1 +
 src/editor/edit-impl.h |  1 +
 src/editor/edit.c      | 21 +++++++++++++++++++++
 src/keybind-defaults.c |  1 +

我正在应用它与neo-mc 的当前 HEAD。补丁已经在那里合并了,但是首先我正在应用反向补丁(不足为奇:使用patch),然后尝试从文件中应用它(附在下面)。

那么为什么git apply 不能正确处理这个简单的补丁?不能 git 程序员编码或至少复制一个像样的工具(即:patch)?还是我错过了什么?

我也遇到过类似的重要问题:使用format-patch 生成的补丁即使使用patch 也无法应用,所以我使用了git diff master..BRANCH > adhoc.patch 并且它起作用了!我能够使用/usr/bin/patch 应用补丁,我什至没有看format-patch/apply 组合。

这是git apply -3 --recount patches/CenterView_v3.patch的输出:

error: patch failed: src/editor/edit-impl.h:233
Falling back to three-way merge...
Applied patch to 'src/editor/edit-impl.h' cleanly.
error: patch failed: src/editor/edit.c:3766
error: repository lacks the necessary blob to fall back on 3-way merge.
error: src/editor/edit.c: patch does not apply

这里来自patch -p1 -i …

patching file lib/keybind.c
Reversed (or previously applied) patch detected!  Assume -R? [n] y
Hunk #1 succeeded at 69 (offset 5 lines).
patching file lib/keybind.h
Reversed (or previously applied) patch detected!  Assume -R? [n] y
Hunk #1 succeeded at 57 (offset 2 lines).
patching file misc/mc.default.keymap
Reversed (or previously applied) patch detected!  Assume -R? [n] y
Hunk #1 succeeded at 278 (offset 3 lines).
patching file misc/mc.emacs.keymap
Reversed (or previously applied) patch detected!  Assume -R? [n] y
Hunk #1 succeeded at 277 (offset 2 lines).
patching file src/editor/edit-impl.h
Reversed (or previously applied) patch detected!  Assume -R? [n] y
Hunk #1 succeeded at 280 with fuzz 1 (offset 47 lines).
patching file src/editor/edit.c
Reversed (or previously applied) patch detected!  Assume -R? [n] y
Hunk #1 succeeded at 3281 (offset 258 lines).
Hunk #2 succeeded at 4085 with fuzz 1 (offset 319 lines).
patching file src/keybind-defaults.c
Reversed (or previously applied) patch detected!  Assume -R? [n] y
Hunk #1 succeeded at 431 (offset 11 lines).

补丁内容:

From b7f010902585692326161e6e4b16fcd9a9378854 Mon Sep 17 00:00:00 2001
From: Sebastian Gniazdowski <sgniazdowski@gmail.com>
Date: Sat, 23 Jan 2021 23:16:37 -0600
Subject: CenterView action

---
 lib/keybind.c          |  1 +
 lib/keybind.h          |  1 +
 misc/mc.default.keymap |  1 +
 misc/mc.emacs.keymap   |  1 +
 src/editor/edit-impl.h |  1 +
 src/editor/edit.c      | 21 +++++++++++++++++++++
 src/keybind-defaults.c |  1 +
 7 files changed, 27 insertions(+)

diff --git a/lib/keybind.c b/lib/keybind.c
index abd44d3e2..9ab8dd5a6 100644
--- a/lib/keybind.c
+++ b/lib/keybind.c
@@ -64,6 +64,7 @@ static name_keymap_t command_names[] = {
     ADD_KEYMAP_NAME (PageDown),
     ADD_KEYMAP_NAME (HalfPageUp),
     ADD_KEYMAP_NAME (HalfPageDown),
+    ADD_KEYMAP_NAME (CenterView),
     ADD_KEYMAP_NAME (Top),
     ADD_KEYMAP_NAME (Bottom),
     ADD_KEYMAP_NAME (TopOnScreen),
diff --git a/lib/keybind.h b/lib/keybind.h
index af019df09..1cef8138d 100644
--- a/lib/keybind.h
+++ b/lib/keybind.h
@@ -55,6 +55,7 @@ enum
     CK_PageDown,
     CK_HalfPageUp,
     CK_HalfPageDown,
+    CK_CenterView,
     CK_Top,
     CK_Bottom,
     CK_TopOnScreen,
diff --git a/misc/mc.default.keymap b/misc/mc.default.keymap
index 2931ddd0a..fe9b5adb1 100644
--- a/misc/mc.default.keymap
+++ b/misc/mc.default.keymap
@@ -275,6 +275,7 @@ End = end
 Tab = tab; shift-tab; ctrl-tab; ctrl-shift-tab
 Undo = ctrl-u
 Redo = alt-r
+CenterView = alt-c
 Top = ctrl-home; alt-lt
 Bottom = ctrl-end; alt-gt
 ScrollUp = ctrl-up
diff --git a/misc/mc.emacs.keymap b/misc/mc.emacs.keymap
index 7cc305db7..9d8ee0fd8 100644
--- a/misc/mc.emacs.keymap
+++ b/misc/mc.emacs.keymap
@@ -275,6 +275,7 @@ End = end; ctrl-e
 Tab = tab; shift-tab; ctrl-tab; ctrl-shift-tab
 Undo = ctrl-u
 # Redo =
+CenterView = alt-c
 Top = ctrl-home; alt-lt
 Bottom = ctrl-end; alt-gt
 ScrollUp = ctrl-up
diff --git a/src/editor/edit-impl.h b/src/editor/edit-impl.h
index 3ad04dbea..69f8c1683 100644
--- a/src/editor/edit-impl.h
+++ b/src/editor/edit-impl.h
@@ -233,6 +233,7 @@ void edit_save_size (WEdit * edit);
 gboolean edit_handle_move_resize (WEdit * edit, long command);
 void edit_toggle_fullscreen (WEdit * edit);
 void edit_move_to_line (WEdit * e, long line);
+void edit_center_display (WEdit * e, long diff);
 void edit_move_display (WEdit * e, long line);
 void edit_word_wrap (WEdit * edit);
 int edit_sort_cmd (WEdit * edit);
diff --git a/src/editor/edit.c b/src/editor/edit.c
index 50879cee2..9146069a2 100644
--- a/src/editor/edit.c
+++ b/src/editor/edit.c
@@ -3023,6 +3023,22 @@ edit_move_to_line (WEdit * e, long line)
     edit_scroll_screen_over_cursor (e);
 }
 
+/* --------------------------------------------------------------------------------------------- */
+/** scroll window so that current line is in center; the diff is a relative offset from that
+  * position */
+
+void
+edit_center_display (WEdit * e, long diff)
+{
+    int center_line_diff = WIDGET (e)->lines / 2 + diff;
+    int current_line = e->curs_row;
+
+    if (current_line < center_line_diff)
+        edit_scroll_upward (e, center_line_diff - current_line);
+    else
+        edit_scroll_downward (e, current_line - center_line_diff);
+}
+
 /* --------------------------------------------------------------------------------------------- */
 /** scroll window so that first visible line is 'line' */
 
@@ -3766,6 +3782,11 @@ edit_execute_cmd (WEdit * edit, long command, int char_for_insertion)
         }
         break;
 
+    case CK_CenterView:
+        /* Center view at cursor line. */
+        edit_center_display (edit, 0);
+        break;
+
     case CK_Top:
     case CK_MarkToFileBegin:
         edit_move_to_top (edit);
diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c
index 7b87c2f5a..ad4b59780 100644
--- a/src/keybind-defaults.c
+++ b/src/keybind-defaults.c
@@ -420,6 +420,7 @@ static const global_keymap_ini_t default_editor_keymap[] = {
     {"Goto", "alt-l; alt-shift-l"},
     {"Refresh", "ctrl-l"},
     {"Shell", "ctrl-o"},
+    {"CenterView", "alt-c"},
     {"Top", "ctrl-home; ctrl-pgup; alt-lt"},
     {"Bottom", "ctrl-end; ctrl-pgdn; alt-gt"},
     {"TopOnScreen", "ctrl-pgup"},
-- 
2.28.0

【问题讨论】:

    标签: git diff patch


    【解决方案1】:

    (一个执行摘要 TL;DR:补丁不是提交;格式补丁用于提交。旧的 patch 命令是关于补丁的,git apply 是介于过渡工具和 @ 的一部分之间的东西987654323@.)

    这里有几个关键问题:

    • 从您的patch 输出看来,补丁的某些部分已经应用,而有些则没有。 patch 命令将询问是否对每个块使用反转选项,但git apply 要求反转应用于 每个 块,或 no 块。所以它不会像这里的补丁那样做。

    • 鉴于某些补丁因此不会应用,git apply 需要--reject 选项来应用剩余的补丁。 patch 命令假定等效(总是愿意创建一个.rej 文件)。 (在您的情况下,让补丁取消应用一半的差异并应用另一半来代替。)

    • patch 假定默认模糊因子为 2;您的几个补丁适用于模糊。模糊因子是patch 允许忽略 的上下文行数。默认情况下,git apply 要求 所有 上下文行匹配:这相当于 patch 中的 -F 0。您可以使用-C 参数在git patch 中获得模糊因子的效果。

    因此,如果您运行 git apply -C 1 --reject,则应用此补丁可能会遭到拒绝。结果与您从patch 获得的结果不匹配,除非您对每次反转都说不。

    (旁注:--recount 真正用于编辑补丁,而不是用于上下文搜索调整。)

    关于格式补丁

    我也遇到过类似的重要问题:使用format-patch 生成的补丁即使使用patch 也无法应用,所以我使用了git diff master..BRANCH &gt; adhoc.patch 并且成功了!!

    这既不足为奇(因为它发生了),也值得密切关注(当它发生时),因为这意味着你没有得到你希望得到的东西。仔细观察,看看 是什么,然后用它来决定如何得到你想要的——不管最终会是什么。

    git format-patch 所做的是将一些普通提交(即一些单父提交)或一些此类提交系列转换为包含足够信息以重构每个提交的电子邮件文本文件.1git am 命令进行重建。稍后重建这些提交,例如,从邮箱格式输入文件,通常会保留原作者,但会产生不同的提交者。)

    git diff 所做的是获取任意两个任意提交2比较它们 并生成一个补丁,如果应用该补丁,则左侧提交给出的快照转换为右侧提交给出的快照。在这里,两个提交是那些在master 顶端的提交,以及那些在BRANCH 顶端的提交。双点语法 git diff master..BRANCH 逐渐被弃用,取而代之的是 git diff master BRANCH 语法,但两者的含义完全相同。

    请注意,单个git format-patch 命令可能会发出no 组提交,该命令会将tip-of-master 快照转换为tip-of-@987654353 @快照。例如,假设我们有以下提交:

              I--J--K   <-- master
             /
    ...--G--H
             \
              L--M--N   <-- BRANCH
    

    git format-patch 命令可以一次性格式化L-M-N 补丁(生成三个可通过电子邮件发送的重建文件),但这些需要应用于提交H 以重现匹配L 的新提交,@ 987654359@ 和 N。或者,git format-patch 可以一次性格式化I-J-K 补丁;但是这些也需要应用于提交H,而不是提交K,以便产生提交K

    如果您拥有是与提交 K 匹配的快照(不是 Git 存储库,只是快照),您可以使用三个补丁将 H 转换为 K逆向 他们在本地获得另外三个快照,相当于JI,然后是H;然后,您可以在正常的向前方向上添加生成LM 和最后N 的三个补丁。

    但是,从上图中我们可以看出,没有commit 可以让我们一步一步从提交K 提交N。要获得一个patch 让我们一步到位,我们必须 diff 直接提交KN。这样的补丁无法重新创建之前的任何提交:如果我们只有K 附带的快照,并且我们应用此补丁,我们将获得N 附带的快照,但我们不知道@987654381 是什么例如,@ 看起来像。

    如果某个补丁(或某些格式补丁输出)的接收者拥有适当的 Git 存储库,他们可以应用格式补丁输出,只要他们具有所需的基本提交,并获得等效的提交(尽管有不同的提交者信息)。例如,这对于仔细审查特别有帮助。但是,如果接收者没有必要的提交——甚至可能根本没有 Git 存储库——format-patch 结果就没有多大帮助,而且一个简单的一次性git diff 补丁可能是最好的。详细信息因收件人而异。


    1format-patch 命令还可以生成单个输出流 (--stdout),适合直接馈送到 git am,但现在它更适用于生成一个充满可发送电子邮件的文件,适合发送给git send-email。这适用于各种 Linux 小组、Git 项目本身等使用的电子邮件补丁方法。

    2git diff 命令可以接受其他不一定是提交的输入,但在这种情况下 (master..BRANCH),我们获得了比较两个提交的模式。

    【讨论】:

    • 感谢您的回答。我明白你对第二个子问题的意思——让我们关注第一个,因为它是完全可重现的。您错过了第一个问题中的一段 - 指出我首先反转补丁然后使用git apply 重新应用它。因此,patch 的输出是此反转的(意外)日志,而不是重新应用的输出。但是,如果patch 能够reverse 补丁,那么它肯定也能够应用它。所以基本上,git apply 无法重新应用刚刚被patch 逆转的补丁......
    • 啊。在使用带有patch 的反向补丁“取消补丁”之后,您仍然会发现此补丁不适用,因为 contexts 不匹配。 patch 程序允许使用 fuzz,但 git apply 不允许:您需要 -C。看起来两个补丁没有反转,所以我认为你仍然需要--reject
    • 谢谢,使用-C 1 应用的补丁。我想知道这是否与补丁的绒毛一样?模糊不是意味着上下文仍然匹配,但模糊线早/晚,而-C 1 忽略了大部分?
    • -C 选项使git patch 完全忽略了一些上下文行,这不像patch 的fuzz 选项那么花哨。 patch 的 fuzz 选项设置补丁将 (a) 抛出但随后 (b) 报告已抛出的上下文行的数量,因此您可以看到有多少。如果上下文行的实际数量为 3 并且补丁显示“with fuzz 2”,则补丁丢弃了 3 个中的 2 个,因此 -C 1 将起作用;例如,如果是 10 个上下文行,并且补丁显示“fuzz 7”,您会想要 -C 3,等等。
    • 因此-C 1 会降到补丁可能。可能过度杀伤并且可能过度匹配,几乎就像您正在应用非上下文补丁(但不是相当那么糟糕)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 2016-07-19
    • 1970-01-01
    • 2020-05-18
    相关资源
    最近更新 更多