By admin , 28 April 2026

Introduction

Tags are named references to specific commits. Unlike branches, tags are not meant to move. They are perfect for marking releases (v1.0.0) and other historical points.

Two kinds of tags

  • Lightweight: just a ref pointing at a commit. No metadata.
  • Annotated: a full Git object with tagger, date, message, and optional GPG signature. Use these for releases.

Creating tags

git tag v1.0.0                          # lightweight
git tag -a v1.0.0 -m "First stable release"   # annotated
git tag -s v1.0.0 -m "Signed release"         # GPG signed

By default tags attach to HEAD. To tag a specific commit:

git tag -a v0.9.0 a1b2c3d -m "Beta"

Listing and inspecting

git tag
git tag -l "v1.*"
git show v1.0.0
git tag -l --format='%(refname:short) %(taggerdate:short) %(subject)'

Pushing tags

Tags are not pushed by default:

git push origin v1.0.0
git push origin --tags                # all tags
git push --follow-tags                # only tags reachable from pushed commits

--follow-tags is the recommended default for release pipelines.

Deleting tags

git tag -d v1.0.0                     # local
git push origin --delete v1.0.0       # remote

Checking out a tag

git switch --detach v1.0.0
# or to base a hotfix branch on a tag
git switch -c hotfix/1.0.1 v1.0.0

Verifying signatures

git tag -v v1.0.0

Requires the tagger's public key in your GPG keyring.

Describing builds with git describe

Once your repo has annotated tags, git describe produces stable build identifiers that combine the latest tag, distance, and abbreviated SHA:

git describe
# v1.2.3-7-gabc1234
git describe --always --dirty
# v1.2.3-7-gabc1234-dirty

The --dirty suffix appears if the working tree has uncommitted changes; --always falls back to a bare SHA when no tag is reachable. CI pipelines often embed this string into binaries to make troubleshooting field reports trivial.

Tagging policies

For long-term-supported projects, decide and document:

  • Naming scheme: v1.2.3, release-2025-04, 1.2.3 (no prefix)?
  • Annotated or signed?
  • Who is allowed to push tags? Many hosts let you protect tag names with patterns.
  • What is the source of truth: tags-on-main only, or release branches with their own tags?
git for-each-ref --sort=-creatordate --format='%(refname:short) %(creatordate:short)' refs/tags | head

Pick a scheme and stick to it; tag history is harder to migrate than commit history.

Common mistakes

Using lightweight tags for releases. They lack a date, author, and message, which makes audit trails painful. Always use -a or -s for releases. Moving a tag after it is published; downstream users will not pick up the change cleanly. Create a new tag (e.g. v1.0.1) instead. Forgetting to push tags and wondering why your release page is empty; remember git push --tags or --follow-tags. Finally, naming tags with characters that conflict with refs (a tag and a branch named release will create ambiguity); use a clear convention like v<semver> for releases.