如何为特定的提交生成Git补丁?

1630
我想为特定的提交哈希值创建一个补丁。
我尝试使用git format-patch ,但这会为自提交以来的每个提交生成一个补丁。有没有办法只为生成一个补丁?
11个回答

2550

尝试:

git <a rel="noreferrer" href="https://git-scm.com/docs/git-format-patch">format-patch</a> <a rel="noreferrer" href="https://git-scm.com/docs/git-format-patch#Documentation/git-format-patch.txt--ltngt">-<n></a> <a rel="noreferrer" href="https://git-scm.com/docs/gitrevisions#_specifying_revisions"><rev></a>

例如:

git format-patch -1 HEAD
-1标志表示补丁中应包含多少个提交:

-<n>

     从最新的<n>个提交准备补丁。


使用以下命令应用补丁:
git am < file.patch

另外,你也可以使用以下方法申请(适用于所有操作系统,包括Windows):
git apply --verbose file.patch

使用-v--verbose选项将显示失败的内容(如果有的话),为您提供修复的线索。

292
应用补丁:git apply --stat file.patch # 显示统计信息。git apply --check file.patch # 在应用之前检查错误。git am < file.patch # 最终应用补丁。 - Adrian
7
如果最后一次提交是来自另一个分支的合并,则似乎无法正常工作。 - Lex Li
60
使用命令 git am -3 < file.patch 进行三方合并,此后可以使用 git mergetool 解决冲突(或手动编辑)参考链接 - Matt
9
该命令也适用于从提交中选择特定的文件:git format-patch -1 <sha> path/to/file.js。这将创建一个仅包含文件.js差异的补丁文件。 - Kristóf Dombi
4
请问需要翻译的原文是:“It would be very helpful if you explained the purpose of the `-1”吗?如果是,我的翻译是:“如果您解释一下“-1”的用途,那将非常有帮助。” - Peter Chaula
显示剩余8条评论

357

生成指定 SHA-1 哈希值最新的前 <n> 个提交所对应的补丁:

git format-patch -<n> <SHA-1> --stdout > <name_of_patch_file>.patch

将最近10个补丁合并为单个补丁文件:

git format-patch -10 HEAD --stdout > 0001-last-10-commits.patch

1
git format-patch -1 HEAD 会为最近的提交生成补丁。 - Sriram Murali
2
抱歉打扰,请问当参数为-2时,它会生成最近两个提交的补丁,是这样吗?还有一个需要澄清的问题是,命令git format-patch -2 HEADgit format-patch HEAD~2这行命令是一样的吗? - Kasun Siyambalapitiya

122

假设你有提交ID 2,紧接在提交1之后,你可以运行以下命令:

git diff 2 1 > mypatch.diff

其中2和1是SHA-1哈希值。


1
感谢dookehster的回复。这意味着我需要脚本查找我感兴趣的提交之前的提交。我希望我能避免这样做。 - elle
13
@elle,不是的,你不需要使用 git diff hash^ hash。 "hash^" 是指前一个提交记录。(但是,当然,manojlds的答案更好)。 - J-16 SDiZ
4
当你在该提交下时,git show HEAD > mypatch.diff 命令将达到相同的效果。 - andho
2
@dookehester 这是正确的还是应该反过来,git diff 1 2 - Kasun Siyambalapitiya
我倾向于使用--no-prefix,并使用'patch -p0 < patch file'应用...以这种方式制作的补丁文件也可以与Subversion的diff输出互换使用。 - Rondo
2
这将无法在差异中包含任何二进制文件。 - stuckj

72

这个命令(正如@Naftuli Tzvi Kay已经建议的那样),

git format-patch -1 HEAD

使用特定的哈希值或范围替换 HEAD

将生成最新提交格式化后类似于Unix邮箱格式的补丁文件。

-<n> - 从最新的 <n> 次提交中准备补丁。

然后,可以通过以下方式以邮箱格式重新应用补丁文件:

git am -3k 001*.patch

参见:man git-format-patch


谢谢!我认为值得注意的是,应用补丁将创建一个提交,其提交消息以[PATCH]为前缀。不过这很容易解决。 - Mike S
2
非常棒。楼主,你还没有接受这个,为什么?@MikeS 不,它不会,就像任何其他适当应用的 git 格式补丁一样。 - underscore_d
2
@MikeS 我没有深入研究为什么,但是省略 -k 标志 (git am -3) 对我来说解决了这个问题(没有 PATCH[0/10] 提交消息)。Git 版本为 2.20.1.windows.1。 - jannis
@jannis 请查看 man git-amman git-mailbox 以获取解释,这是 -k 选项的预期行为。 - éclairevoyant

41
git format-patch commit_Id~1..commit_Id  
git apply patch-file-name

快速简单的解决方案。


9
在应用补丁之前,请不要忘记调用git apply --check patch-file-name。这将有助于避免问题。 - iamantony
2
请注意,git apply 命令会丢失所有提交信息,您需要手动提交。git am 命令在幕后调用 git apply 命令,但会保留提交信息,这意味着它会自动进行提交。请参阅 https://dev59.com/hGct5IYBdhLWcg3wPbEm#12240235。 - éclairevoyant

23
如果您想确保(单个提交)补丁被应用在特定的提交之上,您可以使用新的git 2.9选项git format-patch --base
git format-patch --base=COMMIT_VALUE~ -M -C COMMIT_VALUE~..COMMIT_VALUE

# or
git format-patch --base=auto -M -C COMMIT_VALUE~..COMMIT_VALUE

# or
git config format.useAutoBase true
git format-patch -M -C COMMIT_VALUE~..COMMIT_VALUE

请查看提交bb52995, 提交3de6651, 提交fa2ab86, 提交ded2c09 (2016年4月26日) 由Xiaolong Ye (``)提交。
(在2016年5月23日由Junio C Hamano -- gitster --合并到提交72ce3ff)

format-patch: 添加'--base'选项以记录基础树信息

维护者或第三方测试人员可能想要知道补丁系列所应用的确切基础树。教授git format-patch一个'--base'选项以记录基础树信息,并将其附加在第一条消息(封面信或系列中的第一条补丁)的末尾。
基础树信息包括“基础提交”,它是项目历史的稳定部分,其他人都在此基础上工作,以及零个或多个“先决条件补丁”,它们是正在飞行中的众所周知的补丁,尚未成为“基础提交”的一部分,需要按照拓扑顺序在“基础提交”的顶部应用这些补丁之前。
“基础提交”显示为“base-commit:”后跟40位十六进制的提交对象名称。
“先决条件补丁”显示为“prerequisite-patch-id:”后跟40位十六进制的“补丁ID”,可以通过将补丁传递给“git patch-id --stable”命令来获取。
Git 2.23 (Q3 2019)将进行改进,因为“format-patch”命令的“--base”选项以不稳定的方式计算前提补丁的补丁ID,现已更新为与“git patch-id --stable”兼容的计算方式。请注意保留HTML标签。

查看提交 a8f6855, 提交 6f93d26 (2019年4月26日) 由 Stephen Boyd (akshayka) 进行。
(由 Junio C Hamano -- gitster -- 合并于 提交 8202d12, 2019年6月13日)

format-patch: 使 --base patch-id 输出稳定

我们在diff.c中处理每个块时没有清除上下文,但是我们在使用'patch-id'工具生成“稳定”的补丁ID时确实这样做了。让我们将类似的逻辑从patch-id.c移植到diff.c中,这样当我们为'format-patch --base='类型的命令调用生成补丁ID时,我们可以获得相同的哈希值。

在Git 2.24之前(2019年第四季度),"git format-patch -o <outdir>"相当于"mkdir <outdir>"而不是"mkdir -p <outdir>",现已得到改正。

请参见提交 edefc31(由Bert Wesarg(bertwesarg于2019年10月11日提交)。 (由Junio C Hamano -- gitster --合并于提交 f1afbb0,2019年10月18日)

format-patch: 创建输出目录的前导组件

Signed-off-by: Bert Wesarg

'git format-patch -o ' 执行的相当于 'mkdir <outdir>' 而不是 'mkdir -p <outdir>',正在进行更正。
避免在可能存在安全隐患的前导目录上使用 'adjust_shared_perm'。通过暂时禁用 'config.sharedRepository'(与 'git init' 相同)来实现。
在Git 2.25(2020年第一季度)中,当设置了format.useAutoBase配置变量时,"git rebase"无法正常工作,现已进行了修正。

请参阅提交 cae0bc0, 提交 945dc55, 提交 700e006, 提交 a749d01, 提交 0c47e06 (2019年12月4日) 由Denton Liu (Denton-L)提交。
(由Junio C Hamano -- gitster --提交 71a7de7中合并,2019年12月16日)

rebase: fix format.useAutoBase breakage

Reported-by: Christian Biesinger
Signed-off-by: Denton Liu

With format.useAutoBase = true, running rebase resulted in an error:

fatal: failed to get upstream, if you want to record base commit automatically,
please use git branch --set-upstream-to to track a remote branch.
Or you could specify base commit by --base=<base-commit-id> manually
error:
git encountered an error while preparing the patches to replay
these revisions:

ede2467cdedc63784887b587a61c36b7850ebfac..d8f581194799ae29bf5fa72a98cbae98a1198b12

As a result, git cannot rebase them.

Fix this by always passing --no-base to format-patch from rebase so that the effect of format.useAutoBase is negated.


在 Git 2.29 (2020年第四季度) 中, "git format-patch"(man) 学会了将 "whenAble" 作为 format.useAutoBase 配置变量的可能值,当自动计算的基础不合理时,它将成为无操作。

请见提交 7efba5f(2020年10月1日),由Jacob Keller (jacob-keller)完成。
(由Junio C Hamano -- gitster --提交 5f8c70a中合并,2020年10月5日)

format-patch: 教授 format.useAutoBase 的 "whenAble" 选项

署名:Jacob Keller

The format.useAutoBase configuration option exists to allow users to enable '--base=auto' for format-patch by default.

This can sometimes lead to poor workflow, due to unexpected failures when attempting to format an ancient patch:

$ git format-patch -1 <an old commit>
fatal: base commit shouldn't be in revision list  

This can be very confusing, as it is not necessarily immediately obvious that the user requested a --base (since this was in the configuration, not on the command line).

We do want --base=auto to fail when it cannot provide a suitable base, as it would be equally confusing if a formatted patch did not include the base information when it was requested.

Teach format.useAutoBase a new mode, "whenAble".

This mode will cause format-patch to attempt to include a base commit when it can. However, if no valid base commit can be found, then format-patch will continue formatting the patch without a base commit.

In order to avoid making yet another branch name unusable with --base, do not teach --base=whenAble or --base=whenable.

Instead, refactor the base_commit option to use a callback, and rely on the global configuration variable auto_base.

This does mean that a user cannot request this optional base commit generation from the command line. However, this is likely not too valuable. If the user requests base information manually, they will be immediately informed of the failure to acquire a suitable base commit. This allows the user to make an informed choice about whether to continue the format.

Add tests to cover the new mode of operation for --base.

git config现在包含在其手册页面中:

format-patch默认情况下为“always”。
也可以设置为“whenAble”,以允许启用--base=auto,如果有合适的基础版本可用,则跳过添加基础信息,否则格式不受影响。


使用 Git 2.30 (2021年第一季度), "git format-patch --output=there"(man) 的预期功能无法正常工作,会导致崩溃。

现在支持该选项。

查看 提交 dc1672d, 提交 1e1693b, 提交 4c6f781 (2020年11月4日) ,作者为Jeff King (peff)
(由Junio C Hamano -- gitster --提交 5edc8bd中合并,日期为2020年11月18日)

format-patch:支持 --output 选项

报告人:Johannes Postler
签名者:Jeff King

我们从未打算在 format-patch 中支持 diff 的 --output 选项。并且直到 baa4adc66a(parse-options:使用 PARSE_OPT_KEEP_UNKNOWN 禁用选项缩写,2019-01-27,Git v2.22.0-rc0),它都是不可能被触发的。我们首先解析 format-patch 选项,然后再将其余部分交给 setup_revisions()。
在那个提交之前,我们会接受 "--output=foo" 作为 "--output-directory=foo" 的缩写。但之后,我们不会检查缩写,并且 --output 会传递给 diff 代码。
这导致荒谬的行为和错误。diff 代码将在 rev.diffopt.file 中打开文件句柄,但我们将用自己为每个单独的补丁文件打开的句柄覆盖它。因此,--output 文件始终为空。但更糟糕的是,diff 代码还设置了 rev.diffopt.close_file,因此 log_tree_commit() 将关闭文件句柄本身。然后 cmd_format_patch() 中的主循环将尝试再次关闭它,导致双重释放。
最简单的解决方案是禁止 format-patch 使用 --output,因为没有人打算让它工作。然而,我们意外地记录了它(因为 format-patch 包括 diff 选项)。而且它确实可以与 "git log"(man) 一起使用,后者将整个输出写入指定的文件。这很容易让 format-patch 也能够工作:它实际上与 --stdout 相同,但指向特定的文件。
我们可以通过 "close_file" 标志检测使用 --output 选项(请注意,我们不能使用 rev.diffopt.file,因为 diff 设置将其设置为 stdout)。所以我们只需要取消设置该标志,但不必做任何其他操作。我们的情况与 --stdout 完全相同(请注意,我们不会 fclose() 文件,但 stdout 情况也是如此;退出程序会为我们处理)。

22

使用提交ID创建git补丁

$ git format-patch -1 commit-id

这个命令会创建一个名为以下文件名的补丁

0001-commit-message.patch

应用补丁的方法:

$ git am 0001-commit-message.patch

3
你能解释一下 -1 这个参数吗?我在文档和网上都找不到相关的参考资料。 - brainbag
1
@brainbag 我在man手册中找到了它:-<n> 从最顶部的<n>个提交准备补丁 - Cezary Drożak
如果您将-1更改为任何>1的数字,您仍将获得每个提交的补丁。这句话怎么回答问题呢? - Farid

12

从特定的提交(而不是最后一次提交)生成补丁的方法如下:

git format-patch -M -C COMMIT_VALUE~1..COMMIT_VALUE

6

基于我的Mercurial背景,我会使用:

git log --patch -1 $ID > $file

但是我现在考虑使用git format-patch -1 $ID


5
如果您只想比较指定的文件,可以使用以下命令:
``` git diff master 766eceb -- connections/ > 000-mysql-connector.patch ```
其中 `connections/` 是要比较的文件路径。

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