Skip to content
masterclass Buddy

Git · Command

git tag

git tag creates, lists, deletes and verifies tags. Tags are names stored under refs/tags/ that mark a specific object, usually a commit such as a release point.

Last updated

How it works

When you give it a name, git tag adds a reference under refs/tags/ that points at the target object, which is HEAD unless you name another one. If you pass -a, -s or -u, Git first writes a tag object that holds the tagger, the date, a message and an optional signature, and the ref points at that object. Passing -m, -F or --trailer on its own also gives you an annotated tag. Without any of those flags you get a lightweight tag, which is a ref that points straight at the commit. The -d, -l and -v flags switch the command into delete, list or verify mode. Listing is the default when you pass no arguments.

Syntax

git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e] [--trailer <token>[(=|:)<value>]] <tagname> [<commit> | <object>]
git tag -d <tagname>...
git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>] [--points-at <object>] [--sort=<key>] [--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]
git tag -v [--format=<format>] <tagname>...

Parameters

NameTypeDescription
<tagname>stringThe tag to create, delete or describe. It must pass the git check-ref-format rules.
<commit> | <object>object referenceWhat the new tag points at, usually a commit. Defaults to HEAD.
-a / --annotateflagCreates an unsigned annotated tag object.
-s / --sign, -u / --local-user=<key-id>flag / key idCreates a signed tag, using either the default signing key or the key you name.
-m <msg> / -F <file>string / pathSupplies the tag message inline or from a file (use - to read from stdin). Either one implies -a.
-f / --forceflagReplaces an existing tag that has the same name instead of failing.
-d / --deleteflagDeletes the named tags.
-v / --verifyflagVerifies the cryptographic signature of the named tags.
-l / --list [<pattern>...]flag / globLists tags, optionally only those matching shell-wildcard patterns.
-n<num>integerPrints up to <num> lines of each tag's annotation when listing.
--sort=<key>stringSorts the listing by a for-each-ref key. Prefix the key with - for descending order.
--contains / --no-contains / --merged / --no-merged / --points-atcommit/objectFilters the listing by containment, reachability or the object a tag points at.

Key points

  • An annotated tag stores extra metadata: who created it, when, a message and possibly a signature. A lightweight tag is just a name for an object.
  • Use annotated tags for releases you publish, and lightweight tags for private or throwaway bookmarks.
  • Listing patterns are shell globs, and you can pass several. A tag is shown if it matches any one of them.
  • The version:refname (or v:refname) sort key orders tags like version numbers, so v1.10 comes after v1.9. Plain lexicographic order would put it first.
  • If you leave out the target, the tag goes on HEAD. To tag an older commit, add its hash or another revision name after the tag name.
  • Signing works with several backends, not only GPG. The gpg.format setting picks the backend.
  • When you combine several --contains filters, a tag only has to contain at least one of the listed commits to be shown.
  • Tag names follow the ref-name rules, so some characters are not allowed.

Common mistakes

  • git describe and some other commands that name commits skip lightweight tags by default. A release marked only with a lightweight tag may not show up in their output.
  • Moving a tag that has already been pushed does not update other people's copies. They keep the old target unless they delete the tag and fetch it again.
  • If tag creation fails, the message you typed in the editor is kept in TAG_EDITMSG, but the next git tag run can overwrite it.
  • With tag.gpgSign turned on, a script can end up signing many tags, so use an agent to avoid typing your passphrase over and over.

Example: Tag a release, inspect and clean up

# Annotated release tag on the current commit
git tag -a v1.4.0 -m "Release 1.4.0"

# Lightweight bookmark on an older commit
git tag before-refactor 9fceb02

# List v1.* tags in version order, newest first, with first message line
git tag -n -l 'v1.*' --sort=-version:refname

# Which tags already contain a given fix?
git tag --contains a1b2c3d

# Remove the temporary bookmark
git tag -d before-refactor

# Publish the release tag
git push origin v1.4.0

The script creates an annotated release tag and a lightweight tag, lists and filters tags, deletes the lightweight one, and pushes the release tag.

When to use it

Marking releases

A CI pipeline creates a signed annotated tag such as v2.3.0 on the release commit, so builds and changelogs can refer to an exact, verifiable point in history.

Finding which releases include a fix

While triaging a bug, run git tag --contains <fix-sha> to see which shipped versions already include the patch.

Temporary bookmarks

Before a risky rebase, a developer drops a lightweight tag on the current commit so they can easily get back to it, then deletes it with -d.

Interview questions

QWhat is the difference between a lightweight and an annotated tag, and how does git tag decide which one to create?

An annotated tag is a real tag object holding the tagger, the date, a message and optionally a signature, and the ref points at that object. A lightweight tag is a ref under refs/tags/ that points straight at the target. git tag makes an annotated tag when you pass -a, -s or -u, or when you pass -m, -F or --trailer without those flags. Otherwise it makes a lightweight tag.

Follow-up: Why might git describe print nothing useful in a repo that only has lightweight tags? Because describe skips lightweight tags by default, which reflects the view that annotated tags are the ones meant for releases.

QYou tagged the wrong commit as v1.0. How do you fix it?

If you haven't pushed it, re-run git tag -f v1.0 <correct-commit> to replace it. If it has already been published, the docs say the sensible choice is a new name such as v1.0.1, because Git deliberately won't change tags other people already have.

Follow-up: If you do force-move a published tag, what do other people have to do? Each person deletes their local copy with git tag -d v1.0 and fetches it again with git fetch origin tag v1.0, then checks it with git rev-parse.

QHow do you list only the v2 tags, sorted as versions, with their first message line?

Run git tag -n -l 'v2*' --sort=version:refname. -l takes shell-glob patterns, -n with no number prints the first line of each annotation, and version:refname sorts names as version numbers. Adding - in front of the key reverses the order.

Follow-up: What does -n show for a lightweight tag that has no annotation? It shows the commit message of the tagged commit instead.

  • git describe
  • git for-each-ref
  • git check-ref-format
  • git config
  • git fetch
  • git rev-parse
  • git interpret-trailers
  • git push

Source: Git - git-tag Documentation. Checked 2026-11-02.