How to programmatically determine whether the Git checkout is a tag and if so, what is the tag name

GitBashShell

Git Problem Overview


In a Unix or GNU scripting environment (e.g. a Linux distro, Cygwin, OSX), what is the best way to determine whether the current checkout is a Git tag. If it is a tag, how can I determine the tag name?

One use of this technique would be automatically labeling a release (like svnversion would do with Subversion).

See my related question about https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch">programmatically detecting the Git branch.

Git Solutions


Solution 1 - Git

The solution to your question is to use

git describe --exact-match HEAD

(which would consider only annotated tags, but you should use annotated and probably even signed tags for tagging releases).

If you want to consider all tags, also lightweight tags (which are usually used for local tagging), you can use --tags option:

git describe --exact-match --tags HEAD

But I think you have "XY problem" here, in that you are asking question about possible solution to the problem, rather than asking question about a problem... which can have better solution.

The solution to your problem is to take a look how Git does it in [GIT-VERSION-GEN][] script, and how it uses it in its [Makefile][].

[GIT-VERSION-GEN]: http://git.kernel.org/?p=git/git.git;a=blob;f=GIT-VERSION-GEN;hb=HEAD "git.kernel.org - git/git.git/blob - GIT-VERSION-GEN" [Makefile]: http://git.kernel.org/?p=git/git.git;a=blob;f=Makefile;hb=HEAD#l204 "git.kernel.org - git/git.git/blob - Makefile"

Solution 2 - Git

A better solution (from Greg Hewgill's answer in the other question) would be:

git name-rev --name-only --tags HEAD

If it returns "undefined" then you are not on a tag. Otherwise it returns the tag name. So a one-liner to do something like my other answer would be:

git_tag=`git name-rev --name-only --tags HEAD | sed 's/^undefined$//'`

Interactive shell example of how it works:

$ git checkout master
Already on "master"
$ git name-rev --name-only --tags HEAD
undefined
$ git checkout some-tag
Note: moving to "some-tag" which isnt a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 1234567... Some comment blah blah
$ git name-rev --name-only --tags HEAD
some-tag

Solution 3 - Git

Usage of git-name-rev is preferred for scripts, since it is part of git plumbing, whereas git-describe is part of porcelain.

Use this command to print the name of the tag if the HEAD points to one, otherwise nothing.

git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null | sed -n 's/^\([^^~]\{1,\}\)\(\^0\)\{0,1\}$/\1/p'

Note the redirection of stderr to /dev/null - otherwise you'll get an error message saying:

fatal: cannot describe 'SOMESHA'"

EDIT: Fixed the regex in sed to support both lighweight and annotated/signed tags.

Solution 4 - Git

The best way to do this is to use the git describe command:

> git-describe - Show the most recent tag that is reachable from a commit > > The command finds the most recent tag that is reachable from a commit. 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.

Solution 5 - Git

You cannot determine if the current checkout “is a tag”. You can only determine if the current checkout was a commit that has tags.

The difference is: if there are several tags pointing to this commit, git can’t tell you which you used to checkout, nor if you actually used one to get there at all.

Jakub’s answer here based on git describe --exact-match (--tags) gives you “the first” of all the (annotated) tags.

And git describe sorts them like this:

  • annotated tags first
    • sorted youngest first
  • lightweight tags come afterwards
    • binary sorted by tag name (that means alphabetically if it’s English encoded in ASCII)
    • git doesn’t store meta-data with lightweight tags, so “youngest-first” can not be realized

Solution 6 - Git

Here is a brief shell script (tested in Bash, not confirmed if it works on ash, etc.). It will set the git_tag variable to the name of the currently checked-out tag, or leave it blank if the checkout is not tagged.

git_tag=''
this_commit=`git log --pretty=format:%H%n HEAD^..`

for tag in `git tag -l`; do
  tag_commit=`git log --pretty=format:%H%n tags/$tag^..tags/$tag`
  if [ "$this_commit" = "$tag_commit" ]; then
    # This is a tagged commit, so use the tag.
    git_tag="$tag"
  fi
done

Comment by Jakub Narębski:

This solution reduces to looping over all tags, and checking if they point to corrent commit, i.e. object pointed by HEAD. Using plumbing commands, i.e. commands meant for scripting, this can be written as:

this_commit=$(git rev-parse --verify HEAD)
git for-each-ref --format="%(*objectname) %(refname:short)" refs/tags/ |
while read tagged_object tagname
do
    if test "$this_commit" = "$tagged_object"
    then
        echo $tagname
    fi
done

This would print all tags that point to current commit.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJasonSmithView Question on Stackoverflow
Solution 1 - GitJakub NarębskiView Answer on Stackoverflow
Solution 2 - GitJasonSmithView Answer on Stackoverflow
Solution 3 - GitTuxdudeView Answer on Stackoverflow
Solution 4 - GitGreg HewgillView Answer on Stackoverflow
Solution 5 - GitRobert SiemerView Answer on Stackoverflow
Solution 6 - GitJasonSmithView Answer on Stackoverflow