1. Introduction

Git provides many features to support its main task of version tracking. One way to categorize commits into a secondary structure is Git tagging. Effectively, the use of tags enables a more human-readable name to be assigned as a ref (reference) to a commit instead of its commit identifier (ID).

In this tutorial, we explore ways to use the tag subcommand for listing and filtering tags. First, we briefly refresh our knowledge about a simple way to show all tags. After that, we turn to Git output formatting with the main example of tags. Finally, we show some advanced filtering when listing tags.

We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. Unless otherwise specified, it should work in most POSIX-compliant environments.

2. Tag Listing

The tag subcommand provides a way to –list all tags irrespective of their relationship to the commit tree:

$ git tag --list
antag
major
minor
xavier

This feature can be useful when working with automated scripts. In many cases, we can –ignore-case when working with sorting and filters and use –color as appropriate.

However, sometimes, we may want to show more information about tags or exclude some of them from the results. To that end, we employ the –list flag of the tag subcommand in many of the following general examples.

3. Git Output Formatting

Git provides many ways to format output that aren’t necessarily specific to a given object type. Here, we use tags for demonstration purposes, but the options apply to other objects, such as branches and commits as well.

3.1. Output Format

One of the most fundamental ways to augment any Git output is the –format option. It enables the specification of a format string, according to which the results are structured.

To understand the option better, let’s format the output of a tag list:

$ git tag --list --format='%(refname)'
refs/tags/tag1
refs/tags/tag10
refs/tags/tag11
refs/tags/tag12
refs/tags/tag13
[...]

Specifiers (or fields) are surrounded by () parentheses, prefixed by a % percent sign. In the context of a –format string, Git interpolates such expressions, expanding them to their respective result.

In this case, we use the refname specifier, which shows the full tag ref path. Further, refname supports modifiers in the form of suffixes:

  • :short gets the minimal non-ambiguous ref name
  • [l|r]strip=<N> strips a given number of characters on the left or right

There are other field names and modifiers:

+---------------------+--------------------------------------------------------------------------+
| Field (Specifier)   | Description                                                              |
+---------------------+--------------------------------------------------------------------------+
| refname             | name of ref as path                                                      |
|                     | :short, :[l|r]strip=<N>                                                  |
+---------------------+--------------------------------------------------------------------------+
| objecttype          | object type such as blob, tree, commit, tag                              |
+---------------------+--------------------------------------------------------------------------+
| objectsize          | object size                                                              |
|                     | :disk                                                                    |
+---------------------+--------------------------------------------------------------------------+
| objectname          | object name (SHA-1)                                                      |
|                     | :short                                                                   |
+---------------------+--------------------------------------------------------------------------+
| deltabase           | if stored as delta, the delta base, else null object                     |
+---------------------+--------------------------------------------------------------------------+
| upstream            | show upstream of ref                                                     |
|                     | :short, :[l|r]strip=<N>, :trackshort, :track, :remotename, :remoteref    |
+---------------------+--------------------------------------------------------------------------+
| push                | local ref that is push for displayed ref                                 |
|                     | :short, :[l|r]strip=<N>, :trackshort, :track, :remotename, :remoteref    |
+---------------------+--------------------------------------------------------------------------+
| HEAD                | * if HEAD is current ref                                                 |
+---------------------+--------------------------------------------------------------------------+
| color               | change color                                                             |
|                     | :<COLOR_NAME>                                                            |
+---------------------+--------------------------------------------------------------------------+
| align               | align text                                                               |
|                     | :width, :position                                                        |
+---------------------+--------------------------------------------------------------------------+
| if, then, else, end | conditionals                                                             |
|                     | :<CONDITION>                                                             |
+---------------------+--------------------------------------------------------------------------+
| symref              | resolve symbolic ref                                                     |
|                     | :short, :[l|r]strip=<N>                                                  |
+---------------------+--------------------------------------------------------------------------+
| signature           | signature                                                                |
|                     | :grade, :signer, :key, :fingerprint, :primarykeyfingerprint, :trustlevel |
+---------------------+--------------------------------------------------------------------------+
| worktreepath        | absolute path to worktree of checked-out ref                             |
+---------------------+--------------------------------------------------------------------------+
| ahead-behind        | commit distance to given commit                                          |
|                     | :<COMMIT>                                                                |
+---------------------+--------------------------------------------------------------------------+
| describe            | general details                                                          |
|                     | :tags, :abbrev, :match, :exclude                                         |
+---------------------+--------------------------------------------------------------------------+
| raw:size            | raw data size                                                            |
+---------------------+--------------------------------------------------------------------------+
| contents            | message details                                                          |
|                     | :size, :subject, :body, :signature, :lines=<N>                           |
+---------------------+--------------------------------------------------------------------------+

Notably, some fields are special operator specifiers such as if, align, and color.

3.2. –column View

Many Git commands employ the –column option for formatting their output as columns instead of lists.

Let’s demonstrate with the tag subcommand.

For example, we can get the list of tags filling column by column, which is the default in most cases:

$ git tag --list --column=column
tag1   tag14  tag19  tag23  tag28  tag32  tag37  tag41  tag46  tag50  tag55  tag6   tag64  tag9
tag10  tag15  tag2   tag24  tag29  tag33  tag38  tag42  tag47  tag51  tag56  tag60  tag65
tag11  tag16  tag20  tag25  tag3   tag34  tag39  tag43  tag48  tag52  tag57  tag61  tag66
tag12  tag17  tag21  tag26  tag30  tag35  tag4   tag44  tag49  tag53  tag58  tag62  tag7
tag13  tag18  tag22  tag27  tag31  tag36  tag40  tag45  tag5   tag54  tag59  tag63  tag8

Alternatively, we can fill going [row]s-first:

$ git tag --list --column=row
tag1   tag10  tag11  tag12  tag13  tag14  tag15  tag16  tag17  tag18  tag19  tag2   tag20  tag21
tag22  tag23  tag24  tag25  tag26  tag27  tag28  tag29  tag3   tag30  tag31  tag32  tag33  tag34
tag35  tag36  tag37  tag38  tag39  tag4   tag40  tag41  tag42  tag43  tag44  tag45  tag46  tag47
tag48  tag49  tag5   tag50  tag51  tag52  tag53  tag54  tag55  tag56  tag57  tag58  tag59  tag6
tag60  tag61  tag62  tag63  tag64  tag65  tag66  tag7   tag8   tag9

Other options dictate when to use columns, provide ways to make the output more –dense, and others.

3.3. –sort Names

Since automated processing may require a certain order, some Git commands make use of the –sort option to augment their output respectively.

To demonstrate, let’s check the default tag order:

$ git tag --list
tag1
tag10
tag11
tag12
tag13
[...]

This is a basic ascending alphanumeric sort. Adding a prefix reverses the order.

Now, we can employ a –sort by author:

$ git tag --list --sort=author
tag1
tag3
tag7
tag9
tag11
[...]

This way, we get the tags in groups by the author key. Internally, each group uses alphanumeric sorting.

Notably, other keys we can use are committerdate and even v:refname:

$ git tag --list --sort=v:refname
tag1
tag2
tag3
tag4
tag5

To further sort by several keys, we can specify –sort multiple times.

4. Tag Filtering

In addition to the regular output formatting options, the tag subcommand provides tag-specific filters.

4.1. Tag –points-at Object

Although we can use log and other subcommands for the purpose, the –points-at option provides a more concise way of getting the objects that a tag points to.

Let’s see it in action:

$ git tag --list --points-at 1add4face0666a430100e7737dd1a56c8914cdd0
tag1

In this case, we see only tag1 points at commmit 1add4face0666a430100e7737dd1a56c8914cdd0.

4.2. Tag –contains or –no-contains Commit

While we can see which tags a commit has, we might also want to list only tags that relate to a given commit in a certain way.

For instance, to get all tags that are reachable from a given commit, we use –contains:

$ git tag --list --contains 1add4face0666a430100e7737dd1a56c8914cdd0
tag1
tag11
tag21
tag31
tag41

Similarly, we can exclude a given commit via –no-contains:

$ git tag --list --no-contains 1add4face0666a430100e7737dd1a56c8914cdd0
tag12
tag13
tag14
tag15
tag16
[...]

In fact, we can omit the –list flag since both of the above options imply it.

4.3. View –merged and –no-merged Tags

When merging commits, one of them might have a tag association. Merged tags are the outcome of commit merges, where the tag remains on the new commit after the merge.

For example, if we merge a secondary branch into the main or master branch, the tags from that branch should remain.

So, –merged only shows tags that were part of a merge, while –no-merged excludes such tags.

Notably, a rebase rewrites history, so tags on any squashed or omitted commits are lost.

5. Summary

In this article, we talked about Git formatted output and filtering with the main example of tags.

In conclusion, knowing how to display tags in a structured manner based on criteria can be helpful when performing simple checks, debugging, and automating tasks.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.