Git Tag: Mark Releases and Milestones in Your History
Git tags let you permanently mark specific commits — typically releases or milestones. This tutorial covers creating lightweight and annotated tags, listing and pushing them, and the most common mistake developers make: forgetting that git push doesn't send tags automatically.
The Problem
You shipped a stable release but a week later you can't find the exact commit it was built from. Or you tagged a release locally and your teammates can't see it because you never pushed the tag. Sound familiar?
Tags are the standard way to mark release points — v1.0.0, v2.3.1 — in your Git history. Unlike branches, they don't move. A tag permanently points to one commit, making it the reliable anchor for changelogs, rollbacks, and CI deployments.
Common mistakes developers make with this:
Creating a tag locally but forgetting that git push does not push tags automatically
Using lightweight tags for releases instead of annotated tags, which carry no author, date, or message
Tagging the wrong commit because you didn't check git log first
Deleting a tag that was already pushed without removing it from the remote too
Gitoryx: Gitoryx displays all tags inline on the commit graph — you can see at a glance which commits are tagged, create or delete tags with a right-click, and push them to remote in one click.
What is Tag: Mark Releases and Milestones in Your History?
A Git tag is a named pointer to a specific commit that never moves. Tags come in two types: lightweight (just a pointer, no extra metadata) and annotated (stores a message, author, and date — recommended for releases). Tags must be pushed to a remote separately from commits.
Step-by-Step Guide
1
List all existing tags
Before creating a tag, check what tags already exist in the repository. Use -l with a glob pattern to filter by prefix.
bash
# List all tags
git tag
# Filter by pattern (e.g. v1.*)
git tag -l "v1.*"
# Show tags in chronological order
git log --tags --simplify-by-decoration --oneline
Tags are listed alphabetically by default, which breaks for versions like v1.9.0 vs v1.10.0. Use git tag -l --sort=-version:refname for correct semantic version ordering.
2
Create a lightweight tag
A lightweight tag is simply a name pointing to a commit — no message, no author, no date. Useful for temporary local bookmarks, but not recommended for public releases.
bash
# Tag the current commit (HEAD)
git tag v1.0.0
# Tag a specific commit
git tag v1.0.0 9b1e4fa
Lightweight tags carry no metadata. Tools like GitHub Releases, CI pipelines, and changelog generators rely on annotated tag objects. Use annotated tags for anything public.
3
Create an annotated tag (recommended for releases)
An annotated tag stores a message, the tagger's name and email, and a timestamp — making it a full Git object. Use -a for annotated and -m for an inline message.
bash
# Annotated tag on HEAD
git tag -a v1.0.0 -m "Release version 1.0.0"
# Annotated tag on a specific commit
git tag -a v1.0.0 -m "Release version 1.0.0" 9b1e4fa
# View the tag details (message, tagger, date)
git show v1.0.0
Follow semantic versioning: vMAJOR.MINOR.PATCH. Breaking changes → major, new features → minor, bug fixes → patch.
4
Push tags to remote
git push does not push tags. You must push them explicitly — either one at a time or all at once with --tags.
bash
# Push a single tag
git push origin v1.0.0
# Push all local tags not yet on the remote
git push origin --tags
git push --tags pushes every local tag including lightweight ones. To push only annotated tags alongside your latest commit, use git push --follow-tags.
See this workflow in Gitoryx. Tags appear as labels directly on commits in the Gitoryx commit graph — no need to run `git log --decorate`
Checking out a tag puts you in 'detached HEAD' state — you're viewing the repository exactly as it was at that tagged commit. Any commits made here won't belong to a branch.
bash
git checkout v1.0.0
# HEAD is now at 9b1e4fa... (detached)
# To make changes based on a tag, create a branch from it:
git checkout -b hotfix/v1.0.1 v1.0.0
If you need to make a hotfix on a tagged release, always create a new branch from the tag rather than committing in detached HEAD.
6
Delete a tag
Deleting a local tag does not remove it from the remote. You must delete it in both places separately.
bash
# Delete a local tag
git tag -d v1.0.0
# Delete the tag on the remote
git push origin --delete v1.0.0
Deleting a tag that others may have already pulled causes confusion. Communicate the deletion to your team before removing a public tag from the remote.
Common Mistakes to Avoid
Forgetting to push tags
git push only pushes commits and branch refs — tags are entirely separate. A tag that exists only locally is invisible to teammates, CI pipelines, and GitHub Releases.
Fix: After creating a tag, always run git push origin <tagname> or git push origin --tags to make it available on the remote.
Using lightweight tags for releases
Lightweight tags store no metadata. Tools like GitHub Releases, semantic-release, and changelog generators rely on annotated tag objects to generate release notes.
Fix: Use git tag -a <name> -m '<message>' for any tag intended as a public release. Reserve lightweight tags for private local bookmarks.
Tagging the wrong commit
It's easy to tag HEAD without realising it's not the right commit — especially after a recent pull.
Fix: Run git log --oneline -5 before tagging to confirm HEAD is correct. When in doubt, tag a specific SHA: git tag -a v1.0.0 -m 'msg' <sha>.
Gitoryx — macOS & Windows
Create and manage tags visually in Gitoryx
Tags appear as labels directly on commits in the Gitoryx commit graph — no need to run git log --decorate
Right-click any commit to create or delete a tag without copying a SHA
Push tags to remote in one click from the tag context menu
Gitoryx distinguishes annotated and lightweight tags visually in the graph
What is the difference between a lightweight and annotated tag?
A lightweight tag is just a named pointer to a commit with no extra data. An annotated tag is a full Git object that stores a message, the tagger's name and email, and a timestamp. Annotated tags are recommended for releases because they carry metadata and are treated as proper release objects by most tools.
Why can't my teammates see my tag?
git push does not push tags automatically. Run git push origin <tagname> to push a specific tag, or git push origin --tags to push all local tags to the remote.
How do I tag a previous commit, not HEAD?
Pass the commit SHA as the last argument: git tag -a v1.0.0 -m 'message' <sha>. Find the SHA with git log --oneline.
Can I move a tag to a different commit?
Yes, but it requires force: git tag -f v1.0.0 <new-sha>. If the tag was already pushed, also force-push it: git push origin -f v1.0.0. Only do this before others have pulled the tag.
How do I list tags sorted by semantic version?
Use git tag -l --sort=-version:refname to list tags in descending semantic version order. This is more reliable than alphabetical sort for versions like v1.10.0 vs v1.9.0.
Everything in this tutorial is faster and clearer with a visual Git client. Gitoryx is free, runs natively on macOS and Windows, and built for developers who want to move fast without breaking things.