Getting the logs within a specified tag range works as expected:
git log v1..v2 --oneline
However, I want the tag range logs of origin/master
. When I provide the tag range and remote/branch arguments I do not get as expected.
git log origin/master v1..v2 --oneline
The output is the same as if v2
is not supplied at all. Indeed, when v2
is removed, the output is from v1
to HEAD
of origin/master
.
How can I make this command respect the tag range argument?
Git version 2.9.0
The problem is that this:
git log origin/master v1..v2 --oneline
does not mean what you think it means.
The gitrevisions syntax v1..v2
is short for v2 ^v1
. When you combine this with origin/master
, you are giving Git all three selectors:
v2
;origin/master
;v1
.Suppose that you have this graph:
o--o--o <-- br1
/
...--o--A--B--o <-- br2
\
o--o--o <-- br3
where each round o
represents a commit. The names br1
, br2
, and br3
each point to one specific commit, which is the tip commit of each branch.
It's the actual commits themselves that point back to their previous commits. I've given two particular commits A
and B
one-letter names here: these commits are extra-interesting, because commit B
is on both branches br1
and br2
, and commit A
is on all three branches. This is because there are two commits "after" B
that both point back to B
. Commit B
is the merge base of branches br1
and br2
, so it's on both branches. Likewise, B
points back to A
, but so does a commit on branch br3
: commit A
is the merge base of br3
and br2
(and also the merge base of br3
and commit B
). So A
is on both branches br2
and br3
, and since B
is on br1
, A
is necessarily also on br1
.
The idea of following commits back (leftward in this graph) to their parent commits is how Git determines this reachability.
Think of v1
and v2
as labels for specific commits, just like branch names, because that's how Git makes them work. Tag names and branch names are both just names for one specific commit. The difference is that a tag name is global—your tag v2
should match everyone else's v2
—and that tag names are not supposed to move. A branch name like br2
automatically moves when we add a new commit to the branch. A tag name doesn't.
Let's draw another graph, this time with a v1
and v2
in it. I don't have your repository so this is not an accurate graph of your repository, but it will, I hope, illustrate the principle:
...--o--v1--A--B--C--D--v2 <-- branch
\ \
F--G--H--I---J--K <-- origin/master
When we say ^v1
we are asking Git to "paint commit v1, and all earlier commits, red temporarily". This keeps them from being selected by the rest of the names or IDs we supply. Then, when we say v2
, we are asking Git to "paint commit v2, and earlier unpainted commits, green temporarily." This paints D
, C
, B
, and A
, but stops after that as v1
is painted red.
Adding origin/master
tells Git to paint commit K
green, and work backwards. Commit J
is a merge commit, so Git paints J
green and then begins painting both parents, D
and I
. Eventually it works its way back to the already-red v1
so it stops there.
Finally, Git looks at all the green-painted commits. That's all of A
through K
plus v2
.
If we leave out v2
—if we ask for
git log ^v1 origin/master
for instance—we will no longer see commit v2
itself, as it was never painted green temporarily; but we will still see all of A
through K
.
Note that this—the ^v1 origin/master
form—is equivalent to v1..origin/master
. I think you are writing origin/master v1..
or v1.. origin/master
(note the space after the ..
!); in this case, the "missing" name is filled in with HEAD
, which names your current commit. So these other commands use ^v1 HEAD origin/master
as the red-paint and green-paint names.
Aside: all the "red paint" (hat-prefixed) operations are done first; or equivalently, you can think of this as "paint red over green, but never paint green over red" and do them in either order. (And remember, all the paint washes off again, right after the Git command finishes. :-) )