Chapter 8: Git Tagging
Git Tagging — one of those features that feels “advanced” at first but is actually super simple and extremely useful once you start releasing real projects.
Think of tags as permanent sticky notes or bookmarks you glue directly onto a specific commit in your history. Unlike branches (which move forward when you add new commits), tags stay exactly where you put them — forever pointing to that exact moment in time.
Why do we even need Git tags? (Real-world reasons)
- Mark official releases → “This commit is v1.0.0 — the version we shipped to users”
- Create stable points for documentation, deployment, changelogs, Docker images, npm publish, etc.
- Make it easy to go back → git checkout v2.3.1 instead of remembering commit hash a1b2c3d…
- Trigger CI/CD pipelines → many tools (GitHub Actions, GitLab CI, Jenkins) auto-deploy or build when a new tag is pushed
- Semantic versioning (semver) → almost every serious project in 2026 uses tags like v1.2.3, v2.0.0-beta.1
Without tags, people end up doing silly things like:
- Creating a release-v1.0 branch (bad — branches should move)
- Copy-pasting folders named project-final-2026-02-13 (nightmare)
- Writing commit hashes in emails/slack (“use commit a1b2c3d for prod”)
Tags fix all that cleanly.
Two Types of Tags – Very Important Difference
Git has two kinds — choose wisely:
| Type | Command Example | What it stores | Metadata (tagger name, email, date, message) | Best for | Recommendation in 2026 |
|---|---|---|---|---|---|
| Lightweight | git tag v1.0.0 | Just a name + pointer to commit (like a branch ref) | No | Private notes, quick bookmarks, temporary labels | Use only for your own experiments |
| Annotated | git tag -a v1.0.0 -m “Release 1.0.0” | Full Git object (like a mini-commit) | Yes — who tagged, when, message (can be GPG-signed) | Public releases, versions, production tags | Always prefer this for real releases |
Best practice 2026 (almost universal advice):
- Use annotated tags (-a) for anything you share/publish/release
- Use lightweight only for personal/temporary markers (e.g. git tag temp-before-refactor)
Annotated tags are more trustworthy — you know who said “this is v2.0”, when, and why.
Hands-on Example – Let’s Tag Our Todo App
(Continue from previous lessons or quick start:)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# Assume we have a small repo with a few commits mkdir todo-app-tagged cd todo-app-tagged git init # Add some files & commits (simulate work) echo "# Todo App v1" > README.md git add . && git commit -m "Initial structure" echo "- Buy milk" >> README.md git add . && git commit -m "Add first todo item" # Imagine this is a stable point — ready for "v1.0.0" |
1. Create a lightweight tag (simple bookmark)
|
0 1 2 3 4 5 6 |
git tag v1.0.0 |
→ No message, no extra info — just points to current HEAD commit.
2. Better: Create an annotated tag (recommended!)
|
0 1 2 3 4 5 6 7 8 9 |
git tag -a v1.0.0 -m "First stable release: basic todo list working" # or multi-line in editor: git tag -a v1.0.1 # (editor opens → write subject + blank line + body) |
Now let’s see what we have:
|
0 1 2 3 4 5 6 7 8 9 10 11 |
git tag # shows: # v1.0.0 # v1.0.1 (if you made both) git tag -l "v1.*" # filter pattern |
See details:
|
0 1 2 3 4 5 6 7 8 |
git show v1.0.0 # lightweight → just shows the commit git show v1.0.1 # annotated → shows tag info first, then commit |
Example annotated output snippet:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
tag v1.0.1 Tagger: Webliance <webliance@example.com> Date: Fri Feb 13 12:45:00 2026 +0530 First stable release: basic todo list working commit def5678... (HEAD -> main, tag: v1.0.1) Author: Webliance <...> ... |
3. Push Tags to GitHub / Remote (Very Important!)
Tags are local by default — git push does not send them!
|
0 1 2 3 4 5 6 7 8 9 10 |
# Push specific tag git push origin v1.0.1 # Push ALL tags (common one-liner) git push origin --tags |
Now on GitHub → go to repo → “Releases” tab → you’ll see nice release page if annotated!
4. Everyday Tag Commands Cheat Sheet
| Action | Command Example | Notes / Best Practice |
|---|---|---|
| Create lightweight | git tag hotfix-20260213 | Quick private marker |
| Create annotated | git tag -a v2.3.0 -m “Add dark mode + bug fixes” | Always use for releases |
| Create + sign (extra security) | git tag -s v2.3.0 -m “…” | Needs GPG key — very secure for open-source |
| List all tags | git tag or git tag -l “v*” | -l supports glob patterns |
| Show tag details | git show v1.0.0 | Annotated shows message |
| Delete local tag | git tag -d v1.0.0-beta | -d = delete |
| Delete remote tag | git push origin –delete v1.0.0-beta | Or old syntax: git push origin :refs/tags/v1.0.0-beta |
| Checkout code at tag | git checkout v1.0.1 | Goes to detached HEAD — safe to look |
| Start branch from tag (hotfix) | git checkout -b hotfix-v1.0.1-fix v1.0.1 | Common for patching old releases |
5. Common 2026 Best Practices & Tips
- Follow Semantic Versioning (semver.org) → vMAJOR.MINOR.PATCH (e.g. v1.0.0, v1.1.0, v2.0.0-rc.1)
- Tag only meaningful points — not every commit
- Use annotated tags for public/shared releases
- Never change/re-tag existing tags (breaks trust & CI) — delete & recreate new one if mistake
- Automate tagging in CI/CD (GitHub Actions can auto-tag on merge to main with conventional commits)
- On GitHub → annotated tags become beautiful “Releases” with changelogs, assets (APK, .zip), etc.
Mini Homework – Try Right Now!
- In any repo → git tag -a practice-v1 -m “My first real tag – Hyderabad 2026”
- git show practice-v1
- git push origin practice-v1 (if you have remote)
- Go to GitHub → see it under Releases or Tags
Got the idea? Tags = immutable milestones with friendly names.
Next class?
- How to create GitHub Releases from tags?
- Signed tags & GPG?
- Auto-versioning with conventional commits?
Or any confusion — lightweight vs annotated still fuzzy? Just say — we’ll clear it up with more examples. You’re doing great! 🚀
