100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

Git Tags and Releases

Understand how git tags mark specific commits as meaningful milestones like versions, and how lightweight vs annotated tags support release workflows and reproducible deployments.

Advanced GitBeginner8 min readJul 9, 2026
Analogies

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.

bash
# 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.0

Semantic 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

Was this page helpful?

Topics covered

#Bash#GitVersionControlStudyNotes#SoftwareEngineering#GitTagsAndReleases#Git#Tags#Releases#Lightweight#StudyNotes#SkillVeris