什么导致git-apply出现“损坏的补丁”错误?

16

(长话短说...)

我正在将一个巨大的PHP应用程序更新到现代化时代...;-)...而其他团队则继续维护其现有版本。

到目前为止,这已经导致了大约275个补丁。问题是,我们所做的更改之一是将<?标记转换为<?php,并在代码中进行了几个类似的更改。所有这些当然都会防止应用补丁,因为源代码确实不匹配。

因此,我想编写一个小脚本来编辑补丁文件:更改补丁中的标记。

然而,我得到的是corrupt patch

那么,我想知道的是:是什么导致了这个消息?也就是说,当Git出现这个消息时,它寻找什么样的错误?我需要“调整我的调整器”...咳咳...以便编辑后的补丁能够正常工作。(注意:在我对其进行调整之前,原始的补丁文件并不“损坏”,因此必须是我做的某些事情。)

我的脚本试图更改前述的PHP标记和<?php echo和一个函数名。仅仅是一次全局preg-replace。我并不容易看出我可能会搞砸的内容与Git相关的“结构性问题”,但显然,有些东西确实存在。

示例补丁:corrupt patch at line 37...

From 342c5939da8cf4cbe495be7d635cd627bd2a44ed Mon Sep 17 00:00:00 2001
From: xxx <xxx@yyy.com>
Date: Wed, 17 Feb 2016 03:45:31 +0000
Subject: [PATCH 001/275] Make it all work 


---
 catalog/includes/modules/shipping/upsFreeGround.php | 2 +-
 catalog/product_info_v3.php                         | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/catalog/includes/modules/shipping/upsFreeGround.php b/catalog/includes/modules/shipping/upsFreeGround.php
index 45a6da4..55ccecb 100755
--- a/catalog/includes/modules/shipping/upsFreeGround.php
+++ b/catalog/includes/modules/shipping/upsFreeGround.php
@@ -194,7 +194,7 @@ function quote($method = '') {

         // Can probably combine this with the above, eventually
         $allFreeBW2016Plaques = false;
-           if (STORES_ID == 10) {
+           if ((STORES_ID == 10) || (STORES_ID == 26)) {
             $allFreeBW2016Plaques = true;
             foreach ($order->products as $aProduct) {
                    $thisNote = $aProduct['product_specific_notes'];
diff --git a/catalog/product_info_v3.php b/catalog/product_info_v3.php
index 09d88de..10d9b76 100644
--- a/catalog/product_info_v3.php
+++ b/catalog/product_info_v3.php
@@ -186,7 +186,7 @@ function doRequestComplete() {
        }
    }
 <?php -if ((STORES_ID == 10) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
+if (((STORES_ID == 10) || (STORES_ID == 26)) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
 function doCheckBW2016PlaqueProperty() {
    thePropertyNumber = document.getElementById('propertyToCheck');
    if (thePropertyNumber.value == "") {
@@ -1426,7 +1426,7 @@ if($combo_count>0) { ?>
                        ?>
                        </div> <!-- div_add_to_cart -->
            </div> <!-- cart_info_row2 -->
-           <?php if ((STORES_ID == 10) && (in_array( $products_id, $bwFreePlaqueIDList2016))) {
+           <?php if (((STORES_ID == 10) || (STORES_ID == 26)) && (in_array( $products_id, $bwFreePlaqueIDList2016))) {
                // First, let's see if we are "sold out"
                $query = "select bw_plaque_2016_id from bw_plaque_2016 where first_one_free='1' limit 1";
                $bwpRes = tep_db_query( $query);
@@ -1629,7 +1629,7 @@ DIVCONTAINER;
 </table> <!--pageTable for sure -->

 <script type='text/javascript'>
-   <?php if ((STORES_ID == 10) && (in_array( $products_id, $bwFreePlaqueIDList2016))) { ?>
+   <?php if (((STORES_ID == 10) || (STORES_ID == 26)) && (in_array( $products_id, $bwFreePlaqueIDList2016))) { ?>
    function doFreePlaquePriceChange() {
        // Change the quantity to 1
        $('quantityToAdd').setValue('1');
-- 
2.6.4 (Apple Git-63)

补丁文件中是否包含SHA1哈希值? - melpomene
我不确定我应该寻找什么。你能告诉我在补丁文件中这样的东西会是什么样子吗?有可能让apply忽略任何这样的哈希值吗?(我知道补丁可能包含反腐败检查...但如果是这样,我想在这种情况下忽略它。) - Mike Robinson
帖子的文本已添加补丁。命令是 git apply --check -v --directory=foobar。我原本希望 -v 选项能告诉我更多关于损坏的信息,但它并没有。 - Mike Robinson
根据我的计算,第37行读取:@@ -1426,7 +1426,7 @@ if($combo_count>0) { ?> - Mike Robinson
1
在我的情况下,损坏的补丁缺少空格,否则空白上下文行(实际更改周围的行以 +- 为前缀的行显然必须具有前导空格,即使它们是空白的)。 它还在最后缺少了一个换行符。 GitHub 删除了这些空格(我从评论中复制了差异并将其粘贴到文本文件中)。 - CodeManX
显示剩余4条评论
4个回答

17

简而言之:我怀疑在转换仅包含<?的行时,您可能已经删除了行末标记。

您的脚本可能已经在补丁中删除了重要的一行,或者您已经更改了标题。 catalog/product_info_v3.php的第一个差异块是格式错误的。

它的标题是:

@@ -186,7 +186,7 @@ function doRequestComplete() {

这说明了有关代码块与preimage(原始文件)和postimage(应用此补丁后生成的文件)对应关系的信息。前置图像信息以-为前缀,是-186,7,表示此代码块包含从第186行开始的7行前置图像。后置图像信息以+为前缀,是+186,7,表示此代码块将在后置图像中发出7行,从第186行开始。

这7行代码既可以包括上下文(相同的行前缀为空格),也可以是仅存在于前置图像中的行(前缀为-),或仅存在于后置图像中的行(前缀为+)。

查看标记每行类型的代码块:

  context:         }
  context:     }
  context:  <?php -if ((STORES_ID == 10) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
postimage: +if (((STORES_ID == 10) || (STORES_ID == 26)) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
  context:  function doCheckBW2016PlaqueProperty() {
  context:     thePropertyNumber = document.getElementById('propertyToCheck');
  context:     if (thePropertyNumber.value == "") {

上下文行将出现在前像和后像中,因此前像有6行,后像有7行。

但是您的标题说前像有7行!所以要么标题是错误的,要么说明有误。 (Git预计在第37行看到另一行前像,但实际上是一个新的标题行,因此Git确定您的补丁文件已损坏。)

此补丁中的行表明您正在向后像添加if行,该行在前像中不存在。 如果是这样,则您已经破坏了标题,它应为-186,6 +186,7,表示正在向后像添加一行。

或者,如果您要更改if行,则省略了其前像状态,应在后像行上面添加一行。

仔细查看,您实际上错过了在上面一行的<?php之后的换行符。 您的上下文可能不是<?php -if ...,因为我怀疑在if前面加上连字符对于PHP来说是无效的。

怀疑这个块应该看起来像:

@@ -186,7 +186,7 @@ function doRequestComplete() {
       }
   }
 <?php
-if ((STORES_ID == 10) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
+if (((STORES_ID == 10) || (STORES_ID == 26)) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
 function doCheckBW2016PlaqueProperty() {
    thePropertyNumber = document.getElementById('propertyToCheck');
    if (thePropertyNumber.value == "") {

现在有 7 行原像(包括前缀为 - 的只存在于原像中的那一行),以及 7 行后像(包括前缀为 + 的只存在于后像中的那一行),都没有被篡改。

因此,我们现在拥有一个合法的补丁,因为指令与标题相匹配。


1
可恶...“点赞”按钮只能使用一次。**:-D** “无价之宝!”现在,让我花半个小时吸收你刚才说的话。### “啊哈,我发现了些什么...” - Mike Robinson

12

当我尝试复制(Ctrl+C -> Ctrl+V)补丁时,出现了这个错误:

$ git patch
[Ctrl+Shift+V to paste in terminal]
[Ctrl+D to end input]

在最后一行会提示我补丁文件已损坏。

解决方法是在使用Ctrl+D结束输入之前添加一个Enter。缺少了最后的空白行。


这解决了我的问题。在运行 git diff 后从终端复制时,确保在选择时将光标放在提示符的第一个字母之前,并正确获取最后一行反斜杠。 - Jesse Hogan
1
与此答案类似,当从终端中复制git diff输出的一块以便使用(在Mac上)pbpaste | git apply -时,只有当我小心地选择包括补丁上下文的最后一个换行符(鼠标位于下一个@@行的左侧)时才能正常工作。如果我仅选择到最后一行的末尾而没有包括它的换行符,则会收到损坏的补丁消息。 - Joshua Goldberg
我还想指出,在git apply的文档中看起来很有前途的选项,以放宽对修补程序上下文准确行数的要求,但实际上并没有起到帮助的作用。它仍然需要完全包含整个上下文,包括最后的换行符。(我尝试过--ignore-eof、-C1、--reject和--recount) - Joshua Goldberg

1
追寻Edward的最佳答案,我仔细研究了“before”和“after”补丁文件。虽然我还不确定我的正则表达式是如何实现的,但显然它所做的是:原始补丁文件:
<?
-if ((STORES_ID == 10) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
+if (((STORES_ID == 10) || (STORES_ID == 26)) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>

...并且在我的正则表达式处理后的那个:

 <?php -if ((STORES_ID == 10) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
+if (((STORES_ID == 10) || (STORES_ID == 26)) && (in_array($products_id, $bwFreePlaqueIDList2016))) { ?>
 function doCheckBW2016PlaqueProperty() {

问题出在哪里?“换行符”不见了。

现在我感到既困惑又有点傻,我看着我的(PHP)代码:

$content = preg_replace('/\<\?\s*(?=[^xp=])/', '<?php ', $content);

and-d-d-d...,"就是它。":/

这个明显有误的正则表达式的目的是使用“零长度正向先行断言”(?=来查找尚未为<?php<?xml<?标签。

(啪!)“呕!”

我正在使用一个正向先行断言来测试字符是否不属于以下列表。在这种情况下,我应该使用一个负向先行断言:(?![px=])。我相信这样就能避免换行符被消耗掉。(我没有一行一行地读取文件。我只是将整个文件都读入了内存中。)

编辑 #2: ... 然后,在这么多毫秒后,正确的答案将出现在评论 #1 中: \s* 吞噬了换行符,并且负向先行断言(似乎根本不起作用)是不必要的。 (实际上,\s* 根本不应该存在!)再次使用正向先行断言,删除 \s*,并将其替换为仅为 <?php 而不是后面跟着一个空格的相同内容...脚本就可以工作:

$content = preg_replace('/\<\?(?=[^xp=])/', '<?php', $content);

非常真诚和热情的感谢所有帮助我解决这个问题的人。 (特别是Edward,他详细地向我们解释了这些补丁文件的格式!)


2
问题在于你的\s*匹配了\n。正向预查没问题。要么不匹配换行符,要么使用(\s*)并将匹配嵌入替换中。 - maaartinus

0

以下是我遇到这个问题的过程:

  1. 我对一些文件进行了修改,然后发现我需要撤销其中一个文件的修改。
  2. 我尝试检出该文件,但我忘记指定文件,所以我使用了 git checkout .
  3. 在检出之前,我使用 git diff 查看了修改内容,因此我从控制台复制了差异输出并创建了补丁文件。
  4. 然后我使用 git apply 应用了补丁文件,结果出现了这个问题。

如何解决它。 将补丁文件格式从 DOS 转换为 Unix。(通过 vim,set ff=unix


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