Tags & Releases

Git · Lesson 9 of 28 7 min read Updated

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 tagAnnotated tag
Create withgit tag v1.1.0-draftgit tag -a v1.1.0 -m "message"
StoresJust a name pointing at a commitA full object: tagger, date, message, then the commit
Good forPrivate, temporary bookmarksReleases 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:

Terminal
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 tag
Output
v1.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:

Terminal
git tag -l "v1.1*"
git tag -n
Output
v1.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:

Terminal
git show v1.0.0 --stat
Output
tag 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:

Terminal
git describe
Output
v1.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.0 to 1.1.1.
  • Increase MINOR for new features that do not break existing use: 1.1.1 to 1.2.0.
  • Increase MAJOR for changes that break existing use: 1.2.0 to 2.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:

Terminal
git push origin v1.0.0
git push origin --tags
Output
To 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:

Terminal
git tag -d v1.1.0-draft
Output
Deleted 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:

  1. Open your repository on GitHub and click Releases, then Draft a new release.
  2. Choose an existing tag such as v1.1.0, or type a new tag name and pick the branch to create it from.
  3. 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.
  4. 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 -a for 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 describe names 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
Terminal
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 describe

The 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?

2. Does git push send your tags to the remote by default?

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?

4. What does v1.1.0-1-g3aa8447 from git describe mean?

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