Tags & Releases
Commit hashes are perfect for computers and terrible for people. Nobody wants to tell a customer "please install daae9bf". A tag gives a permanent, human-friendly name such as v1.0.0 to one specific commit, so you can always find the exact code that went live on a given day.
In this lesson you will create and inspect tags, learn the difference between the two kinds, name versions with semantic versioning, share tags with a remote, and turn a tag into a GitHub release.
Two kinds of tags
| Lightweight tag | Annotated tag | |
|---|---|---|
| Create with | git tag v1.1.0-draft | git tag -a v1.1.0 -m "message" |
| Stores | Just a name pointing at a commit | A full object: tagger, date, message, then the commit |
| Good for | Private, temporary bookmarks | Releases and anything you share |
A tag, unlike a branch, never moves. Once v1.0.0 points at a commit, it keeps pointing there even as you add more commits.
Creating tags
Priya decides that commit daae9bf (the menu title fix) was the first public version of the shop website, and today's latest commit is version 1.1. She tags both, plus a quick lightweight bookmark:
git tag -a v1.0.0 -m "First public version of the shop website" daae9bf
git tag v1.1.0-draft
git tag -a v1.1.0 -m "Contact page and price styles"
git tagv1.0.0 v1.1.0 v1.1.0-draft
When you give a commit at the end, Git tags that commit; without one, it tags HEAD. git tag on its own lists all tags in alphabetical order, and -l with a pattern filters them. -n adds the first line of each message:
git tag -l "v1.1*"
git tag -nv1.1.0 v1.1.0-draft v1.0.0 First public version of the shop website v1.1.0 Contact page and price styles v1.1.0-draft Revert "Change opening hours"
The lightweight tag has no message of its own, so -n shows the subject of the commit it points at.
Inspecting a tag
git show on an annotated tag prints the tag's own details first, then the commit:
git show v1.0.0 --stattag v1.0.0
Tagger: Priya Sharma <priya@example.com>
Date: Fri Oct 30 14:37:30 2026 +0530
First public version of the shop website
commit daae9bf7acf870dc6cc43077e530b50fca23955a
Author: Priya Sharma <priya@example.com>
Date: Fri Oct 30 14:33:45 2026 +0530
Use a hyphen in the menu page title
menu.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)Tags work anywhere a commit is expected: git diff v1.0.0 v1.1.0, git log v1.0.0..HEAD, git switch --detach v1.0.0 to look at old code, or git restore --source=v1.0.0 menu.html.
Where am I? git describe
git describe names the current commit relative to the nearest annotated tag. After one more commit, it says so:
git describev1.1.0-1-g3aa8447
Read it as "1 commit after v1.1.0, at commit 3aa8447" (the g stands for Git). Build scripts often embed this string as a version number, so a bug report tells you exactly which code the user had.
Semantic versioning
Most projects name versions with semantic versioning (SemVer): MAJOR.MINOR.PATCH, for example 2.4.1.
- Increase PATCH for bug fixes that change nothing else:
1.1.0to1.1.1. - Increase MINOR for new features that do not break existing use:
1.1.1to1.2.0. - Increase MAJOR for changes that break existing use:
1.2.0to2.0.0. - Pre-releases add a label:
2.0.0-beta.1.
The v prefix (v1.0.0) is a common convention for tag names, not part of SemVer itself. Git's own releases are tagged this way: Git 2.56.0 is tag v2.56.0 in Git's repository.
Sharing and deleting tags
git push does not send tags by default. Push one tag by name, or all of them:
git push origin v1.0.0
git push origin --tagsTo github.com:priya-sharma/sweet-shop.git * [new tag] v1.0.0 -> v1.0.0 To github.com:priya-sharma/sweet-shop.git * [new tag] v1.1.0 -> v1.1.0
--tags pushes every tag, including private lightweight ones you did not mean to share. A safer habit is git push --follow-tags, which pushes only annotated tags that point at commits you are pushing.
To delete a mistaken tag, remove it locally:
git tag -d v1.1.0-draftDeleted tag 'v1.1.0-draft' (was 24d8d06)
If you had already pushed it, delete it on the remote too with git push origin --delete v1.1.0-draft.
Avoid moving or deleting release tags that others have already fetched. If v1.1.0 had a bug, release v1.1.1; do not quietly point v1.1.0 at different code.
Releases on GitHub
A GitHub release is a page built on top of a tag. It adds a title, release notes written in Markdown, and downloadable files such as installers or zip archives (GitHub automatically attaches the source code as .zip and .tar.gz). To create one:
- Open your repository on GitHub and click Releases, then Draft a new release.
- Choose an existing tag such as
v1.1.0, or type a new tag name and pick the branch to create it from. - Give the release a title and notes. The Generate release notes button lists the merged pull requests and contributors since the previous release, which you can edit.
- Attach files if you have any, tick Set as a pre-release for betas, and click Publish release.
People who "watch" your repository for releases get notified, and the release page becomes the official download link for that version.
Common mistakes
- Forgetting to push tags. The tag exists only on your laptop until you push it.
- Using lightweight tags for releases. They have no author, date or message; use
-afor anything public. - Moving a published tag. Others will keep the old one and you will have two different
v1.1.0s. Make a new version instead. - Treating a tag like a branch. You cannot commit "on" a tag. To change old code, create a branch from the tag:
git switch -c hotfix v1.0.0.
Key points
- A tag is a fixed, readable name for one commit; it never moves.
- Annotated tags (
git tag -a) store tagger, date and message and are right for releases; lightweight tags are simple bookmarks. git describenames the current commit relative to the nearest tag.- Semantic versioning uses MAJOR.MINOR.PATCH to signal breaking changes, features and fixes.
- Push tags explicitly (
git push origin <tag>or--follow-tags); a GitHub release adds notes and files on top of a tag.
Exercise
In a practice repository with a few commits, create an annotated tag v0.1.0 on the oldest commit and v0.2.0 on the newest. List the tags with their messages, then show which files changed between the two versions. Finally, make one more commit and run git describe.
Show solution
git log --oneline --reverse
git tag -a v0.1.0 -m "First prototype" 5a40b1e
git tag -a v0.2.0 -m "Second prototype"
git tag -n
git diff --stat v0.1.0 v0.2.0
git commit -am "Small wording fix"
git describeThe first git log line gives the oldest commit's hash (yours will differ). After the new commit, git describe prints something like v0.2.0-1-g1a2b3c4: one commit after v0.2.0.
Quick Quiz
1. What is the main difference between an annotated and a lightweight tag?
Annotated tags are full objects with metadata, which is why releases use them.
2. Does git push send your tags to the remote by default?
Plain git push sends branches only.
3. A project is at version 3.4.2 and you add a new feature that does not break anything. What is the next version under SemVer?
New, backwards-compatible features increase MINOR and reset PATCH to 0.
4. What does v1.1.0-1-g3aa8447 from git describe mean?
The middle number counts commits since the tag and the last part is the commit's short hash.
Learn Git with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
