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

Posted

技术标签:

【中文标题】如何为特定提交生成 Git 补丁?【英文标题】:How can I generate a Git patch for a specific commit? 【发布时间】:2011-10-03 06:03:24 【问题描述】:

我需要编写一个脚本,为 SHA-1 提交号列表创建补丁。

我尝试使用git format-patch <the SHA1>,但自该 SHA-1 值以来,每次提交都会生成一个补丁。在生成了几百个补丁后,我不得不终止该进程。

有没有办法只为特定的 SHA-1 值生成补丁?

【问题讨论】:

【参考方案1】:

假设您在提交 1 之后有提交 id 2,您将能够运行:

git diff 2 1 > mypatch.diff

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

【讨论】:

感谢dookehster 的回复。这意味着我需要脚本来找到我感兴趣的提交之前的提交。我希望我可以避免这种情况。 @elle,不,你没有——git diff hash^ hash。 “hash^”给出了之前的提交。 (当然,manojlds 的回答更好) git show HEAD > mypatch.diff 在提交时也应该这样做。 @dookehester 是正确的还是相反的,git diff 1 2 这将无法在 diff 中包含任何二进制文件。【参考方案2】:

试试:

git @987654321@ -1 <sha>

git format-patch -1 HEAD

根据上面的文档链接,-1 标志告诉 Git 应该在补丁中包含多少次提交;

-

从最顶层的提交中准备补丁。


使用命令应用补丁:

git am < file.patch

【讨论】:

应用补丁:git apply --stat file.patch # show stats. git apply --check file.patch # 申请前检查错误。 git am &lt; file.patch # 最后应用补丁。 如果最后一次提交是来自另一个分支的合并,它似乎不起作用。 使用git am -3 &lt; file.patch 使用三向合并来申请,这将让您在之后使用git mergetool(或手动编辑)found here 解决冲突。 此命令也仅适用于提交中的特定文件:git format-patch -1 &lt;sha&gt; path/to/file.js 这将创建一个仅包含 file.js 差异的补丁 如果你能解释一下-1的目的会很有帮助【参考方案3】:

用于从特定 SHA-1 哈希的最顶部 提交生成补丁:

git format-patch -<n> <SHA-1>

单个补丁文件中 head 的最后 10 个补丁:

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

【讨论】:

能否请您提供第一个命令的示例 git format-patch -1 HEAD 将为最近的提交生成补丁 请原谅我问这个问题,所以当它是 -2 时,它会为最近的 2 次提交生成补丁,还有一件事要澄清的是命令 got format-patch -2 HEAD 与该行相同git format-patch HEAD~2【参考方案4】:

从特定提交(不是最后一次提交)生成补丁:

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

【讨论】:

【参考方案5】:

如果您想确保(单个提交)补丁将应用于特定提交之上,您可以使用新的 git 2.9(2016 年 6 月)选项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

参见Xiaolong Ye (``)commit bb52995、commit 3de6651、commit 3de6651、commit ded2c09(2016 年 4 月 26 日)。(由 Junio C Hamano -- gitster -- 合并到 commit 72ce3ff,2016 年 5 月 23 日)

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

维护人员或第三方测试人员可能想知道确切的基础树 该补丁系列适用于。教 git format-patch 一个 '--base' 选项 记录基本树信息并将其附加到第一个的末尾 消息(求职信或系列中的第一个补丁)。

基本树信息由“基本提交”组成,这是众所周知的 提交是项目历史中稳定部分的一部分 else 可以使用零个或多个“必备补丁”,它们是 尚未成为“基本提交”一部分的著名补丁 需要按拓扑顺序在“基本提交”之上应用 在应用补丁之前。

“基本提交”显示为“base-commit:”,后跟 40-hex 提交对象名称。 “必备补丁”显示为“prerequisite-patch-id:”,后跟 40 位十六进制“补丁 id”,可以通过“git patch-id --stable”命令传递补丁来获得。


Git 2.23(2019 年第三季度)将改进这一点,因为“format-patch”的“--base”选项以不稳定的方式计算了必备补丁的patch-ids,该方法已更新为以一种方式计算与“git patch-id --stable”兼容。

参见commit a8f6855、commit 6f93d26(2019 年 4 月 26 日)Stephen Boyd (akshayka)。(由 Junio C Hamano -- gitster -- 合并于 commit 8202d12,2019 年 6 月 13 日)

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

我们没有在每次处理一个大块时刷新上下文 patch-iddiff.c 中生成代码,但是当我们这样做的时候 使用“patch-id”工具生成“稳定”补丁 ID。

让我们将类似的逻辑从patch-id.c 移植到diff.c,这样当我们为“format-patch --base=”类型的命令调用生成补丁ID 时,我们可以获得相同的哈希值。


在 Git 2.24(2019 年第四季度)之前,“git format-patch -o &lt;outdir&gt;”相当于“mkdir &lt;outdir&gt;”而不是“mkdir -p &lt;outdir&gt;”,目前正在更正。

参见Bert Wesarg (bertwesarg) 的commit edefc31(2019 年 10 月 11 日)。(由 Junio C Hamano -- gitster -- 合并到 commit f1afbb0,2019 年 10 月 18 日)

format-patch: 创建输出目录的主要组件

签字人:Bert Wesarg

'git format-patch -o' 相当于 'mkdir &lt;outdir&gt;' 而不是 'mkdir -p &lt;outdir&gt;',正在纠正中。

避免在可能有安全隐患的前导目录中使用“adjust_shared_perm”。通过像 'git init' 一样暂时禁用 'config.sharedRepository' 来实现。


在 Git 2.25(2020 年第一季度)中,“git rebase”在设置 format.useAutoBase 配置变量时无法正常工作,已更正。

参见Denton Liu (Denton-L) 的commit cae0bc0、commit 945dc55、commit 700e006、commit a749d01、commit 0c47e06(2019 年 12 月 4 日)。(由Junio C Hamano -- gitster -- 合并到commit 71a7de7, 2019 年 12 月 16 日)

rebase:修复format.useAutoBase破损

报告人:Christian Biesinger署名人:Denton Liu

对于format.useAutoBase = true,运行 rebase 会导致错误:

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.

通过始终将 --no-base 从 rebase 传递给 format-patch 来解决此问题,从而取消 format.useAutoBase 的效果。


在 Git 2.29(2020 年第四季度)中,“git format-patch(man) 学会将“whenAble”作为format.useAutoBase 配置变量的可能值变为否-op 当自动计算的基数没有意义时。

参见Jacob Keller (jacob-keller) 的commit 7efba5f(2020 年 10 月 1 日)。(由 Junio C Hamano -- gitster -- 合并到 commit 5f8c70a,2020 年 10 月 5 日)

format-patch:教format.useAutoBasewhenAble”选项

签字人:Jacob Keller

format.useAutoBase 配置选项的存在允许用户默认为格式补丁启用“--base=auto”。

由于尝试格式化旧补丁时出现意外失败,这有时会导致工作流程不佳:

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

这可能非常令人困惑,因为用户请求--base 不一定立即显而易见(因为这是在配置中,而不是在命令行中)。

我们确实希望--base=auto 在无法提供合适的基础时失败,因为如果格式化的补丁在请求时不包含基础信息,这同样会令人困惑。

format.useAutoBase 一种新模式“whenAble”。

此模式将导致 format-patch 在可能的情况下尝试包含基本提交。但是,如果找不到有效的基本提交,则 format-patch 将继续格式化补丁而无需基本提交。

为了避免使另一个分支名称无法与 --base 一起使用,请不要教 --base=whenAble--base=whenable

相反,重构 base_commit 选项以使用回调,并依赖全局配置变量 auto_base

这确实意味着用户无法从命令行请求此可选的基本提交生成。然而,这可能不是太有价值。如果用户手动请求基本信息,他们将立即被告知未能获取合适的基本提交。这使用户可以就是否继续该格式做出明智的选择。

添加测试以涵盖--base 的新操作模式。

git config 现在包含在其man page 中:

format-patch 默认情况下。 也可以设置为“whenAble”允许 如果有合适的基地可用,则启用--base=auto,但跳过 添加基本​​信息,否则格式不会消失。


在 Git 2.30(2021 年第一季度)中,“git format-patch --output=there(man) 没有按预期工作,而是崩溃了。

现在支持该选项。

参见Jeff King (peff) 的commit dc1672d、commit 1e1693b、commit 4c6f781(2020 年 11 月 4 日)。(由 Junio C Hamano -- gitster -- 合并于 commit 5edc8bd,2020 年 11 月 18 日)

format-patch: 支持 --output 选项

报告人:Johannes Postler签字人:Jeff King

我们从未打算在格式补丁中支持 diff 的 --output 选项。 直到baa4adc66a (parse-options: disable option abbreviation with PARSE_OPT_KEEP_UNKNOWN, 2019-01-27, Git v2.22.0-rc0),它是不可能触发的。我们首先解析格式补丁选项,然后将剩余部分交给setup_revisions()。 在提交之前,我们接受“--output=foo”作为“--output-directory=foo”的缩写。但之后,我们不检查缩写,并且 --output 被传递给 diff 代码。

这会导致无意义的行为和错误。 diff 代码将在 rev.diffopt.file 中打开一个文件句柄,但我们将用我们为每个单独的补丁文件打开的句柄覆盖它。所以 --output 文件总是空的。 但更糟糕的是,差异代码还设置了 rev.diffopt.close_file,因此log_tree_commit() 将自行关闭文件句柄。然后cmd_format_patch() 中的主循环会尝试再次关闭它,从而导致双重释放。

最简单的解决方案是使用 format-patch 禁止 --output,因为没有人打算让它工作。但是,我们不小心记录了它(因为 format-patch 包含 diff-options)。它确实适用于 "git log"(man) ,它将整个输出写入指定的文件。也很容易让 format-patch 工作:它实际上与 --stdout 相同,但指向特定文件。

我们可以通过"close_file" 标志检测到 --output 选项的使用(请注意,我们不能使用 rev.diffopt.file,因为 diff 设置会将其设置为 stdout)。所以我们只需要取消设置那个标志,但不需要做任何其他事情。我们的情况与 --stdout 完全一样(请注意,我们不会 fclose() 文件,但 stdout 情况也不会;退出程序会为我们解决这个问题)。

【讨论】:

【参考方案6】:

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

git format-patch -1 HEAD

HEAD 替换为特定的哈希或范围。

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

-&lt;n&gt; - 从最上面的 个提交中准备补丁。

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

git am -3k 001*.patch

请参阅:man git-format-patch

【讨论】:

谢谢!我认为值得注意的是,应用补丁将创建一个带有以 [PATCH] 为前缀的提交消息的提交。不过这很容易解决 现象级的。 OP,你还没有接受这个,因为......? @MikeS 不,与其他任何git-formatted 补丁相比,它不会,至少如果用户以正确的方式应用它。 @MikeS 我并没有真正调查原因,但是省略了-k 标志(git am -3)修复了这个表单(没有PATCH[0/10] 提交消息)。 Git 版本 2.20.1.windows.1【参考方案7】:

仅针对特定 SHA-1 值生成补丁的方法是什么?

很简单:

选项 1。git show commitID &gt; myFile.patch

选项 2。git commitID~1..commitID &gt; myFile.patch

注意:将 commitID 替换为实际的提交 ID(SHA-1 提交代码)。

【讨论】:

选项 1 完全错误,与问题无关。 选项 2 也是无效命令。你会得到这样的错误:混帐a5f4bcaeb7fa7de27ae79d9522332e872889bbf0〜1..a5f4bcaeb7fa7de27ae79d9522332e872889bbf0混帐: 'a5f4bcaeb7fa7de27ae79d9522332e872889bbf0〜1..a5f4bcaeb7fa7de27ae79d9522332e872889bbf0' 不是一个git命令。请参阅“git --help”。请在发布答案前检查 选项 1 实际上是我在搜索如何执行此操作时所寻找的。来自我的 +1! @AnshumanManral 我不太明白为什么git show 是错误的且不相关;除了声称之外,也许可以解释一下。【参考方案8】:
git format-patch commit_Id~1..commit_Id  
git apply patch-file-name

快速简单的解决方案。

【讨论】:

另外,在应用补丁之前不要忘记致电git apply --check patch-file-name。这将有助于避免问题。【参考方案9】:

如果你只想区分指定的文件,你可以使用:

git diff master 766eceb --connections/ > 000-mysql-connector.patch

【讨论】:

【参考方案10】:

以我的Mercurial 背景,我打算使用:

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

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

【讨论】:

【参考方案11】:

使用 commit-id 创建补丁

$ git format-patch -1 commit-id

此命令使用以下文件名创建补丁

0001-commit-message.patch

应用补丁。

$ git am 0001-commit-message.patch

【讨论】:

您能澄清一下-1 的说法吗?我无法在文档或在线找到对它的引用。

以上是关于如何为特定提交生成 Git 补丁?的主要内容,如果未能解决你的问题,请参考以下文章

如何为子模块更新编写 git 提交消息?

Git 补丁操作

如何为特定用户实施旧密码和新密码?

git如何打补丁?

Git 打补丁----基于源码改动生成 patch 包的方法

如何为 GitHub 项目生成统计信息?