Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git describe with two tags on the same commit

Tags:

git

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?

like image 231
Bill Door Avatar asked Nov 11 '11 02:11

Bill Door


People also ask

Can you have multiple tags on the same commit git?

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.

Is it possible to refer to a commit by using name of a tag?

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.

What does git describe -- tags do?

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.

Can two commits have same tag?

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.


4 Answers

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.
like image 164
Adrian Cornish Avatar answered Sep 29 '22 15:09

Adrian Cornish


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
like image 22
qneill Avatar answered Sep 29 '22 13:09

qneill


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.

like image 32
jgmjgm Avatar answered Sep 29 '22 15:09

jgmjgm


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*'

like image 45
eephillip Avatar answered Sep 29 '22 13:09

eephillip