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.
Git · Command
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
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.
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>...| Name | Type | Description |
|---|---|---|
<tagname> | string | The tag to create, delete or describe. It must pass the git check-ref-format rules. |
<commit> | <object> | object reference | What the new tag points at, usually a commit. Defaults to HEAD. |
-a / --annotate | flag | Creates an unsigned annotated tag object. |
-s / --sign, -u / --local-user=<key-id> | flag / key id | Creates a signed tag, using either the default signing key or the key you name. |
-m <msg> / -F <file> | string / path | Supplies the tag message inline or from a file (use - to read from stdin). Either one implies -a. |
-f / --force | flag | Replaces an existing tag that has the same name instead of failing. |
-d / --delete | flag | Deletes the named tags. |
-v / --verify | flag | Verifies the cryptographic signature of the named tags. |
-l / --list [<pattern>...] | flag / glob | Lists tags, optionally only those matching shell-wildcard patterns. |
-n<num> | integer | Prints up to <num> lines of each tag's annotation when listing. |
--sort=<key> | string | Sorts the listing by a for-each-ref key. Prefix the key with - for descending order. |
--contains / --no-contains / --merged / --no-merged / --points-at | commit/object | Filters the listing by containment, reachability or the object a tag points at. |
# 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.0The script creates an annotated release tag and a lightweight tag, lists and filters tags, deletes the lightweight one, and pushes the release tag.
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.
While triaging a bug, run git tag --contains <fix-sha> to see which shipped versions already include the patch.
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.
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.
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.
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 describegit for-each-refgit check-ref-formatgit configgit fetchgit rev-parsegit interpret-trailersgit push