105

I have a repository which has multiple tags on the same commit. For example:

commit #3 <--- TAG1 / TAG2 / TAG3

  |

commit #2 <--- TAG4/ TAG5

  |

commit #1 <--- TAG6/ TAG7

I'd like to find out what tags are on a particular commit. For example, if I check commit 1, I'd like to get tag 6 and tag 7.

I have tried:

git checkout <commit 1> 
git tag --contains

which displayed tags 1-7.

git checkout <commit 1>
git describe --tags HEAD

displayed tag 6 only.

What is the proper way to do this in Git?

9 Answers 9

220

For completion (thanks to Ciro Santili answer), git tag has got the option --points-at that does exactly what OP is asking.

git tag --points-at HEAD

It does not have the effect of also listing the tags put on forward commits (as Jonathan Hartley stated in his comment regarding git tag --contains).

8
  • 2
    This is the best answer here. No complicated command line needed, but gives just the tag name.
    – Jason R
    Commented Sep 20, 2016 at 17:16
  • 11
    From git help tag: --points-at <object> – Only list tags of the given object (HEAD if not specified). Implies --list.. So -l and HEAD can be omitted.
    – fphilipe
    Commented Aug 17, 2018 at 8:28
  • 1
    @fphilipe Thanks. I suggested an edit without the -l. Removing HEAD would make the command ambiguous.
    – Aalex Gabi
    Commented Feb 20, 2020 at 13:15
  • 1
    @friederbluemle tag of another tag, rather than of a commit itself; it's subtly different. See here.
    – ijoseph
    Commented Sep 16, 2020 at 19:36
  • 1
    @AalexGabi I understand it's an explicit reference. However there is nothing ambiguous about omitting it. Like many other commands (e.g. git branch, git reset, etc), it defaults to HEAD. The comment about ambiguity had me double check the man page if there was something unexpected with the tag --contains command. It turned out there isn't, so HEAD is not required, and in most scenarios does not need to be specified. Commented Oct 13, 2020 at 6:55
28

I guess maybe git has had some options added since this question was asked, but since it still comes in pretty high on google, I thought I'd add that this way works nicely:

git tag -l --contains HEAD

Or replace HEAD with any other valid commit reference you like.

This will print a newline separated list of tags if the HEAD contains any tags, and print nothing otherwise, so you would get:

TAG6
TAG7

And of course there are lots of nice ways with various other shell tools that you can format that output once you have it...

3
  • 1
    Works perfectly. Just why do you need -l? From the manual it seems that -l without args lists all tags, which is the same as tag without arguments. Commented Jul 3, 2013 at 10:09
  • @cirosantilli oops, I guess I didn't read the manual carefully enough. Though I think I like adding the -l because it makes sure I don't end up adding a new tag if I forget the --contains flag.
    – arcticmac
    Commented Jul 26, 2013 at 20:28
  • 9
    This gives the wrong output. It shows all tags that 'contain' the given commit, i.e. tags applied to previous reachable commits are also included. The original question explicitly says he doesn't want these old tags, only the 'current' tags ON the given commit. Commented Apr 23, 2014 at 8:58
18

Some improvements on William's answer:

git config --global alias.tags 'log -n1 --pretty=format:%h%d'

The output looks like this:

~$ git tags
7e5eb8f (HEAD, origin/next, origin/master, origin/HEAD, master)
~$ git tags HEAD~6
e923eae (tag: v1.7.0)
2
  • I'm trying to add sed/tr/grep commands to convert this output into a plain list with no punctuation. I can construct such a 'git log' command directly, but the escaping is killing me when I put it into a git config alias. Here's my log command: git log -n1 --pretty="format:%d" | sed "s/, /\n/g" | grep tag | sed "s/tag: \|)//g" Commented Apr 17, 2014 at 14:12
  • I've refined this to make it more useful to automated scripts (e.g putting the tags into your prompt), in an answer below... Commented Apr 23, 2014 at 8:55
12

git tag --points-at

--points-at

Only list tags of the given object (HEAD if not specified). Implies --list.

from https://git-scm.com/docs/git-tag

6

This is not ideal, but perhaps helpful:

$ git log -n 1 --decorate --pretty=oneline

You could play around with the format to get exactly what you want.

2
  • Thanks for your info , my repo has more than 300 different gits. Press "Q" for 300 time really hard for me .
    – Shawn
    Commented Feb 24, 2010 at 8:07
  • Whenever I using git log to display anything , I need press q button to exit the log display mode . Not sure if you facing the same
    – Shawn
    Commented Feb 25, 2010 at 6:40
5

This displays the commit id of HEAD, as well as any branches or any tags that also happen to be exactly at HEAD.

git reflog --decorate -1

Sample output:

484c27b (HEAD, tag: deployment-2014-07-30-2359, master, origin/master) HEAD@{0}: 484c27b878ca5ab45185267f4a6b56f8f8d39892: updating HEAD
4

Here's a refinement of @JoshLee's answer, which manipulates the output to list only tags (not branches, nor HEAD) and strips the word 'tag:' and decorative punctuation. This is useful if you are scripting something up which needs to find the current tags (e.g. put them in your prompt):

git log -n1 --pretty="format:%d" | sed "s/, /\n/g" | grep tag: | sed "s/tag: \|)//g"

Example output:

$ git log -n 1 --decorate=short
commit a9313...c7f2 (HEAD, tag: v1.0.1, tag: uat, mybranch)
...
$ git log -n1 --pretty="format:%d" | sed "s/, /\n/g" | grep tag: | sed "s/tag: \|)//g"
v1.0.1
uat
1
  • 1
    Thanks for the answer! I had to modify the last sed command because it was not stripping the 'tag: ' string for me, maybe because my tags have levels, like internal/variant/version. If anyone has the same issue, here's the modified script: git log -n1 --pretty="format:%d" | sed "s/, /\n/g" | grep tag: | sed 's/tag: //' Commented Jan 25, 2023 at 23:53
3

If you are using git tag --points-at HEAD, make sure to use Git 2.42 (Q3 2023).

"git tag --list --points-at X"(man) showed tags that directly refers to object X, but did not list a tag that points at such a tag, which has been corrected with Git 2.42 (Q3 2023).

See commit d9e0062, commit 870eb53, commit b9584c5 (02 Jul 2023) by Jeff King (peff).
See commit 468887f (01 Jul 2023) by Jan Klötzke (jkloetzke).
(Merged by Junio C Hamano -- gitster -- in commit 5929e66, 25 Jul 2023)

ref-filter: handle nested tags in --points-at option

Signed-off-by: Jan Klötzke

Tags are dereferenced until reaching a different object type to handle nested tags, e.g. on checkout.
In contrast, "git tag --points-at=..."(man) fails to list such nested tags because only one level of indirection is obtained in filter_refs().

Implement the recursive dereferencing for the "--points-at" option when filtering refs to unify the behaviour.

That will list nested tag, and that will do it faster:

ref-filter: avoid parsing tagged objects in match_points_at()

Signed-off-by: Jeff King

When we peel tags to check if they match a --points-at oid, we recursively parse the tagged object to see if it is also a tag.
But since the tag itself tells us the type of the object it points to (and even gives us the appropriate object struct via its "tagged" member), we can use that directly.

We do still have to make sure to call parse_tag() before looking at each tag.
This is redundant for the outermost tag (since we did call parse_object() to find its type), but that's OK; parse_tag() is smart enough to make this a noop when the tag has already been parsed.

In my clone of linux.git, with 782 tags (and only 3 non-tags), this yields a significant speedup (bringing us back where we were before the commit before this one started recursively dereferencing tags):

Benchmark 1: ./git.old for-each-ref --points-at=HEAD --format="%(refname)"
  Time (mean ± σ):      20.3 ms ±   0.5 ms    [User: 11.1 ms, System: 9.1 ms]
  Range (min … max):    19.6 ms …  21.5 ms    141 runs

Benchmark 2: ./git.new for-each-ref --points-at=HEAD --format="%(refname)"
  Time (mean ± σ):      11.4 ms ±   0.2 ms    [User: 6.3 ms, System: 5.0 ms]
  Range (min … max):    11.0 ms …  12.2 ms    250 runs

Summary
  './git.new for-each-ref --points-at=HEAD --format="%(refname)"' ran
    1.79 ± 0.05 times faster than './git.old for-each-ref --points-at=HEAD --format="%(refname)"'
1

I'm doing git tag -l | grep $(git describe HEAD) it returns the tag of the latest commit, or nothing if the last commit isn't tagged

Not the answer you're looking for? Browse other questions tagged or ask your own question.