8

I have created test repository: https://github.com/Labutin/CompareBranches Let's try to clone and compare two branches branch_01 and branch_02

$ git clone https://github.com/Labutin/CompareBranches.git
Cloning into 'CompareBranches'...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.

$ git diff remotes/origin/branch_01..remotes/origin/branch_02 --exit-code
$ echo $?
0

So, we have two branches which are identical.

But if I try to use Web UI to compare two branches https://github.com/Labutin/CompareBranches/compare/branch_01...branch_02 it shows 1 file changed. Why? Whats wrong with Web UI? Maybe I have to modify URL?

dhythhsba
  • 972
  • 2
  • 11
  • 21
  • If you use VS code, the GitLens extension can compare files between branches without caring of the commits ahead/behind or history of them: https://stackoverflow.com/questions/42112526/how-to-compare-different-branches-in-visual-studio-code – Guillermo Abad Lopéz May 04 '23 at 09:59

2 Answers2

7

This question is really about GitHub—but we can use it to construct a question about Git.

After I clone your repository:

$ git clone https://github.com/Labutin/CompareBranches.git
Cloning into 'CompareBranches'...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 0), reused 0 (delta 0), pack-reused 0

I can view all of the commits in it:

$ cd CompareBranches
$ git log --all --decorate --oneline --graph
* 5707453 (origin/branch_02) Commit to branch_02
| * c0e3722 (origin/branch_01) commit to branch_01
|/  
* 0e9a4e3 (HEAD -> master, origin/master, origin/HEAD) Initial commit

So there are three commits, whose actual (but abbreviated) names are 0e9a4e3, c0e3722, and 5707453. The remote-tracking name origin/master refers to the first commit, and the other two remote-tracking names origin/branch_01 and origin/branch_02 refer to the other two commits.

If we ask Git to compare commits c0e3722 and 5707453, we see no difference:

$ git diff c0e3722 5707453

But if we ask Git to compare, say, commit 0e9a4e3 to commit 5707453, we see that these two commits are different:

$ git diff 0e9a4e3 5707453
diff --git a/README.md b/README.md
index f00f3be..b183451 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
-# CompareBranches
\ No newline at end of file
+# CompareBranches
+commit1

Since commits c0e3722 and 5707453 have the same content, comparing 0e9a4e3 to either one will show this same change.

The git diff command has special syntax

When we want to compare two commits, we can run:

git diff <thing-1> <thing-2>

and Git will compare thing-1 to thing-2. The part inside the angle brackets can be anything that identifies a commit: a raw hash ID like 0e9a4e3, or a branch name, or a remote-tracking name, for instance.

If we run:

git diff <thing-1>..<thing-2>

Git does exactly the same thing: it compares the two things.

But, if we use three dots instead of two:

git diff <thing-1>...<thing-2>

Git does something special. Instead of comparing the two commits we named, Git finds a third commit. In particular, Git looks for a merge base commit, from which both the named commits descend. We see this in the git log --graph output above: the two tip commits both descend from commit 0e9a4e3. So that's the merge base, and therefore, with three dots instead of two, that's the commit Git uses on the left side of the comparison.

The right side is just <thing-2> itself.

As it turns out, GitHub is doing the same thing here, which is why GitHub's link has three dots in it: GitHub is mimicking the syntax of git diff, on purpose.

torek
  • 448,244
  • 59
  • 642
  • 775
2

Note the triple dot in the Github URL. Github is doing git diff remotes/origin/branch_01...remotes/origin/branch_02. r1..r2 is subtly different from r1...r2.

r1..r2 asks for all commits reachable from r2 except those reachable by r1.

r1...r2 asks for "all the commits that are reachable by either of two references but not by both of them."

Let's look at these in your repo.

A - B [branch_02]
 \
  C [branch_01]

git log branch_01..branch_02 gives us B. branch_02 can reach A and B, but branch_01 can also reach A, so this gives us B.

git log branch_01..branch_02 gives us B and C. branch_01 can reach A and C. branch_02 can reach A and B. Both branches can reach A, so it is excluded. We're left with B and C.

For more, see Revision Selection, Double Dot and Triple Dot in Pro Git.

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • GitHub actually doesn't "do" the two dot comparison at all, it seems. – torek Aug 09 '18 at 22:24
  • Looks like two dot doesn't work. Try https://github.com/Labutin/CompareBranches/compare/branch_01..branch_03 Branches are not equal. – dhythhsba Aug 09 '18 at 22:26
  • 1
    @dhythhsba I think you're right. It looks like Github is interpreting `branch_01..branch_03` as a reference and defaulting the base to `master`. Rather than throwing an error because the reference doesn't exist, it's reporting no difference. For example, https://github.com/Labutin/CompareBranches/compare/aldkflakjfkjd – Schwern Aug 09 '18 at 22:39