如何获取这两个git提交之间的差异(interdiff)?

12

我想查看 Git 中同一次提交的两个版本之间的差异。基本上是差异的 diff。根据我目前所了解的,这被称为“interdiff”。我已经阅读了几篇有关如何创建 git 补丁的 interdiff 的教程,但我无法在我的特定情况下使这些方法起作用。

所以这就是设置过程。我有两个分支分叉,每个都有略微不同的提交:

* 29e734f - (origin/feature_branch, new_commits) New commit 3 (69 minutes ago) <Ajedi32>
* b22ebea - New commit 2 (89 minutes ago) <Ajedi32>
* 09d42c2 - New commit 1 (2 hours ago) <Ajedi32>
| * 467e08f - (old_commits) Old commit 3 (4 weeks ago) <Ajedi32>
| * f2bf1cb - Old commit 2 (4 weeks ago) <Ajedi32>
| * 34a2187 - Old commit 1 (4 weeks ago) <Ajedi32>
|/  
*   1b05a4a - (origin/base, base) Base commit (5 weeks ago) <Ajedi32>
在这个例子中,我想要找到“旧提交3”和“新提交3”之间的差异。我尝试从这两个提交创建一个补丁文件,并通过interdiff实用程序运行它们,但是我得到的只有这个:
1 out of 2 hunks FAILED -- saving rejects to file /tmp/interdiff-1.Kgwx8u.rej
interdiff: Error applying patch1 to reconstructed file

我不太确定那是什么意思,所以现在有点卡住了。我接下来该怎么办?

注:我不希望在这里使用git diff old_commits new_commits。我不希望输出包括提交1和2的修订。


哦,根据提问者的问题,这可能与此问题有关。 - Ajedi32
出于好奇,diff的差异会对您有什么帮助? - Shahbaz
1
@Shahbaz 你的意思是它有什么用处?我使用它使得代码审查人员更容易看到我在响应先前审查时所做的更改。这个页面是我遵循的教程之一。它在页面顶部附近有一个相当好的解释,说明了interdiffs的有用性。 - Ajedi32
@Shahbaz 或者你是在问如果我通过“diff”命令运行差异内容会发生什么?在那种情况下,我不确定但我怀疑结果可能不太好看。 - Ajedi32
这与 diff "old_commit_3".."new_commit_3" 有何不同? - mnagel
1
从原始问题中:@mnagel:“注意:我这里不是在寻找git diff old_commits new_commits。我不希望输出包括提交1和2的修订。”因此,如果“旧提交1”和“新提交1”之间存在一些小差异,我不希望包括这些更改。我正在比较补丁,而不是存储库的状态。 - Ajedi32
5个回答

12

Git 2.19引入了一个新命令,git range-diff,它可以进行以下操作:

git-range-diff - 比较两个提交范围(例如分支的两个版本)

git range-diff [--color=[<when>]] [--no-color] [<diff-options>] [--no-dual-color]
               [--creation-factor=<factor>]\
               ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )

描述

该命令显示两个补丁系列版本之间的差异,或更一般地说,两个提交范围(忽略合并提交)之间的差异。

为此,它首先找到来自两个提交范围的相互对应的提交对。当两个提交之间的差异(即作者信息、提交消息和提交差异)与修补程序大小相比较小时,就说这两个提交是相互对应的。有关详细信息,请参见下面的 算法

最后,匹配提交的列表按第二个提交范围的顺序显示,未匹配的提交会在显示完它们所有祖先之后被插入。

git range-diff base old_commits new_commits

会自动将在old_commits分支中进行的提交与new_commits分支中的提交进行匹配,并显示每个提交之间的差异摘要。

或者,如果您只想获取这些分支中最后一次提交的更改,则可以运行:

git range-diff old_commits~..old_commits new_commits~..new_commits

自 Git 2.31 起,你可以使用更简洁的方式提交单个提交:

git range-diff old_commits^! new_commits^!

了解更多range-diff相关内容,请参阅官方文档


5
也许是这样的:

也许是这样的:

git log -p -1 new_commits > patch.new
git log -p -1 old_commits > patch.old
diff patch.old patch.new

或者用一个简洁的一行命令(在中):
diff <(git log -p -1 old_commits) <(git log -p -1 new_commits)

用于这个一行命令的Bash语法是进程替换 - Fabien
有没有使用git log而不是git show的理由,例如diff <(git show old_commits) <(git show new_commits)这样的东西? - doak
@doak 很好的观点 - 看起来输出是相同的,至少在我目前使用的 git 版本上是这样... 不过这可能并不总是如此... - twalberg
@talberg,我非常确定这是真的,至少过去10年是这样,未来20年也是如此 ;)从man git show: "对于提交,它显示日志消息和文本差异。" - doak
那个一行代码似乎符合 POSIX 标准。 - xeruf

1
如果两个提交之间的差异很小,比较被删除和添加的行(即仅以+或-开头的行)是有效的。这将消除由@@开始的块边界或不重要的提交消息正文引入的噪音。
diff -u --ignore-matching-lines '^[^+-]' \
    <(git show 1d9e4ac) <(git show 7b8e5c9)

一个示例输出为:

--- /dev/fd/63  2015-02-13 13:27:08.612683558 +0100
+++ /dev/fd/62  2015-02-13 13:27:08.616683527 +0100
@@ -62,13 +57,24 @@
  }

 diff --git a/src/crush/CrushWrapper.h b/src/crush/CrushWrapper.h
-index 0113662..282cbeb 100644
+index 3b2e6e6..0a633a5 100644
 --- a/src/crush/CrushWrapper.h
 +++ b/src/crush/CrushWrapper.h
-@@ -874,6 +874,25 @@ public:
-     return false;
+@@ -863,6 +863,36 @@ public:
+     if (!crush) return -1;
+     return crush_find_rule(crush, ruleset, type, size);
    }
- 
++
++  bool ruleset_exists(int const ruleset) const {
++    for (size_t i = 0; i < crush->max_rules; ++i) {
++     if (crush->rules[i]->mask.ruleset == ruleset) {
++       return true;
++     }
++    }
++
++    return false;
++  }
++
 +  /**
 +   * Return the lowest numbered ruleset of type `type`
 +   *

1
您的两个补丁在GNU interdiff上失败,因为它们没有直接的共同基础/父级。
虽然git range-diff生成了一组差异的外部差异(通常很难阅读;不知道/不一致关于以前提交中已经消耗/合并的更改;不是一个适用的补丁格式),但通过将一个提交的副本合并到另一个(最好是较新的)提交的父级上进行合并重新基础,您可以获得有效且易于阅读的interdiff。有几种方法。
在您的示例中,将<New commit 1><Old commit 1>进行比较是微不足道的,因为它们已经有一个立即的共同父级
diff NC1 OC1

根据您实际的问题,我们可以举例如下来,离常见基础更远:

# move to the parent of NC3 - entering detached HEAD state
git checkout NC3~1
# merge-rebase the single patch <Old-commit-3> onto here
git cherry-pick OC3
# show the diff or reverse diff (or diff in gitk GUI)
git diff -R NC3
# back to feature_branch
git checkout feature_branch

当然,在这里可能会出现合并冲突,这些冲突(必然)会暴露出不兼容的更改,无法通过“虚拟公共基础”的交叉差异进行处理,因为缺少真正的直接公共父项。
可以创建(临时)标签或分支来保存这个重新基于的比较提交(或一系列提交),例如用于与其他用户讨论和工作。否则,临时未引用的悬挂提交将很快被自动回收。

0
也许这会给你想要的东西,但如果提交 1-2-3 之间有很强的依赖关系,它将失败。
$ git checkout 'old commit 2'
$ git cherry-pick -n 'new commit 3'
$ git diff 'old commit 3'

$ git checkout 'new commit 2'
$ git cherry-pick -n 'old commit 3'
$ git diff 'new commit 3'

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