C.W.K.
Stream
Lesson 05 of 05 · published

Tags and Release History

~16 min · tag, release

Level 0Untracked Rookie
0 XP0/47 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

Tags pin moments; releases are tags with metadata

Branches move; tags do not. A tag is a name for one specific commit, and that pointer never changes once set. The use case is releases: tag v2.4.0 at the commit you shipped, and forever after, anyone can git checkout v2.4.0 and stand exactly where production stood at that release. The same is true for any moment worth recovering: a customer reproduces a bug "in v2.3.7" and you can put yourself there in seconds.

Two tag types matter. Lightweight tags (git tag v2.4.0) are just pointer files — a name for a commit, no metadata. Annotated tags (git tag -a v2.4.0 -m "Release 2.4.0") are full Git objects with author, date, message, and (optionally) GPG signature. For real releases, always use annotated tags. The signature confirms the release came from an authorized human; the message becomes part of the audit trail; git describe uses annotated tags to produce human-readable commit IDs like v2.4.0-12-g3f4a5d6.

git push --tags sends tags to the remote; many people are surprised by this — plain git push does NOT include tags. Configure git config --global push.followTags true to make tags follow normal pushes when they point at commits being pushed. git tag -d <name> deletes a local tag; git push origin --delete <name> deletes the remote tag. Be careful: tag deletion on a remote is rarely correct because anyone who already pulled the tag still has it.

GitHub's "Releases" feature is annotated tags plus a UI: release notes, downloadable assets (tarballs, binaries), pre-release flags. gh release create v2.4.0 --notes "..." from the CLI, or push a tag and use the web UI. Combined with semantic-release tooling and Conventional Commits, releases become an automatic side effect of merging — the team never writes release notes by hand again.

Code

Annotated tag for a release·bash
# Annotated, signed tag at HEAD:
git tag -a v2.4.0 -m "Release 2.4.0 — OAuth2 GitHub login" -s

# At a specific commit:
git tag -a v2.4.0 abc1234 -m "..."

# List tags:
git tag --list 'v2.*'             # filtered by glob

# See the tag's metadata:
git show v2.4.0

# Push tags (does NOT happen with plain git push):
git push origin v2.4.0
git push --tags                   # all tags

# Make tags follow regular pushes when they point at the same tip:
git config --global push.followTags true
GitHub Releases via gh·bash
# Create a tag + GitHub release in one go:
gh release create v2.4.0 \
  --title "v2.4.0 — OAuth2 GitHub login" \
  --notes-file CHANGELOG-2.4.0.md \
  --target main

# Attach a built artifact:
gh release upload v2.4.0 dist/myapp-2.4.0.tar.gz

# Mark as pre-release:
gh release create v2.5.0-beta.1 --prerelease --generate-notes

# git describe — most recent tag plus offset:
git describe --tags
# Output like: v2.4.0-12-g3f4a5d6
# meaning 12 commits past v2.4.0, current short hash 3f4a5d6

External links

Exercise

On a real repo, create one annotated tag at HEAD with a meaningful message. Push it with git push origin <tag>. Confirm GitHub shows it under Releases or Tags. Then run git describe --tags and read the output format. Add push.followTags = true globally. Note in writing one moment in your project's history you would tag retroactively if you could.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.