SamB
SamB

Reputation: 9224

How can I list all lightweight tags?

I want to list all of the lightweight tags in my repository; the best I can think of involves combining git for-each-ref, grep, and cut, but it seems like it'll be kind of fiddly...

(While we're at it, we might as well talk about the same thing for annotated tags: someone is sure to end up here wondering about that at some point.)

Edit:

By lightweight tags, I meant those tag refs that do not refer to tag objects. (In other words, unannotated tags.)

Upvotes: 28

Views: 7343

Answers (5)

CervEd
CervEd

Reputation: 4263

Using git for-each-ref and awk

Annotated Tags

git for-each-ref refs/tags |
  awk '$2~/tag/{print $3}'

Light-weight tags

git for-each-ref refs/tags |
  awk '$2~/commit/{print $3}'

First field is the hash, second the type, third is the name of the ref

Upvotes: 0

Christian Halstrick
Christian Halstrick

Reputation: 1

Be aware that you can tag (lightweight and annotated) blobs and trees also.

Therefore git for-each-ref refs/tags | grep -v commit will not only list annotated tags, but also tags to blobs and trees.

I suggest to use grep for the word 'tag' to find annotated tags:

git for-each-ref refs/tags | grep -w tag.

Don't forget -w, otherwise you will find the substring tag also for lightweight tags.

Upvotes: 0

learning2code
learning2code

Reputation: 728

To list only lightweight tags:

git for-each-ref refs/tags | grep commit

To list only annotated tags:

git for-each-ref refs/tags | grep -v commit

Explanation:

git for-each-ref lists all refs: heads, remotes, stash and tags

git for-each-ref refs/tags lists only tags refs.

| grep commit lists only those lines which contain the word commit. These are tags pointing to a commit and are thus lightweight tags.

| grep -v commit lists only those lines which do not contain the word commit. These are tags pointing to a tag and are thus annotated tags.

Upvotes: 13

John Lawrence Aspden
John Lawrence Aspden

Reputation: 43

Summarizing the other answer and its comments, if you're using bash:

function git-lightweight-tags() { 
    git for-each-ref refs/tags/ --format '%(objecttype) %(refname:short)' | awk '$1 == "commit" {print $2}' ; 
}

Upvotes: 1

torek
torek

Reputation: 489123

All the lightweight tags are in the refs/tags/ namespace and can be enumerated with, e.g.:

git for-each-ref --format '%(refname:short)' refs/tags/

or:

git show-ref --tags

As for annotated tags, well, the trick here—it affects the "lightweight" tags part too—is that an annotated tag is actually an object in the git repository, but, there's a lightweight tag that points to that object, that lets you get at the annotated tag by its tag name.1 So it's really a pair of things: a lightweight tag, plus the in-repo annotated tag object, that makes it "not a lightweight tag", except for that stubborn fact that it is a lightweight tag at the same time!

Thus, it boils down to: find all lightweight tags, then optionally select only tags pointing to commits or tags pointing to tag-objects depending on the behavior you want, then go on to emit the tag name.

There's a long example in the git-for-each-ref documentation of writing an entire script in the --format string and using eval to execute it (or you could pipe to sh to execute, at the cost of one extra process). I usually find it simpler to pipe the output of git for-each-ref into a while read ... loop:

git for-each-ref refs/tags/ --format '%(objecttype) %(refname:short)' |
    while read ty name; do [ $ty = commit ] && echo $name; done

which prints all lightweight-only tags.

Compare with:

git for-each-ref refs/tags/ --format '%(objecttype) %(refname:short)' |
    while read ty name; do [ $ty = tag ] && echo $name; done

which prints all annotated tags (or more precisely, "lightweight-that-are-annotated" tags).

Note that a tag can (conceivably—there's no actual use case for this right now, as far as I know) point to something other than a commit or a tag; it's up to you whether to do something with a tag pointing directly to a tree or blob.


1Without the lightweight tag, you would not be able to refer to annotated tag annotag using the name annotag—not without going through all the search effort that git fsck uses to find dangling objects, at least. Moreover, if you delete the lightweight tag, the annotated tag object may get garbage-collected. You can make one tag object point to another tag object to keep it in the repo (i.e., inhibit the gc) without an external name for the second tag object, as long as the first one has an external name. That's definitely an odd thing to do though.

Interestingly, the internal format for the annotated tag contains the external name, so one can use this technique to protect "old" annotated tags, hide them by removing their lightweight tags, and then later restore the original lightweight tag. Whether anyone can come up with a use for this, though... :-)

Upvotes: 31

Related Questions