为什么 "git format-patch" 在两个提交之间的差异与 "git diff" 不同?

3
假设有两个基于主分支的分支x1和x2。 git diff x1..x2 显示了x1和x2之间的差异,但是git format-patch x1..x2 -1 --stdout显示的是主分支和x2之间的差异。是否有方法让 format-patch 显示x1和x2之间的差异?
例如:
git branch x1 master
git branch x2 master
git switch x1
echo a >> README
git commit -a -m x1 # one extra line at x1
git switch x2
echo a >> README
echo a >> README
git commit -a -m x2 # 2 extra lines at x2

git diff x1..x2 # show one line betwen x1 and x2
git format-patch -1 --stdout x1..x2 # show 2 lines between master and x2

如果我理解正确,似乎 git format-patch 只会发出实际提交(可能已被存储),但它不会尝试计算 interdiff。

3个回答

2
为什么“git format-patch”在两个提交之间的差异与“git diff”不同?
这两个命令对“..”的解释不同。git diff正在寻找两个提交,而git format-patch正在寻找一个或多个补丁系列。
它们都试图提供帮助,将您告诉它们的内容转换为它们可以处理的内容。
git diff实际上忽略了这两个点。它正在寻找两个提交,您给了它两个提交,这就是两个提交。当您考虑到它还有自己(非常有用和合理的)三个点语法的解释时,这可能会让人感到困惑较少。
git format-patch正在寻找补丁系列,因此它使用所有git rev-list驱动的命令共享的更常见的点解释。

1

TL;DR: 如果你正在寻找像interdiff这样的东西,请考虑使用git range-diff

详细

如果我理解正确,似乎git format-patch只会发出真正的提交(可能被隐藏),但不会尝试计算interdiff。

这是正确的,但可能是基于一些错误的假设。

特别地,git format-patch是将一个提交或一整个提交链转换为可以通过电子邮件发送的格式的方法。接收到补丁的人可以使用git am创建等效的提交,保留大部分元数据和git patch-id1

由于它旨在保留一系列提交,因此format-patch专门查看单个提交链。您使用的x1..x2语法是允许的,但是当将其提供给单个修订版本时,它被视为一个“since”限定符,也就是说,git format-patch X意味着git format-patch X..HEAD

请注意,在这种修订范围内,即任何有效的指示符A和B的A..B - 有关更多详细信息,请参见 gitrevisions文档 - 提交B充当正引用并通常包含在所选提交中,但A则是负引用,例如^A,并始终被排除在外。 因此,如果AB命名相同的提交,则B也将被排除在外。但是,您可以排除通常不会出现的提交。 特别是,您已经创造了这种情况:

       I   <-- x1
      /
...--H   <-- master
      \
       J   <-- x2

该命令选择由x1..x2所选取的提交始终为提交J,而由x2..x1所选取的提交始终为提交I——与使用master..x2master..x1时获得的相同集合。

(注意:git format-patch-number选项会在给定单个提交说明符时强制更改行为,而不是一个范围。在使用范围说明符时,它似乎没有任何影响。)

然而,git diff命令并不遵守这些规则。通常情况下,范围A..B表示B可到达的所有提交,但不包括从A可到达的所有提交,这就是我们在master/x1/x2分支中看到的情况。但是git diff A..B只意味着git diff A B。Git会提取提交A作为左侧,并提取提交B作为右侧,然后进行比较。这里也没有任何交互差异,也没有尝试返回到master上的公共提交。 git diff命令对于三个点范围语法也有特殊解释:git diff A...B表示查找AB之间的合并基础,然后将其与提交B进行比较。这也不是任何形式的交互差异,git diff根本不这样做。

1默认情况下,使用git am时,提交者和提交日期是新的。但是,您也可以添加--committer-date-is-author-date选项。如果这样做,并且您设置了提交者名称与原始提交者名称匹配,并将其应用于正确的起始提交,则即使原始提交具有相同的作者和提交者日期,您也将获得具有相同哈希 ID 的提交。也就是说,通过一些努力,有时候通过电子邮件发送的补丁可以产生逐位相同的提交,就像您使用git bundle或类似工具一样。


感谢您提供的信息性解释。事实上,我一直在尝试找到一个可以直接使用git am而不需要调用git commit的差异输出。现在我想我可以简单地将git format-patch的头部和git diff的内容合并。 - speedogoo

0

还有一种情况,git format-patch 实际上使用了 git diff 而不是仅限于两个提交,如 this 2018 thread 所解释:

当重新提交补丁系列时,通常将其与之前的版本包括一个 interdiffrange-diff 对于审核者来说非常有帮助。

这样做需要手动运行 git-diffgit-range-diff 并将结果复制/粘贴到新版本的封面信中。

该系列通过引入 git-format-patch 选项 --interdiff--range-diff 来自动化该过程,从而将此类差异插入到封面或单个补丁的评论部分中(对于只有一个补丁的系列来说)。
在后一种情况下,interdiff 或 range-diff 将进行缩进以避免混淆 git-am 和人类读者。

而在另一个2018年的讨论串中:

When submitting a revised a patch series, the --range-diff option embeds a range-diff in the cover letter showing changes since the previous version of the patch series.
The argument to --range-diff is a simple revision naming the tip of the previous series, which works fine if the previous and current versions of the patch series share a common base.

However, it fails if the revision ranges of the old and new versions of the series are disjoint.
To address this shortcoming, extend --range-diff to also accept an explicit revision range for the previous series.
For example:

git format-patch --cover-letter --range-diff=v1~3..v1 -3 v2
这个问题在 Git 2.29(2020 年第四季度)中得到了解决,“format-patch --range-diff=<prev> <origin>..HEAD”不再忽略<origin>,当<prev>是单个版本时。

请查看 提交记录 07a7f8d, 提交记录 72a7239, 提交记录 cdffbdc (2020年9月8日) ,由Eric Sunshine (sunshineco提交。
(由Junio C Hamano -- gitster --提交记录 634e008中合并,于2020年9月22日)

format-patch:当已知时,使用“origin”作为当前系列范围的起点

签名作者:Eric Sunshine

在格式化一个覆盖 origin..HEAD 的补丁系列时,人们会期望该范围被用作计算前一版本和当前版本之间的补丁系列的范围差异时的当前系列范围。

然而,当 --range-diff=<prev> 指定单个修订版本而不是范围时,infer_range_diff_ranges() 忽略了 origin..HEAD,并意外地基于 <prev> 计算当前系列范围。
通过无条件地使用 origin..HEAD 作为当前系列范围的起点来解决这种异常情况,而不管 <prev> 如何,只要已知 origin,并且仅在不知道 origin 时才回退到基于 <prev> 的当前系列范围。


请注意,从 Git 2.41(2023 年第二季度)开始,已经澄清了默认选项。

详见 commit f024913(由 Alex Henrie (alexhenrie) 于 2023 年 4 月 2 日提交)。
(由 Junio C Hamano -- gitster --commit 9e0d1aa 中合并,日期为 2023 年 4 月 21 日)

format-patch:修正没有参数的 --thread 文档

签名-by: Alex Henrie

在 Git 中,几乎所有命令行标志都无条件地覆盖相应的配置选项(请参见this thread)。

添加一个测试以确认 git format-patch --threadman也是如此。

git format-patch现在在其手册页面中包含以下内容:

--thread没有参数时等同于--thread=shallow


在Git 2.41(2023年第二季度)中,当 "git send-email"(man) 使用验证钩子接收一条没有消息ID的邮件和一条带有消息ID的邮件时,它未能为前者自动分配唯一的消息ID,而是重用了后者的消息ID,这已得到纠正。
这是一个最近修复的回归问题,因此无需在发布说明中提及。

请查看提交 20bd08a提交 3ece9bf(2023年5月17日),由Junio C Hamano (gitster)提交。
(由Junio C Hamano -- gitster --合并于提交 b04671b,2023年5月19日)

send-email:验证后清除$message_id

测试者:Douglas Anderson

最近git-send-email(man)开始两次解析相同的消息,一次是在发送第一条消息之前验证所有消息,然后在验证钩子满意并且每个消息都被发送后,再次读取内容以找出要发送到哪里等信息。
不幸的是,即使在验证完成后,读取消息的效果仍然存在。换句话说,如果输入文件中存在,则会分配$message_id变量,但该变量是全局的,并且在运行pre_process_file之前不会清除。
这会导致读取没有message-id的消息,然后读取带有message-id t的消息时出现问题---子报告似乎与先前编写的消息具有相同的ID。
在开始读取pre_process_file中的标题之前,请清除变量。

你可以在提取补丁测试中看到:

threaded_patches=$(git format-patch -o threaded --thread=shallow -s --in-reply-to="format" HEAD^1)

另请参阅Git 2.42(2023年第三季度)和修复泄漏问题:commit c6d26a9commit cfa1209(2023年5月18日),由Jeff King (peff)提交。 <sup>(由Junio C Hamano -- gitster --commit e490bea中合并,2023年6月13日)</sup> - VonC

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