Git am 错误:"补丁不适用"。

102

我正在尝试使用git将一个项目中的多个提交移动到第二个类似的项目中。

因此,我创建了一个包含5个提交的补丁:

git format-patch 4af51 --stdout > changes.patch

然后将补丁移动到第二个项目的文件夹中,并希望应用该补丁:

Then move the patch to second project's folder and wants to apply the patch:

git am changes.patch 

...但是它给我错误:

Applying: Fixed products ordering in order summary.
error: patch failed: index.php:17
error: index.php: patch does not apply
Patch failed at 0001 Fixed products ordering in order summary.
The copy of the patch that failed is found in:
   c:/.../project2/.git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

于是我打开了index.php文件,但是那里什么也没改变。我猜想可能会有一些>>>>>>>标记之类的东西,就像解决合并冲突时一样,但在该文件中没有标记任何冲突。 git status命令也给出了一个空的已更改文件列表(只有changes.patch)。所以我运行了git am --continue 命令,但又出现了另一个错误:

Applying: Fixed products ordering in order summary.
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort". 

我正在使用Windows 7和最新的git版本 "1.9.4.msysgit.1"。

顺便说一句,在经过几个小时的谷歌搜索后,我找到了几种解决方案,但对我来说都没有用:


git am -3 changes.patch 

出现奇怪的"sha1信息"错误:

Applying: Fixed products ordering in order summary.
fatal: sha1 information is lacking or useless (index.php).
Repository lacks necessary blobs to fall back on 3-way merge.
Cannot fall back to three-way merge.
Patch failed at 0001 Fixed products ordering in order summary.
The copy of the patch that failed is found in:
   c:/.../project2/.git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort". 

git am changes.patch --ignore-whitespace --no-scissors --ignore-space-change

给出的第一个错误如上所示: "error: patch failed: index.php:17",但在index.php中没有冲突标记。


6
我也一直在努力解决这个问题。但在某些情况下,这是由于在提交时使用了CRLF作为行尾符的文件(例如来自Windows源)所致。似乎git不喜欢CRLF行尾符,尽管有像eol.crlf这样的配置功能。关于CR/LF问题的示例链接:https://dev59.com/MXI_5IYBdhLWcg3wJPdu - Rob
1
太好了。我刚刚把我收到的补丁文件的行结尾转换成“Unix”风格,之后它就可以工作了。这值得点赞。 - user2808624
9个回答

126

什么是补丁?

补丁不过是一系列指令的简单组合:"在这里添加","在那里删除","将这个第三个内容改为第四个内容"。这就是为什么Git告诉你:

The copy of the patch that failed is found in:
c:/.../project2/.git/rebase-apply/patch
你可以使用你喜欢的查看器或编辑器打开该补丁,然后在你喜欢的编辑器中打开要更改的文件,并“手动应用”该补丁,利用你所知道的知识(而Git不知道),以弄清楚当要更改的文件现在看起来与之前更改时几乎完全不同的情况下,“添加此处”应该如何完成,这些更改以补丁的形式传递给你。
稍微详细一点
三方合并引入了比纯粹的“指令序列”更多的信息:它告诉你文件的原始版本是什么。如果你的存储库有原始版本,你的Git软件可以在你的存储库中工作,将你对文件所做的更改与补丁所要求对该文件进行的更改进行比较。
正如你在上面看到的那样,如果你请求三方合并,Git找不到其他存储库中的“原始版本”,因此它甚至无法尝试三方合并。结果就是你得不到冲突标记,必须手动执行补丁应用程序。
(有些情况下,“更多”的部分是缺少的。补丁中的“Index:”行提供了额外的信息,例如:)
diff --git a/go.mod b/go.mod
index 1fefa60..38a3a41 100644

第二行可能会是这样的,例如:

index 1fefa6021dcd205c1243e236d686595920d9621b..38a3a41434fda3a68ce3356092a89afca81eb614 100644

在更完整的情况下。请注意,由两个点分隔的两个哈希 ID。左边的是您的 Git 软件将使用的哈希 ID,以尝试在您自己的仓库中查找具有给定哈希 ID 的文件。对该文件应用提供的补丁,该文件 必须存在必须有效且必须生成其哈希 ID 为右侧的文件,并执行所有这些操作将为 Git 提供所需的所有其他信息。)

使用 --reject

当您必须手动应用补丁时,Git 仍然可以自动为您应用大多数 补丁,并仅将少量部分留给能够理解代码(或需要修补的内容)的实体。添加--reject告诉 Git 这一点,并将“不适用”的补丁部分保留在拒绝文件中。如果您使用此选项,则仍然必须手动应用每个失败的补丁,并确定如何处理被拒绝的部分。

完成所需更改后,可以git add修改后的文件,并使用git am --continue告诉Git提交更改并移到下一个补丁。

如果没有需要做的事情怎么办?

由于我们没有您的代码,因此我无法确定是否存在这种情况,但有时,您可能会发现其中一个补丁说出类似于“修复第42行单词的拼写”之类的话,而该处的拼写已经被修复。

在这种特殊情况下,您查看了补丁和当前代码后,应该对自己说:“啊哈,补丁应该完全跳过!” 这就是您使用 Git 已经打印的其他建议的时候:

If you prefer to skip this patch, run "git am --skip" instead.
如果你运行 `git am --skip`,Git 将跳过那个补丁,所以如果邮箱中有五个补丁,最终只会添加四个提交,而不是五个(如果你跳过两次,则为三个而不是五个,依此类推)。

15
感谢您的好回答。对我来说,“git am changes.patch --reject”是最好的选择,因为它提供了替代“冲突标记”(在.rej文件中找到)的方法。然后使用“git add”,“git am --continue”,一切都很顺利 :-) - nanuqcz
3
是的,谢谢。让我感到惊讶的是git am没有像patch那样尝试应用那些可以被应用的更改。 - Steven R. Loomis
有时候,Git 无法应用自己生成的补丁,这似乎很奇怪。我正在使用 git log --pretty=email --patch-with-stat --reverse -- filename - Ed Randall
3
如果您使用 git amgit apply -3,并且在您的存储库中有该文件的基本版本,则 Git 应该能够进行三方合并(当然可能存在合并冲突)。请注意,您可能需要发送补丁的人在生成补丁时使用 --full-index - torek
1
@DrumM:合并和变基始终具有基础文件,因此它们实际上不需要等效的“--reject”,因为它们始终具有等效的“-3”。话虽如此,我习惯于使用GNU风格的“patch”命令,因此我通常更喜欢“--reject”模式,但我可以看到双方的论点。 - torek
显示剩余15条评论

22

我曾经遇到过同样的问题。我已经使用过

git format-patch <commit_hash>

创建补丁的过程中,我的主要问题是由于一些冲突而导致补丁失败,但我无法在文件内容中看到任何合并冲突。我曾尝试使用git am --3way <patch_file_path>命令来应用补丁。

正确的应用补丁命令应该是:

git am --3way --ignore-space-change <patch_file_path>
如果您执行上述修补命令,则在修补应用失败时,它将创建合并冲突。然后,您可以像解决git merge的合并冲突一样,在文件中解决冲突。

1
参数“-3Way”是错误的,正确的是“-3”或“--3way”。但这并不能回答问题。 - jaques-sam
1
代码部分有一个打字错误。感谢您指出。如果您完整地阅读了答案,您可能会注意到,在文本部分中以粗体字标出了--3Way。根据问题描述,在应用补丁后合并冲突没有显示。我曾经遇到过同样的问题,并且使用了那个命令解决了它。 - srs
  1. 在“...应该是:”后面加上--,这看起来已经像是一个打字错误了,我很容易忽略--并读取下面的代码行。
  2. 只呈现正确的代码比较好,有时代码答案不包含正确答案,这很烦人;-)
  3. 原始问题中的打字错误并没有导致他的问题...
- jaques-sam
4
我不知道为什么,但是选用--ignore-space-change选项使我的补丁文件起作用了。 - Sky
这对我有用。在我的情况下,我仍然有合并冲突,但是这个命令设置了冲突文件的正常状态。我能够使用 git mergetool 解决冲突。 - esteuart

8

git format-patch也有-B标志。

虽然手册中的描述还有很大的改进空间,但简单来说,这是在执行完全重写文件之前,format-patch遵守的阈值(通过删除所有旧内容,然后插入所有新内容来完成)。

当手动编辑过于繁琐,并且源比目标更具权威性时,这对我非常有用。

一个例子:

git format-patch -B10% --stdout my_tag_name > big_patch.patch
git am -3 -i < big_patch.patch

8

这种错误可能是由于LF与CRLF行尾不匹配引起的,例如当您查看补丁文件并确定它应该能够应用时,但实际上却无法应用。

为了测试这一点,如果您有一个只适用于单个文件的补丁,可以尝试在该文件上运行'unix2dos'或'dos2unix'(尝试两个,以确定哪个会导致文件更改;您可以在Windows和Unix上获取这些实用程序),然后将该更改提交为测试提交,然后再尝试应用补丁。如果这样做成功了,那就是问题所在。

NB git am默认将补丁应用为LF格式(即使补丁文件包含CRLF格式),因此如果您想将CRLF格式的补丁应用于CRLF格式的文件,您必须使用git am --keep-cr命令,如此答案所述。


5

补丁-p1的文档会非常有帮助。 - Scott Hutchinson

1

tl;dr 临时添加源仓库 (git remote add NAME ../some/path),获取对象 (git fetch NAME),然后使用 cherry-pick 提交。

应用补丁和进行三方合并是有区别的。如果使用补丁,则有一个要打补丁的文件和一个包含更改及其上下文的差异文件。如果上下文发生了变化,则 git (或者说,patch) 就无法再应用该补丁了,因为它会认为,“只要上下文匹配,那么这就是目标行。”(实际情况比这要复杂一些,但让我们保持简单。)

假设你想要打补丁(修订版本 A,或 HEAD):

aa
b
c
d
e

使用(简化补丁):

 a
 b
-c
+cc
 d
 e

这里的git不知道应用补丁之前文件的样子(创建补丁的基础文件)。我们把应用补丁之前的版本称为B,应用补丁之后的版本称为C。换句话说,git不知道文件的B版本长什么样。一方面,它可以假设在A版本之前你做了 a -> aa,并应用补丁。另一方面,B版本可能是这样的:
aa
b
c
d
e
a
b
c
d
e

这意味着在修订版本A之前,您删除了最后5行(a-e),补丁不应被应用。
这就是当你没有传递“-3”给“git am”时会发生的情况。当你传递“-3”时,它会尝试进行三方合并。如果它有足够的信息,它要么成功,要么留下上下文标记。那么,它需要什么?它需要知道文件在所有3个版本中的样子(文件的完整内容)。为此,“git format-patch”为每个文件提供2个哈希值:变更前的文件内容的哈希值(blob哈希值)和变更后的哈希值(参见索引行)。现在,如果“git am -3”能够通过哈希值在您的(目标)存储库中找到这些blobs,它就知道文件在所有3个版本中的样子,并可以进行三方合并。
例如,修订版本A(HEAD):
aa
b
c
d
e

修订版B(补丁之前):

a
b
c
d
e

修订版 C(打补丁后):

a
b
cc
d
e

了解这一点后,可以看到您更改了第一行,补丁更改了第三行,可以应用而不冲突。

现在,如何使修订版本B和C可用于git? 一种方法是暂时添加源存储库(远程),在其中创建了补丁(git remote add NAME ../some/path),然后执行git fetch NAME。 应用补丁后(git am -3 PATCH),如果不再需要,可以删除远程(git remote rm NAME)。 但是...您可以选择cherry-pick提交。

可以应用更改而无需添加远程,但这看起来并不实用。

这里是邮件列表上的回复,以及我的实验。以防万一。


0

使用补丁时,gitcherry-pick/rebase/merge冲突更挑剔。例如,给定以下文件:

line 1
line 2
line 3

并且以下更改(这不是差异,而是以类似差异的形式说明的更改):

 line 11
 line 2
-line 3
+line 33

使用 rebase/merge 不会产生冲突,但使用 am 会产生冲突。可以在 这里 找到重现步骤。

因此,一种选择是更改补丁直到它适用。


0

有几个模块抱怨补丁无法应用。我错过的一件事是分支已经变得陈旧。在使用git diff master BRANCH > file.patch生成补丁文件后,执行git merge master。转到原始分支后,可以使用git apply file.patch来应用补丁。


0

我遇到了同样的错误。在创建补丁时,我回滚了提交版本。因为之前的补丁是相反的方式。

[mrdubey@SNF]$ git log 65f1d63 commit 65f1d6396315853f2b7070e0e6d99b116ba2b018 Author: Dubey Mritunjaykumar

Date: Tue Jan 22 12:10:50 2019 +0530

commit e377ab50081e3a8515a75a3f757d7c5c98a975c6 Author: Dubey Mritunjaykumar Date: Mon Jan 21 23:05:48 2019 +0530

之前使用的命令:git diff new_commit_id..prev_commit_id > 1 diff

出现错误:patch failed: filename:40

可行的方法:git diff prev_commit_id..latest_commit_id > 1.diff


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