What are the differences between double-dot ".." and triple-dot "..." in Git commit ranges?
Asked Answered
C

5

608

Some Git commands take commit ranges and one valid syntax is to separate two commit names with two dots .., and another syntax uses three dots ....

What are the differences between the two?

Chirurgeon answered 20/1, 2009 at 20:32 Comment(1)
Related: Git diff .. ? What's the difference between having .. and no dots.Hypophosphite
I
294

It depends on whether you're using a log command or a diff command. In the log case, it's in the man git-rev-parse documentation:

To exclude commits reachable from a commit, a prefix ^ notation is used. E.g. ^r1 r2 means commits reachable from r2 but exclude the ones reachable from r1.

This set operation appears so often that there is a shorthand for it. When you have two commits r1 and r2 (named according to the syntax explained in SPECIFYING REVISIONS above), you can ask for commits that are reachable from r2 excluding those that are reachable from r1 by "^r1 r2" and it can be written as "r1..r2".

A similar notation "r1...r2" is called symmetric difference of r1 and r2 and is defined as "r1 r2 --not $(git merge-base --all r1 r2)". It is the set of commits that are reachable from either one of r1 or r2 but not from both.

Which basically means that you'll get all commits that are in either of the two branches, but not in both.

In the diff case, it's in the man git-diff documentation:

  git diff [--options] <commit>...<commit> [--] [<path>...]

      This form is to view the changes on the branch containing and up to
      the second <commit>, starting at a common ancestor of both
      <commit>. "git diff A...B" is equivalent to "git diff
      $(git-merge-base A B) B". You can omit any one of <commit>, which
      has the same effect as using HEAD instead.

Which is a bit fuzzy. Basically it means it shows only the differences in that branch compared to another branch: it looks for the last common commit with the first committish you gave it, and then diffs the second committish to that. It's an easy way to see what changes are made in that branch, compared to this branch, without taking notice of changes in this branch only.

The .. is somewhat simpler: In the git-diff case, it's the same as a git diff A B and just diffs A against B. In the log case, it shows all commits that are in B but not in A.

Irrawaddy answered 20/1, 2009 at 20:46 Comment(6)
It's pretty ridiculous how the meaning of .. and ... is exactly swapped for log and diff: log A..B is changes from merge base to B which is what diff A...B doesPickup
@Pickup Yeah, that is really poor usability. I recommend not to use the dot notations for git diff.Greenlet
Does this mean A...B == A..B + B..A?Balkh
@Balkh for git log this is absolutely yesDeuterium
@Balkh but for git diff it's the opposite! git diff A..B == git diff A...B + git diff B...A !Mouthwatering
@Pickup I'm embarrassed to admit how many years I used git while believing I was just really bad at remembering which one was two-dots and which one was three-dots -- never noticing that the times I "mixed them up" were correlated with which git command I was using.🤦🏻‍♂️Other
H
996

Using Commit Ranges with Git Log

When you're using commit ranges like .. and ... with git log, the difference between them is that, for branches A and B,

git log A..B

will show you all of the commits that B has that A doesn't have, while

git log A...B

will show you both the commits that A has and that B doesn't have, and the commits that B has that A doesn't have, or in other words, it will filter out all of the commits that both A and B share, thus only showing the commits that they don't both share.

Visualization with Venn Diagrams & Commit Trees

Here is a visual representation of git log A..B. The commits that branch B contains that don't exist in A is what is returned by the commit range, and is highlighted in red in the Venn diagram, and circled in blue in the commit tree:

 "git log A..B" diagramTree 1

These are the diagrams for git log A...B. Notice that the commits that are shared by both branches are not returned by the command:

 "git log A...B" diagramTree 2

Making the Triple-Dot Commit Range ... More Useful

You can make the triple-dot commit range ... more useful in a log command by using the --left-right option to show which commits belong to which branch:

$ git log --oneline --decorate --left-right --graph master...origin/master
< 1794bee (HEAD, master) Derp some more
> 6e6ce69 (origin/master, origin/HEAD) Add hello.txt

In the above output, you'll see the commits that belong to master are prefixed with <, while commits that belong to origin/master are prefixed with >.

Using Commit Ranges with Git Diff

Someday I might add my own explanation for how the commit ranges work with git diff, but for now, you might want to check out What are the differences between double-dot ".." and triple-dot "..." in Git diff commit ranges?.

See Also

Hypophosphite answered 12/6, 2014 at 14:13 Comment(14)
This answer actually explains the difference with a concise text, examples and pictures. I like it much better than the currently top voted answer which just quotes the unclear documentation. (tl;dr thanks to this answer I actually understand the difference.)Crackling
@Cupcake could you add the meaning off ... in git diff ?Aftertime
@Aftertime if I can spend an hour or so on it someday, then I will, but for now, I'm not going to be adding it any time soon. This other answer goes into an explanation for git diff, though I'm not sure if that answer will be clear for most people. As I commented on the question above, this question deals with git diff specifically.Hypophosphite
@Aftertime actually, now that you bring it up, I'll go ahead and link to that other question in my answer, for future readers like yourself.Hypophosphite
Isn't this actually the opposite? dig diff a..b is ALL diffs, or basically the same as git diff a b. Whereas git dif a...b is ONLY changes b has made since branching from a.Nuclei
@Nuclei I was confused, so I tried it on a repo that I set up with similar commits. a...b has more commits than a..b. So the diagram is correct.Stereotropism
At least for git log. For git diff maybe things are reversed: stackoverflow.com/questions/7251477/…Stereotropism
@Nuclei is right.. git diff A..B shows all commits that they don't have each other; and git diff A...B shows only B has according to shared ancestor.. git version 2.18.0Gwenore
Does "git log --right-only master...test" (three dots) equals to "git log master..test" (double dots) ?Arnelle
The explanation here is much better IMO https://mcmap.net/q/11932/-what-are-the-differences-between-double-dot-quot-quot-and-triple-dot-quot-quot-in-git-diff-commit-ranges. Mixing it with git log behaviour doesn't shed more light, but confusion.Tolmach
"This is a result of the type of diff we use on GitHub. We use git's three dot diff, which is the difference between the latest commit on the HEAD branch and the last common ancestor commit with the base branch." -- github.community/t5/How-to-use-Git-and-GitHub/…. So GitHub is using diff A...B by default, which is the same as log A..B, which means ... I don't quite follow haha. Can you someone help reconcile this SO answer with this explanation from the GitHub discussion link? Would appreciate any help.Loredo
Simpler explanation matthew-brett.github.io/pydagogue/git_diff_dots.htmlThreap
The answer looks very nice - and is plain wrong. It's the opposite, diff .. shows the changes of both branches, while diff ... shows only the changes in the branch in question, counted from the common ancestor. The accepted answer looks less nice, but has the advantage of being right.Margay
@Margay this answer is correct, and your comment is also correct. .. and ... behave differently in git diff and git log. See the diagram in this answer for clarification - https://mcmap.net/q/11932/-what-are-the-differences-between-double-dot-quot-quot-and-triple-dot-quot-quot-in-git-diff-commit-rangesMayweed
I
294

It depends on whether you're using a log command or a diff command. In the log case, it's in the man git-rev-parse documentation:

To exclude commits reachable from a commit, a prefix ^ notation is used. E.g. ^r1 r2 means commits reachable from r2 but exclude the ones reachable from r1.

This set operation appears so often that there is a shorthand for it. When you have two commits r1 and r2 (named according to the syntax explained in SPECIFYING REVISIONS above), you can ask for commits that are reachable from r2 excluding those that are reachable from r1 by "^r1 r2" and it can be written as "r1..r2".

A similar notation "r1...r2" is called symmetric difference of r1 and r2 and is defined as "r1 r2 --not $(git merge-base --all r1 r2)". It is the set of commits that are reachable from either one of r1 or r2 but not from both.

Which basically means that you'll get all commits that are in either of the two branches, but not in both.

In the diff case, it's in the man git-diff documentation:

  git diff [--options] <commit>...<commit> [--] [<path>...]

      This form is to view the changes on the branch containing and up to
      the second <commit>, starting at a common ancestor of both
      <commit>. "git diff A...B" is equivalent to "git diff
      $(git-merge-base A B) B". You can omit any one of <commit>, which
      has the same effect as using HEAD instead.

Which is a bit fuzzy. Basically it means it shows only the differences in that branch compared to another branch: it looks for the last common commit with the first committish you gave it, and then diffs the second committish to that. It's an easy way to see what changes are made in that branch, compared to this branch, without taking notice of changes in this branch only.

The .. is somewhat simpler: In the git-diff case, it's the same as a git diff A B and just diffs A against B. In the log case, it shows all commits that are in B but not in A.

Irrawaddy answered 20/1, 2009 at 20:46 Comment(6)
It's pretty ridiculous how the meaning of .. and ... is exactly swapped for log and diff: log A..B is changes from merge base to B which is what diff A...B doesPickup
@Pickup Yeah, that is really poor usability. I recommend not to use the dot notations for git diff.Greenlet
Does this mean A...B == A..B + B..A?Balkh
@Balkh for git log this is absolutely yesDeuterium
@Balkh but for git diff it's the opposite! git diff A..B == git diff A...B + git diff B...A !Mouthwatering
@Pickup I'm embarrassed to admit how many years I used git while believing I was just really bad at remembering which one was two-dots and which one was three-dots -- never noticing that the times I "mixed them up" were correlated with which git command I was using.🤦🏻‍♂️Other
A
34

I think the biggest source of confusion about two dots versus three dots is because when used with git diff it's sort of the opposite of when used with git log.

Please see the other answers, the actual documentation, or numerous blog posts for the exact details, but I find these simple statements to work well for conveying the right idea:

git log  A..B   # Show me commits only on B.
git log  A...B  # Show me commits only on A or only on B.
git diff A..B   # Show me changes only on A or only on B.
git diff A...B  # Show me changes only on B.
Ambassador answered 13/10, 2021 at 17:2 Comment(1)
This article explains well enough I believe matthew-brett.github.io/pydagogue/git_diff_dots.htmlBootblack
T
16

This is a bit confusing = So here is a how it is actually for this flow

      A---B---C topic
     /
D---E---F---G main

https://github.com/alexcpn/gitdiffs/pull/2/commits https://github.com/alexcpn/gitdiffs/pull/1/commits

Git Log Behaviour

1 > git log --oneline --graph topic...main
* 9411a8b (HEAD -> main) G
* 3a567aa F
* aad429f (topic) C
* 6b1eb5a B
* d65c129 A
topic
D
E
A
B
C
main
D
E
F
G
In topic and main, but not in
both
2 git log --oneline --graph main...topic
* 9411a8b (HEAD -> main) G
* 3a567aa F
* aad429f (topic) C
* 6b1eb5a B
* d65c129 A
topic
D
E
A
B
C
main
D
E
F
G
Same as above
3 git log --oneline --graph topic..main
* 9411a8b (HEAD -> main) G
* 3a567aa F
topic
D
E
A
B
C
main
D
E
F
G
In main,but not in topic
4 git log --oneline --graph main..topic
* aad429f (topic) C
* 6b1eb5a B
* d65c129 A
topic
D
E
A
B
C
main
D
E
F
G
In topic, but not in main

Git Diff behaviour

1 git diff topic..main
D
E
-A
-B
-C
+F
+G
topic
D
E
A
B
C
main
D
E
F
G
what's in main
whats not in main compared
to topic
2 git diff main..topic
D
E
-F
-G
+A
+B

+C
topic
D
E
A
B
C
main
D
E
F
G
whats in topic
whats not in topic compared to
main
3 git diff main...topic
D
E (you may get newline here)
+A
+B
+C
topic
D
E
A
B
C
main
D
E
F
G
In topic,but not in main
4 git diff topic...main
D
E
+F
+G
topic
D
E
A
B
C
main
D
E
F
G
In main, but not in topic
Threap answered 9/3, 2021 at 8:54 Comment(1)
might want to change 'master' -> 'main' to reduce confusionAlfonso
P
-1

The '...' syntax is shorthand for "diff between two branches, starting with the common ancestor". From the official Git book:

git diff master...contrib

is equivelent to:

$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db
Psychiatrist answered 24/2, 2023 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.