Git diff提交范围中双点".. "和三点"..."有什么区别?

272

以下命令有什么区别?

git diff foo master   # a 
git diff foo..master  # b
git diff foo...master # c

diff手册对此有介绍:

比较分支

$ git diff topic master    <1>
$ git diff topic..master   <2>
$ git diff topic...master  <3>
  1. 主题分支和主分支之间的差异。
  2. 同上。
  3. 自主题分支开始后,在主分支上发生的更改。

但对我来说并不完全清楚。

5个回答

438

由于我已经创建了这些图片,所以我认为在另一个答案中使用它们可能是值得的,尽管 ..(点-点)和 ...(点-点-点)之间的区别描述与 manojlds 的答案 本质上是相同的。

git diff 命令通常¹ 只会显示在提交图中恰好两个点之间状态的树之间的差异。 在 git diff 中,..... 表示如下:

# Left side in the illustration below:
git diff foo..bar
git diff foo bar  # same thing as above

# Right side in the illustration below:
git diff foo...bar
git diff $(git merge-base foo bar) bar  # same thing as above

git diff 的不同提交规范示例的插图

换句话说,git diff foo..bargit diff foo bar 是完全相同的;两者都将展示你两个分支 foobar 之间的差异。而 git diff foo...bar 则会展示你在这两个分支上的“合并基底”和 bar 的最新提交之间的差异。“合并基底”通常是这两个分支之间最后一个公共提交,所以这个命令将展示你在 bar 上的变更,同时忽略 foo 分支中间的所有工作。

这就是关于 git diff..... 表示方法的全部内容。然而...


... 这里的一个普遍困惑是,在像 git log 这样需要一组提交作为一个或多个参数的命令中使用 ..... 表示法时,其意义略有不同。(所有这些命令最终都会使用 git rev-list 从它们的参数中解析一组提交。)

.....git log 中的含义可以如下图所示:

git log 的提交范围不同表示方法的插图

因此,git rev-list foo..bar 展示你在分支 bar 上所有不在分支 foo 上的内容。而 git rev-list foo...bar 则展示你在 foobar 上的所有提交,但不包括两者都有的提交。第三个图表只是表明,如果你列出这两个分支,那么你将会得到它们两者中有的或所有的提交。

总的来说,我觉得这有点混乱,我认为这些提交图表很有帮助 :)

¹ 我只说“通常”是因为例如在解决合并冲突时,git diff 将展示一个三方合并。


1
我喜欢你的图表。我也在一段时间前想出了自己的图表。我有一些关于自己的git diff图表的想法,稍后会制作。 - user456814
52
有人注意到了吗?在 git diff 中,..... 的效果与 git rev-list 相比似乎是相反的! - Robert Siemer
3
你说的“这就是你需要知道的……”就让我信服了。Git 中有很多类似的符号和术语,在不同的上下文中意义不同;感谢您如此清晰地澄清了这一点。 - ShreevatsaR
感谢提到rev-list。我在寻找通过rev-parse执行rev-list所执行的操作的方法时发现了这个问题。 - Mad Physicist
1
@foxiris 当 ^ 出现在引用提交的东西之前时,它表示“不是”。因此,git rev-list bar ^foo 的意思是“除了 foo 中的任何内容之外,所有在 bar 中的内容都包括在内”。 - Mark Longair
显示剩余3条评论

104

我的综合版关于使用 .. vs ... 以及使用 diff vs log的比较。

Diff vs Log & .. vs ..


4
如果没有那么多不同颜色和混杂着 ../... 的集合运算,这会非常好。例如在 log A...B 中,不清楚该命令返回图表中的交集(白色部分)还是 A-B 并集的剩余部分(绿色)。去掉所有的集合操作符,只使用一种颜色会更加明确。 - xealits
2
这个真的应该是 diff A..B <—> log A...B 吗?也就是说,真的用两个点的diff对应三个(!)点的log吗?还是图像中存在错别字。从点的颜色编码方式来看,我认为图像中有一个错别字。左下角:log A...B 应该是 log A..B,对吧?右侧的log应该是 ... 而不是 .. - KajMagnus
3
@KajMagnus,实际上红色/蓝色只是用于区分2个点和3个点(无论是与diff还是log一起使用)。该图表是正确的。在第一列中,使用2个点的差分结果类似于使用3个点的日志(这就是整个图表的目的所在)。使用2个点进行差分将提供自两个版本到发散点的代码更改(由提交周围的绿色气泡和货车图表的绿色部分所示),而使用3个点的日志将提供自两个版本到发散点的更改日志(提交消息)。 - DolphinDream
3
@KajMagnus,这似乎很令人困惑和矛盾,因为git loggit diff都以不同的方式处理...。_log_使用gitrevisions(请参阅man 7 gitrevisions)来解析...,但是_diff_没有。_diff_使用自己定义的...,它与gitrevisions使用的不同。 - snath03
2
@snath03 好久不见了,但我想我用的是LibreOffice Draw。 - DolphinDream
显示剩余5条评论

38
git diff dev master 比较 dev 分支和 master 分支的最新提交之间的差异。 git diff dev..master 也可以达到同样的效果,这两个命令是等价的。 git diff dev...master 比较 master 分支自它与 dev 分支的共同祖先(即git merge-base dev master)以来引入的变更。换句话说,它只显示自共同祖先以来 master 分支引入的变更。 这里的Learn.GitHub "what a merge would introduce" example (archived)解释了何时使用这两个命令:
$ git diff master dev

它会告诉你一个函数是从第一个文件添加的,还有一行文本被添加到了README中。为什么?因为在这个分支上,README仍然有原始的那一行,但是在'master'上你已经将其删除 - 因此直接比较快照看起来好像是'dev'添加的。

你真正想要比较的是'dev'自分支分叉以来所做的更改。为此,Git有一个很好的简写:

$ git diff master...dev

2
git diff foo...master 自从foo的共同祖先以来,master分支引入了哪些更改 - 0fnt
@manojlds 好的,那么另一个问题是,如果你在 dev 分支上提交了你的更改(函数),并将这些更改推送到远程 dev 分支,这是否意味着可见的更改只有函数还是函数和 readme 都会被更改? - David
1
如果我没记错的话,GitHub的拉取请求差异使用了三个点。是这样吗? - Shaun Luttin
1
GitHub示例页面的链接已损坏。 - K.-Michael Aye
1
我从未知道有三个点(...)的版本存在。我一直使用 git diff $(git merge-base master dev) dev 而不是 git diff master...dev,但它们似乎是等效的。因此,对于任何想知道的人来说,显然 git diff master...devgit diff $(git merge-base master dev) dev 是相同的东西。 - Gabriel Staples

6
git diff foo master

这将展示在那个时刻 foo 分支和 master 分支之间的差异。

git diff foo..master

同时还会显示在该时间点上foo分支和master分支之间的差异(等同于上述内容)。

git diff foo...master

将展示自从master分支创建以来,foo分支与之间的所有差异。

因此,前两个命令是相同的,最后一个命令显示更广泛的差异历史。


2

git log tree

上面的图片相当于下面的图形树

A0 <- A1 <- A2 <- A3 (master)
   \
    C0 <- C1 (test)

一张图片胜过千言万语,下面展示了 .....^ 之间的区别。
$ git log master..test
# output C0 C1

$ git log ^master test
# output C0 C1

$ git log master…test
# output A1 A2 A3 C0 C1

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