git diff 在文件相同时报告差异

git diff reporting differences when files are the same

我正在尝试将一些更改从一个 b运行ch 手动合并到我存储库中的另一个 b运行ch 中。我使用以下方法找出差异:

git diff branchA...branchB path/to/files > diff.patch

效果很好,展示了我所有的变化。然后我手动更新了 b运行chA 中我想要的文件并提交了它们。 Git 状态显示一切都是最新的。现在我想再次 运行 差异以确保我没有遗漏任何东西。

然而,当我 运行 差异时,它向我展示了与我第一次 运行 它时所有相同的差异。例如,它向我显示在 b运行chB 中添加的行,即使确切的行现在存在于 b运行chA 中。

我猜这是因为我手动进行了合并,但我现在如何再次获得正确的差异?

three-dot 语法 (branchA...branchB) 在 git diff 中有一个特殊的含义,不同于它在大多数 1 中的正常含义 git 命令。

通常,X...Y 告诉 git rev-list 构建两个集合的 对称差异 :第一个集合是从 [=16 可到达的所有提交的集合=],第二个是可从 Y 到达的所有提交的集合。对称差为"all commits reachable from either X or Y, but not from both"。换句话说,如果追溯 XY 的历史最终得出一些共同的提交——这些提交是最低共同祖先——那么我们只剩下那些提交既不是 LCA 也不是祖先。

LCA 也就是我们所说的合并基地。在大多数情况下,只有一个 LCA,它是您确定的两个提交的(单一)合并基础。当这两个提交是两个不同分支的提示时,这很有意义,对称差异是 "commits after the merge base, on both branches",这是 cherry-picking.

等操作的一个很好的起点

不过 git diff 不太好。原因是 git diff 比较 两个并且只有两个 提交。2 如果你给它一个包含许多提交的大集合,它有不知道该怎么办。

因为 git diff 只适用于一对提交,它重新定义了 two-dot X..Y 和 three-dot X...Y 语法。对于 two-dot 版本,它只需要两个命名的提交,但对于 three-dot 版本,它做了一些聪明的事情:

git diff X...Y 表示 git diff $(merge-base X Y) Y

由于 three-dot 语法通常在(通常是单个)合并基础处停止,因此 git diff 假设您在给它提供此语法时必须打算做某事 merge-ish。因此,它会为您命名的提交找到一个合并基础。幸运的是,只有一个最低公共祖先,因此这是 the 合并基础。然后它将合并基础提交与两个命名提交中的第二个进行比较。

如果这两个名称是 branch-names(在这种情况下),并且您签出 first 分支并向其添加一个或多个提交(正如你在本例中所做的那样),我们可以确定两个 branch-tips 的合并基础没有改变。 second 分支也没有改变(因为我们只修改了第一个,向它添加了新的提交)。因此,如果我们现在 运行 一个新的 git diff 使用相同的 three-dot 语法,我们将比较 完全相同的两个提交 .

要亲自查看,请使用:

git merge-base branchA branchB

并记下打印的提交 ID,然后检查 branchA 并添加一个提交和 运行 相同的 git merge-base 命令。您可能还想在添加新提交之前和之后 运行 git rev-parse branchAgit rev-parse branchB :您会看到 branchA 获取新提交而 branchB 没有.

您还可以运行:

git rev-list --left-right branchA...branchB

这将生成对称差异提交列表(不包括 merge-base),用 <> 字符标记它们以显示两者中的哪一个(左或右)边)祖先选择了他们。 (将 rev-list 更改为 log --graph 以查看日志消息。)


1任何使用 git rev-list 的 git 命令。这包括 git log(实际上,git loggit rev-list 本质上是相同的命令,具有不同的默认输出)。

2好吧,git 也执行它所谓的 "combined diffs" 合并提交,但您不能从 git diff 中获取一个本身。