Dec 11th, 2020 - written by Kimserey with .
When using a branching mechanism like GitFlow or Mainline, we usually create pull request from short lived branches to long lived branches (e.g.
master). The pull request diff page gives a view of the changes that were brought by the branch. By default all repositories managers like GitHub, GitLab or Bitbucket uses “triple dot” diff to show diff. In this post we’ll look at the difference between “triple dot” and “double dot” diff.
In order to check the diff between two branches in the follownig scenario:
1 2 3 C (3fd5643, my-branch) / B---D (5b07cc7, master)
with git we can use
1 git diff master my-branch
which is the same as
1 git diff master..my-branch
This will give us the difference between
my-branch, which is the difference between
It will show:
In this particular example, because my-branch is branched from master, by definition there is no extra commit on master that C does not contain hence a simple
git diff master is guaranteed to show only the changes brought by my-branch.
In other words, for branches, diff double dot gives us the difference between the tip of two branches.
We saw how double dot allows us to see the diff between two latest of two branches.
Now with our previous example, let’s say someone merge a change:
1 2 3 C (3fd5643, my-branch) / B---D---E (5b07cc7, master)
If we make a pull request, we only want to show our changes based of the merge-base (also known as most common ancestor). We don’t want
E to show up in our pull request diff - because first it wasn’t brought by us, and second it’s already on
master, there is no value in adding it for review “again”.
In order to do this, repository managers like GitHub or GitLab use the “triple dot”:
1 git diff master...my-branch
The triple dot compares
my-branch with the most common ancestor between
my-branch. This allows to only show changes brought up by
my-branch. To find the base, we can use
1 git merge-base master my-branch
This would give us the most recent common ancestor between
my-branch which we could then use with a double dot diff to find just the changes added by
1 git diff `git merge-base master my-branch` my-branch
This essentially results in the same as a triple dot diff.
A common scenario where pull-requests diffs can be confusing is when we branch from
master, then branch out of the first branch and employ a
squash merge strategy to merge pull-requestes. This scenario can happen when we want to break a pull-requests in smaller bits to gradually get them reviewed and don’t want to keep intermediate commits:
1 2 3 4 5 D (feature-2 / open PR against master #2) / C (feature-1 / open PR against master #1) / B (master)
In this scenario, we branch out of
B where we add a commit
feature-1 and submit a pull-request #1 to merge back to
master. At the same time, we branch out from
C and create a commit
feature-2 where we open a second pull-request against
At this point, PR #1 contains the changes from
C and PR #2 contains the changes from
D in their diffs.
Now if PR #1 is merged,
1 2 git checkout master git merge --squash feature-1
we’ll get the following graph:
1 2 3 4 5 D (feature-2: open PR against master #2) / C (feature-1) / B---E (master: squashed commit of feature-1)
master now has
E which contains the merge of
C, the result of the squash merge.
But if we check the pull-request #2 of
feature-2, because it uses triple-dot diff, the pull-request will still show the diff of
D even though
C has been merged to
E). We can see the same diff if we do:
1 git diff master...feature-2
This is because even thought
C is merged, the merge-base hasn’t changed due to the squash behaviour. To fix this we can either merge
1 2 git checkout feature-2 git merge master
which will bring the graph to:
1 2 3 4 5 D---F feature-2 (Merge branch 'master' into feature-2) / / C / (featuer-1) / / B---E (master: squashed commit of feature-1)
Hence moving the common ancestor to
E and therefore the difference would only be what is in
E which would be only
D, as the changes in
Or the other way would be to rebase
1 git rebase --onto master feature-1 feature-2
which will result in the following graph:
1 2 3 4 5 C (featuer-1) / B---E (master: squashed commit of feature-1) \ D' (feature-2: rebased from feature-1 onto master)
And that concludes today’s post, I hope you learnt the difference between triple dot and double dot diff in git and how repository managers display the diffs in pull-request!