Git Tags and Releases
Branches move — they are pointers that advance as new commits land. Tags, in contrast, are meant to be fixed references to a single, specific commit, typically marking a meaningful point in history such as a software release. Once you tag commit a1b2c3d as v2.4.0, that name will always refer to exactly that commit (tags aren't supposed to move, though Git technically allows force-moving one). This makes tags the natural mechanism for versioning: they give humans and tools a stable, memorable name for 'the exact code that shipped in release 2.4.0,' which is essential for reproducing bugs, rolling back deployments, or auditing what was live at a given time.
Cricket analogy: A team's league table position moves every week as matches are played, but the record for 'who won the 2011 World Cup Final' is fixed forever — tags are like that permanent trophy record, unlike a constantly advancing points table.
Lightweight vs annotated tags
Git supports two kinds of tags. A lightweight tag, created with git tag v1.0.1, is nothing more than a named pointer to a commit — essentially a branch that never moves, with no extra metadata. An annotated tag, created with git tag -a v1.0.1 -m "Patch release: fix session timeout", is a full Git object with its own SHA, storing the tagger's name and email, the date, a message, and optionally a GPG signature (via -s). Annotated tags are the recommended default for anything user-facing like releases, because the metadata and optional signature provide provenance and integrity guarantees that a lightweight tag cannot.
Cricket analogy: A quick scribbled note in the scorebook margin naming a match 'the Miracle at Headingley' is like a lightweight tag — just a label — while an official match report with umpire signatures, weather conditions, and commentary is like an annotated tag, carrying real provenance.
# Create an annotated tag for a release
$ git tag -a v2.4.0 -m "Release 2.4.0: add rate limiting, fix session bug"
# Inspect it — note it has its own object, author, and message
$ git show v2.4.0
tag v2.4.0
Tagger: Priya Nair <priya@example.com>
Date: Thu Jul 9 10:12:00 2026 -0700
Release 2.4.0: add rate limiting, fix session bug
commit a1b2c3d4e5f6...
Author: Priya Nair <priya@example.com>
...
# Tags are NOT pushed by default — push them explicitly
$ git push origin v2.4.0
# or push everything, including all local tags
$ git push origin --tags
# Tag a past commit retroactively
$ git tag -a v2.3.1 9f8e7d6 -m "Hotfix release for v2.3.0"
# List and filter tags
$ git tag --list "v2.4.*"
v2.4.0
# Check out a tag (detached HEAD — you're inspecting, not on a branch)
$ git checkout v2.4.0Semantic versioning and release workflows
Most teams pair tags with semantic versioning (MAJOR.MINOR.PATCH), where MAJOR increments for breaking changes, MINOR for backward-compatible features, and PATCH for backward-compatible bug fixes. On platforms like GitHub, pushing a tag such as v2.4.0 can trigger a CI/CD release pipeline and can be attached to a 'Release' — a richer object with generated changelogs, binaries, and release notes built on top of the tag. Because tags are lightweight references to immutable commits, they also make excellent deployment markers: a rollback becomes as simple as redeploying the artifact built from a known-good tag.
Cricket analogy: Just as a bilateral series is labeled by year and edition (Ashes 2023) so fans always know exactly which contest is meant, pushing a version tag like v2.4.0 can trigger the ground crew's post-match highlight-reel pipeline and gets bundled with full match stats as a 'release'.
An annotated tag is stored as its own object type in Git's object database (alongside blobs, trees, and commits) — running git cat-file -t v2.4.0 returns 'tag', not 'commit', which is why git show on an annotated tag prints tagger metadata before the commit it points to.
Tags are not automatically pushed with git push — you must use git push origin <tag> or git push --tags. Also avoid moving or re-tagging a version that has already been published; if you must fix it, use git tag -f and force-push carefully, but be aware anyone who already fetched the old tag now has a diverging reference, which can cause confusing, hard-to-diagnose inconsistencies.
- Tags mark a specific, (ideally) immutable commit — commonly used for release versions.
- Lightweight tags are simple pointers; annotated tags (-a) store tagger, date, message, and can be GPG-signed (-s).
- Tags are not pushed by default; use git push origin <tag> or git push --tags.
- Semantic versioning (MAJOR.MINOR.PATCH) is the common convention paired with tags.
- Checking out a tag puts you in detached HEAD state — create a branch if you intend to commit from there.
- Re-tagging an already-published version can cause confusing divergence across clones; avoid it.
Practice what you learned
1. What is the key difference between a lightweight tag and an annotated tag?
2. By default, what happens to tags when you run `git push`?
3. What state does `git checkout v2.4.0` put you in?
4. Which command creates an annotated, GPG-signed tag?
5. Why is re-tagging an already-published version considered risky?
Was this page helpful?
You May Also Like
git revert vs git reset
Two different ways to undo changes in Git — revert creates a new commit that inverses a prior one, while reset moves the branch pointer and rewrites history — and when to use each safely.
Commit Message Conventions
Why disciplined, structured commit messages make history searchable and automatable, and how conventions like Conventional Commits standardize their format.
Branch Protection and CI Checks
Explore how branch protection rules and required CI status checks stop broken or unreviewed code from reaching important branches like main.
Git Flow and Trunk-Based Development
Two contrasting branching strategies for organizing a team's work — Git Flow's structured long-lived branches versus trunk-based development's short-lived branches off a single trunk.
Related Reading
Related Study Notes in Software Engineering
Browse all study notesMicroservices Study Notes
Software Architecture · 30 topics
Software EngineeringTesting & TDD Study Notes
Software Testing · 30 topics
Software EngineeringDesign Patterns Study Notes
Software Design · 30 topics
Software EngineeringSoftware Engineering Study Notes
Python · 40 topics
Software EngineeringSystem Design Study Notes
Architecture · 40 topics