Two and three dots with log¶
Note
Remember it’s different for git diff
- see Two and three dots with diff and
Pain in the dots
Logging without dots¶
Two dots is the default in git log
. That is, if you do:
git log master topic
what you’ll get is the same as if you asked for:
git log master..topic
So logging without dots is the same as Logging with two dots.
Logging with two dots¶
Let’s say you asked for this:
git log start-branch..end-branch
You will see a log of a series of commits. The commits will be all the commits
reachable from end-branch
that are not reachable from start-branch
.
In fact, the two dot form of log is shorthand. The git log
line above is
shorthand for:
git log ^start-branch end-branch
end-branch
above means — “show me all commits that can be
reached from end-branch
”. ^start-branch
means — “excluding any
commits that can be reached from start-branch
”.
Note
What does “reachable” mean?
A commit, \(s\), can be reached from some commit, \(t\), if and only if there is a path from \(s\) to \(t\) along the ancestry graph of the commits.
The ancestry graph is the directed acyclic graph of the history, where the nodes are the commits and the edges are directed backwards from nodes to their parents.
Put more formally, a sequence of commits, \(v_0, v_1, ..., v_n\), forms a path between \(v_0\) and \(v_n\) if and only if \(v_{i-1}\) is the parent of \(v_i\) for \(i=1\) to \(i=n\). A commit is also reachable from itself, so a commit sequence of length 1 is defined as forming a path.
Obviously git log start-branch..end-branch
cannot include the commit
pointed to by start-branch
because you can always reach start-branch
from itself.
Let’s say we have this history:
H--I--J topicB
/
| E---F---G topicA
|/
A---B---C---D master
What would git log topicB..topicA
show? From topicA
we can
reach G, F, E, D, C, B, A
. From topicB
we can reach J, I, H, D, C, B,
A
. So, the commits reachable from topicA
but not reachable from
topicB
are G, F, E
.
Logging with three dots¶
Now you ask for this:
git log start-branch...end-branch
There are three dots between start-branch
and end-branch
. This three
dot version of the command finds all commits that are reachable from
start-branch
, OR that are reachable from end-branch
BUT that are NOT
reachable from both start-branch
AND end-branch
.
Put another way, you will see all commits reachable from start-branch
AND
all commits reachable from end-branch
BUT excluding any commits reachable
from any common ancestor. As the gitrevisions
man page puts it, the three
dots command above is equivalent to:
git log start-branch end-branch --not $(git merge-base --all start-branch end-branch)
Put another way, if \(S\) is the set of all commits that can be reached from
start-branch
and \(E\) is the set of all commits that can be reached
from end-branch
then the commits returned from the three dot version of log
are:
(\(X \setminus Y\) denotes the set of members of \(X\) that are not in set \(Y\)).
By example, from the history above, let’s think about what would we get from:
git log topicB...topicA
From topicA
we can reach this set of commits — G, F, E, D, C, B,
A
. From topicB
we can reach J, I, H, D, C, B, A
. That means that we
can reach D, C, B, A
from both of topicA
AND topicB
. So the
returned commits would be G, F, E, J, I, H
.