In one of my projects, I am trying to get the most recent tag after checking out a branch, or from origin/master
. If I run git tag -l --sort=-creatordate --merged | head -n1
or a git describe
, I get the same result, but its from a tag from far in the past. If i get rid of the --merged
, it shows the correct, most recent tag in the project.
Checking out a specific branch gives me back the correct, last known tag in the repo, but if its from origin/master
or FETCH_HEAD
, it gives me one in the past.
I'm mainly wondering how the --merged
flag works, because the manual pages just says it Only list tags whose commits are reachable from the specified commit
.
There are two parts to this:
git tag -l --merged
works, which uses the concept itself.Reachability is a property of a graph, or in Git's case, a directed graph. Consult either the Wikipedia article, or Think Like (a) Git, for a proper explanation. (Or see one of my many StackOverflow answers showing how to determine which branches contain various commits.) The rest of this assumes you already understand reachability.
A tag, in Git, is just a fancy pointer: in effect, an arrow pointing to one particular commit. But there's one other special thing about tags in Git: They can point to a tag object, rather than directly to a commit.1 The tag object has the annotations for git tag -a
or message for git tag -m
. The tag object then holds the raw hash ID of the underlying commit.
This brings us to the wording in the documentation:
the manual pages just says [
--merged
]Only list tags whose commits are reachable from the specified commit.
We run:
git tag -l --merged <commit-specifier>
so we pick one commit. That's the "specified commit".
The git tag -l
operation will now iterate through every tag. The tag either points directly to a commit, or to an annotated tag object that points to a commit. That commit exists somewhere in the overall Git commit graph. The question that --merged
applies is: can we reach the tagged commit from the specified commit?
If we can reach the tagged commit, git tag -l
lists the tag. If not (or if we must skip the tag because it does not tag a commit after all), git tag -l
omits the tag.
1A tag name is actually allowed to point to a tree or blob object, as well as to a commit or annotated tag object. In this case there is no commit at all, and git tag -l --merged
will just ignore that tag. Also, an annotated tag object can actually refer to any other kind of object, not just a commit object. So the true rule for resolving a tag name is:
We now know the object to which the tag refers. If it's a commit, it's a candidate for this --merged
listing. If not, it's not.