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.