Topic 6.3
Tags & Releases: SemVer, Annotated vs Lightweight
In one line
A tag is a permanent, human-friendly label pointing at one specific commit — genuinely different from a branch because it never moves — and the standard way to mark a real, shippable release.
Think of it like this
A branch is a bookmark that moves forward as you keep reading; a tag is a sticky note marking one specific, permanent page you want to be able to find again instantly, forever, exactly as it was. A tag never moves once created — it points at exactly one commit, permanently.
Key ideas
- 01
git tag v1.0.0creates a LIGHTWEIGHT tag — just a name pointing directly at your current commit, with no additional metadata at all, functionally similar to a branch that simply never advances.git tag -a v1.0.0 -m "First stable release"creates an ANNOTATED tag instead — a full Git object with its own message, tagger name, and date, genuinely recommended for anything meant to represent a real release. - 02
SEMANTIC VERSIONING (SemVer) is the near-universal convention for version numbers:
MAJOR.MINOR.PATCH(e.g.2.4.1) — MAJOR increments for breaking changes, MINOR for new, backward-compatible functionality, and PATCH for backward-compatible bug fixes. Following this convention lets anyone depending on your project understand, from the version number alone, roughly how risky an upgrade is likely to be. - 03
Tags, unlike branches, are NOT pushed to a remote by default —
git push <remote> <tagname>pushes one specific tag, andgit push <remote> --tagspushes every tag at once. This is a genuinely common early surprise: creating a tag locally and assuming it's automatically visible to collaborators or a hosting platform's 'Releases' page, when it actually needs its own explicit push. - 04
git checkout <tagname>lets you inspect a project's exact state at that specific tagged release — genuinely useful for reproducing a bug reported against a specific old version, or comparing behavior between two release points, without any ambiguity about exactly which commit you're looking at. - 05
GitHub/GitLab RELEASES are a platform feature built directly on top of Git tags — creating a 'Release' on the platform is typically just attaching release notes, and sometimes compiled binary artifacts, to an underlying tag. The tag itself is the real, portable, core-Git concept; the platform's release page is a convenience layer on top of it.
Code & diagrams
Annotated tags, SemVer, and the push step people forget.
cd ~/scratch/my-first-repo
# A lightweight tag — just a name, no extra metadata
git tag v0.1.0-lightweight
# An annotated tag — the recommended form for real releases
git tag -a v1.0.0 -m "First stable release"
# See the difference
git show v0.1.0-lightweight # shows the commit directly
git show v1.0.0 # shows the TAG object first: message, tagger, date — then the commit
# List every tag
git tag
# Tags are NOT pushed automatically — this is easy to forget
git push origin v1.0.0
# or, to push every tag at once:
git push origin --tags
# Inspect the exact state of a specific released version
git checkout v1.0.0
# (detached HEAD — you're viewing history, not on a branch; git switch back to a branch when done)
git switch main
# Delete a tag if it was created by mistake
git tag -d v0.1.0-lightweight # locally
git push origin --delete v0.1.0-lightweight # and on the remote, if it was pushedExplain it without notes
What's the practical difference between a lightweight tag and an annotated tag, and which should you use for a real release?
You created a tag locally and told a teammate to pull it, but they can't find it. What's the most likely explanation?
Practice
Create both a lightweight and an annotated tag on the same commit, then use git show on each to see the concrete difference in what information is displayed.
Create a tag, confirm with git tag that it exists locally, then push it explicitly and confirm (via a second clone, or a hosting platform's UI) that it's now visible remotely.
Trade-offs
- ↔
Lightweight tags are quick and require no extra thought, which is fine for personal, throwaway markers — but for anything representing a real, shippable release that others depend on, the small extra step of an annotated tag (a real message, a real author record) is worth it for the same reason a real commit message is worth it: it's a permanent record future-you (or a teammate) will be genuinely glad exists.
Done when you can
I can create both lightweight and annotated tags and explain the difference.
I understand Semantic Versioning's MAJOR.MINOR.PATCH convention.
I know tags must be explicitly pushed and aren't shared automatically like regular commits.