git tag: marking versions and releases

Updated July 31, 2026

A tag is a permanent name for one particular commit, and unlike a branch it never moves. A branch travels forward with every commit, while v1.0 three years from now points exactly where it pointed on release day.

You put tags where returning to an exact state matters: a version, a release, the build you sent to a customer.

$ git tag -a v1.0 -m "first release"
$ git tag
v0.9
v1.0

Lightweight and annotated

A lightweight tag (git tag v0.9) is nothing but a name for a commit hash. It works as a temporary bookmark for yourself.

An annotated tag (git tag -a v1.0 -m "...") is a separate object in the repository, with an author, a date, and a message. That is the one you put on releases: a year later the record of who tagged the commit and why is worth having.

$ git show v1.0
tag v1.0
Tagger: Anna Petrova <anna@example.com>
Date:   Tue Jul 21 21:37:52 2026 -0400

first release

commit a66eee41906fb21dff6996229c345e4ddb4386eb

The difference also shows in the listing with messages. A lightweight tag has no message of its own, so -n prints the subject of the commit it sits on:

$ git tag -n
v0.9            bob fixes typo
v1.0            first release

$ git tag -l "v1.*"       # filter by pattern
v1.0

You can tag after the fact by naming a hash: git tag -a v0.8 773e133. And to see the project the way it went out in a release, use git switch --detach v1.0. That branchless mode is covered in detached HEAD.

Tags do not travel with a plain push

git push sends the commits of branches and leaves tags alone. You send them separately:

$ git push origin v1.0
To github.com:username/my-project.git
 * [new tag]         v1.0 -> v1.0

$ git push --tags         # send every tag at once

Deleting works in two places as well: git tag -d v0.9 removes the tag on your side, git push origin --delete v0.9 removes it on the server.

Common mistakes

You tagged the commit, built the release, and there is nothing on GitHub: git push origin v1.0 was never run. That is the first thing to check.

Moving an already published tag with git tag -f is a bad idea. For everyone who fetched it, v1.0 still points at the old commit, and talking about "that exact version" turns into guesswork. Release v1.0.1 instead.

Where this is in the book

The "What comes next" section of the final project.