We occasionally have two tags on the same commit. When we use git describe for that commit, git describe always returns the first tag. My reading of the git-describe man page seems to indicate that the second tag should be returned (which makes more sense).
SEARCH STRATEGY For each committish supplied, git describe will first look for a tag which tags exactly that commit. Annotated tags will always be preferred over lightweight tags, and tags with newer dates will always be preferred over tags with older dates. If an exact match is found, its name will be output and searching will stop.
Is there a way to have git describe
return the second tag?
To push multiple tags simultaneously pass the --tags option to git push command. When another user clones or pulls a repo they will receive the new tags.
If the tag points to the commit, then only the tag is shown. Otherwise, it suffixes the tag name with the number of additional commits on top of the tagged object and the abbreviated object name of the most recent commit.
The `git describe` command finds the most recent tag that is reachable from a commit. If the tag is on the current commit, then only the tag is shown. Otherwise, it shows the tag name appended with the number of additional commits and the abbreviated SHA of the current commit.
Tags are usually used to indicate a particular state or release of a project. The following are the rules for a tag: Each tag must be unique; two separate commits cannot have the same tag.
Have you tried any of the options to git describe?
--all
Instead of using only the annotated tags, use any ref found in .git/refs/. This option enables
matching any known branch, remote-tracking branch, or lightweight tag.
--tags
Instead of using only the annotated tags, use any tag found in .git/refs/tags. This option
enables matching a lightweight (non-annotated) tag.
From all I can tell, 'git describe' cannot disambiguate lightweight tags and so prints the first one it encounters. This snippet assumes tags follow a pattern sortable by 'sort -R' and will return the 'latest' tag on a given SHA:
git tag --contains SHA | sort -R | tail -1
Git's behaviour in this case is bizarre and confusing.
When I make two tags to the same commit, I notice that in .git/refs/tags
that each of the tags has it's own commit so it's theoretically possible to checkout an exact tag in a unambiguous fashion.
In practice that's not so.
Lets say I have commit ABCD. I make two tags to it (annotated), v1.0 and v2.0.
I then have something like this..
master -> ABCD
hotfix -> ABCD
v1.0 (3423) -> ABCD
v1.0 (4234) -> ABCD
When I checkout a branch such as master or hotfix, I notice that git simply stores in .git/HEAD
the ref to the branch so all is good, it's not ambiguous but a specific branch.
When I checkout the commit directly, it will be inherently ambiguous. HEAD will contain simply the hash of the commit, ABCD
.
When you checkout a tag such as v1.0 or v2.0, HEAD will not contain the tag ref or tag commit but instead the commit id, as if you checked out the commit directly!
Where this becomes confusing is that if you checkout a branch such as master, then check out a tag, git status and describe will show the right tag, the one that you checked out, even though it's ambiguous!
If you then however checkout another tag pointing to the same, it'll show the original tag. Switching from branch to tag, remembers the tag, switching from tag to tag doesn't.
I don't know if this is a bug or how git is even doing it (I'm guessing it repeats (.git/logs/HEAD) but given the behaviour appears arbitrary I'd hazard a guess that if you simply want to use a command to get at what the user has selected from the top down be it a tag, branch or commit then I don't think that's reliably supported.
If you're trying to use a command to get at the version automatically then you're going to need to either have the user manually enter the tag or have some procedure in place to eliminate collisions.
Lightweight tags (not annotated, don't have a commit themselves, are just a pointer straight to a commit) behave in the same strange manner. Given it's able to preserve exactly where the user checked out in one case but fails in the other, I'd propose that it's a bug and should be reported.
The use case for this is that the user only checks out one thing, even though that identifier might point to something with many other identifiers. For convenience, you want to get the identifier the user put in to use as an identifier for example for a build. Git's ability to remember that identifier is inexplicably inconsistent.
In this case your scripts will need to attempt to derive a single best identifier but if the identifier is ambiguous it should produce an error. You cannot rely on things such as git status or describe as sometimes they'll not produce what was last checked out as seen when switching from tag to tag rather than branch to tag.
This can be seen in .git/logs/HEAD
which appears to contain branch to tag reports but once you're on a tag doesn't log anything.
Describe appears to always return the most recent annotated (non-lightweight) tag. If you're mixing tag types, you should not assume consistent behaviour. Light weight tags appear to use the most recent version as well (presumably based on the file's time stamp rather than commit time) but aren't searched without --all
or --tags
. Even with --all, annotated tags appear to take precedence over more recent lightweight tags.
The only convenient way to get all identifiers for the current tag that I can find is to run git show-ref with dereference and to grep for your current commit. This won't include timestamps for sorting.
I had two tagging conventions on the same commit and wanted to keep the git describe
features such as dirty, sha, and commit counts past tag etc.
So in git version 2.24.1
since I had a unique differentiating string in one tag vs the other I just used the match operator. You can also use exclude
git describe --tags --match '*xXx*'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With