Commits are identified by long hashes like 52ac9e1, which are perfect for computers but hard for people to remember. When you release version 1.0 of your app, publish a website or hand in a project, you'll want a clear, permanent label on that exact commit, so you can always find, rebuild or compare it later.
That label is a tag. Unlike a branch, a tag never moves: v1.0.0 points at the same commit forever. Tags are how projects mark their releases, and services like GitHub turn them into downloadable release pages.
In this lesson you'll create lightweight and annotated tags, list and inspect them, tag older commits, share tags with a remote, look at the code from a tag, delete tags, and use semantic versioning, the numbering scheme almost every project follows.
Lightweight and annotated tags
git tag v0.9 # lightweight
git tag -a v1.0.0 -m "First public release" # annotated- A lightweight tag is just a name pointing at a commit, like a bookmark.
- An annotated tag is a full object stored in Git, with its own message, the tagger's name and email, and the date.
Use annotated tags for releases. They record who released what and why, git describe uses them, and many tools only show annotated tags. Lightweight tags suit temporary, personal markers.
Listing, inspecting and tagging old commits
| Command | What it does |
|---|---|
git tag | List all tags alphabetically |
git tag -l "v1.*" | List tags matching a pattern |
git show v1.0.0 | Show the tag's details and the commit it points to |
git tag -a v0.8 9d41e07 -m "..." | Tag an older commit by its hash |
git describe --tags | Describe HEAD relative to the latest tag, e.g. v1.0.0-3-g52ac9e1 |
The git describe output means "three commits after v1.0.0, at commit 52ac9e1", which is handy for build numbers.
Sharing and using tags
git push doesn't send tags by default. Push them explicitly:
git push origin v1.0.0 # one tag
git push origin --tags # every tagOthers receive tags when they fetch. To look at the code exactly as it was at a tag, check it out:
git switch --detach v1.0.0This puts you in detached HEAD state: you're looking at a commit rather than a branch. That's fine for reading or building old code. If you want to make changes from there, create a branch first: git switch -c hotfix-1.0 v1.0.0.
Semantic versioning and deleting tags
Most projects number releases as MAJOR.MINOR.PATCH, called semantic versioning:
- PATCH (1.0.0 to 1.0.1): bug fixes only.
- MINOR (1.0.1 to 1.1.0): new features that don't break anything.
- MAJOR (1.1.0 to 2.0.0): changes that break existing behaviour.
Mistakes happen. Delete a tag locally with git tag -d v1.0.0 and on the remote with git push origin --delete v1.0.0. Avoid changing a tag that others may already have downloaded; it's usually better to release a new version number.
Sorting tags and finding which release has a fix
git tag sorts names alphabetically, which puts v1.10.0 before v1.2.0. Ask for version order instead:
git tag --sort=-v:refname # newest version firstThe v:refname key compares the numbers in each name as versions, and the leading - reverses the order so the latest release comes first.
When someone reports that a bug fix isn't in their version, find out which releases include the commit:
git tag --contains 52ac9e1This lists every tag whose history includes that commit. If it prints nothing, the fix hasn't been released yet.
Two details about git describe matter in scripts. Without --tags it only considers annotated tags, which is another reason to annotate releases. And git describe --abbrev=0 prints just the most recent tag name, such as v1.0.0, without the commit count and hash, which is handy when a build needs to know the last version number.
If you'd rather not push tags as a separate step, git push --follow-tags pushes your commits together with any annotated tags that point at commits being pushed. Lightweight tags are left behind, so personal bookmarks stay on your machine.
Git Tags Syntax
Tag commands
git tag -a v1.2.0 -m "Release message"
git tag -a v1.1.0 <commit> -m "..."
git tag -l "v1.*"
git show v1.2.0
git push origin v1.2.0
git push origin --tags
git switch --detach v1.2.0
git tag -d v1.2.0
git push origin --delete v1.2.0Git Tags Summary
- Tags give permanent names to specific commits; unlike branches, they don't move.
- Use annotated tags (
git tag -a v1.0.0 -m "...") for releases. git tag,git tag -l "pattern"andgit show <tag>list and inspect tags.- Push tags explicitly with
git push origin <tag>or--tags. - Follow semantic versioning: MAJOR.MINOR.PATCH.
Git Tags Examples
Each example shows the code, the output it produces and what each part does.
Tagging and inspecting a release
git tag -a v1.0.0 -m "First public release"
git tag
git show v1.0.0 --no-patchv1.0.0
tag v1.0.0
Tagger: Sam Lee <sam@example.com>
Date: Mon Sep 2 14:20:11 2024 +0100
First public release
commit 52ac9e1d0b7f3a8e9c2d4f6a1b3c5d7e9f0a2b4c (HEAD -> main, tag: v1.0.0)
Author: Sam Lee <sam@example.com>
Date: Mon Sep 2 13:55:42 2024 +0100
Add dark colour schemeLine-by-line explanation
- Line 1Create an annotated tag on the current commit.
- Line 2List the tags in the repository.
- Line 3Show the tag's own details, then the commit it points to. --no-patch hides the file changes.
Tagging an older commit and listing by pattern
git log --oneline -3
git tag -a v0.9.0 9d41e07 -m "Beta for testers"
git tag -l "v0.*"
git describe --tags52ac9e1 (HEAD -> main, tag: v1.0.0) Add dark colour scheme
9d41e07 Add contact form
6b2a1f3 Style the navigation bar
v0.9.0
v1.0.0Line-by-line explanation
- Line 2Give the hash to tag a commit from the past.
- Line 3-l with a pattern lists only matching tags.
- Line 4HEAD is exactly at a tag, so describe prints the tag name alone.
Publishing tags and checking out an old release
git push origin v1.0.0
git switch --detach v0.9.0
git switch mainTo https://github.com/samlee/portfolio.git
* [new tag] v1.0.0 -> v1.0.0
HEAD is now at 9d41e07 Add contact form
Switched to branch 'main'Line-by-line explanation
- Line 1Tags must be pushed explicitly. On GitHub, v1.0.0 now appears under Tags.
- Line 2Look at the code as it was in the beta. You're in detached HEAD state.
- Line 3Return to the main branch when you're done.
Common Git Tags Mistakes
Expecting git push to send tags
git tag -a v1.1.0 -m "New features"
git push # the tag stays on your computergit push origin v1.1.0Committing in detached HEAD
git switch --detach v1.0.0
git commit -am "Hotfix" # commit isn't on any branch and is easy to losegit switch -c hotfix-1.0.1 v1.0.0
git commit -am "Hotfix"Moving a published tag
git tag -f v1.0.0 # retag a different commit after people downloaded it
git push --force origin v1.0.0git tag -a v1.0.1 -m "Fix missing image"
git push origin v1.0.1Git Tags in Real Projects
Tags and releases
Libraries on npm, PyPI and Packagist are published from tagged commits, and GitHub turns each tag into a release page with downloadable source code. Deployment pipelines often build and ship automatically when a tag like v2.3.0 is pushed, and changelogs list what changed between one tag and the next.
Git Tags FAQ
How do I list tags along with their messages?
Run git tag -n. It lists each tag followed by the first line of its annotation message; for a lightweight tag, which has no message, it shows the commit's message instead. Use -n5 to show up to five lines of each message.
How do I get tags from a remote?
A normal git fetch or git pull downloads tags that point at commits you're fetching. To download every tag the remote has, run git fetch --tags. Then git tag lists them locally.
How can I tell if a tag is annotated or lightweight?
Run git cat-file -t v1.0.0. It prints tag for an annotated tag, because there's a separate tag object, and commit for a lightweight tag, which points straight at a commit. git show also gives it away: annotated tags show a "Tagger" line and message before the commit.
How do I list tags by date?
Use git for-each-ref --sort=-creatordate --format='%(refname:short) %(creatordate:short)' refs/tags. It lists every tag with its date, newest first. For annotated tags the date is when the tag was made; for lightweight tags it's the date of the commit.
Should version tags start with v?
It's a convention, not a rule. Most projects write v1.2.0, and many release tools expect that prefix, but the semantic versioning specification itself treats the version as just 1.2.0. Pick one style and use it consistently, because tags sort and match by their exact names.
Git Tags Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the command that creates an annotated tag.
git tag v2.0.0 "Second major release"
What's the main difference between a tag and a branch?
Which kind of tag should you use for a release?
A project is at version 2.4.1 and you fix a bug without adding features. What's the next version?
How do you share every local tag with origin?
Git Tags Quiz
5 questions to check what you learned.
Which command lists all tags?
- git tag
- git tags --all
- git show tags
- git log --tags only
What does "detached HEAD" mean?
- The repository is broken
- HEAD has been deleted
- You're on a remote branch
- You're looking at a specific commit instead of a branch
In semantic versioning, which number changes for a breaking change?
- MAJOR
- MINOR
- PATCH
- None
How do you tag a commit from the past?
- You can't
- git tag --old v0.5
- git tag -a v0.5 <commit-hash> -m "..."
- Check it out and wait
What does
v1.0.0-3-g52ac9e1from git describe mean?- Version 1.0.3
- Three commits after the v1.0.0 tag, currently at commit 52ac9e1
- Three tags called v1.0.0
- A broken tag