A git tag is a permanent, human-readable name pinned to one commit, the way you mark v1.2.0 so you can find the exact code you shipped months later. For anything you release, create an annotated tag, push it to the remote on its own, and you are done:
git tag -a v1.2.0 -m "Release 1.2.0" # create an annotated tag
git push origin v1.2.0 # push it (git push alone will NOT)That is the whole job for most people. The rest of this page is the detail behind it: the difference between annotated and lightweight tags, tagging a commit you made last week, listing and inspecting tags, deleting one in both places it lives, and checking one out.
Annotated vs lightweight tags
Git has two kinds of tag, and the choice matters more than it looks.
An annotated tag is a full object in git's database. It stores the tagger's name and email, the date, a message, and can be GPG-signed and verified. It is the right tool for a release, and it is what release tooling and CI expect.
git tag -a v1.2.0 -m "Release 1.2.0"A lightweight tag is just a pointer to a commit, with no metadata attached. It is fine as a private, throwaway bookmark, but it carries no record of who tagged it or when.
git tag v1.2.0The rule I follow: annotated for anything anyone else will see, lightweight only for a quick local marker. There is a real functional reason beyond tidiness. Some commands, git describe chief among them, ignore lightweight tags by default, so a release marked with a lightweight tag can quietly fall out of your version strings. If in doubt, use -a.
To sign a tag instead of just annotating it (the tagger identity is then cryptographically verifiable), use -s:
git tag -s v1.2.0 -m "Release 1.2.0" # GPG-signed
git tag -v v1.2.0 # verify a signed tagTag a past commit
By default a tag points at whatever HEAD is right now. To tag an older commit (you forgot to tag the release, or you are backfilling history), pass the commit SHA as the last argument:
git log --oneline # find the commit you want
git tag -a v1.0.0 -m "Release 1.0.0" 9fceb02 # tag that specific commitThe same form works with any commit-ish: a branch name, HEAD~3, or a short SHA.
Push tags to the remote
Here is the gotcha that catches almost everyone: git push does not push tags. You can tag a commit, push your branch, and the tag stays sitting on your machine. Tags have to go up explicitly.
Push a single tag (the form I prefer, because it is deliberate):
git push origin v1.2.0Push every tag you have that the remote does not:
git push origin --tagsThere is a middle option worth knowing. --follow-tags pushes your branch and any annotated tags that point at commits in the range being pushed, in one command. It deliberately ignores lightweight tags, which is another reason to annotate your releases:
git push --follow-tagsYou can make that the default with git config --global push.followTags true, so a normal git push carries your release tags along without --tags dragging up every stray bookmark.
List and inspect tags
List all tags:
git tagFilter with a pattern (-l plus a glob), which is how you find one release line among many:
git tag -l "v1.*"To see what a tag actually points at, plus its message and (for annotated tags) the tagger and date, use git show:
git show v1.2.0For an annotated tag that prints the tag object header, then the commit it references. For a lightweight tag you just get the commit, which is itself a quick way to tell the two apart.
Delete a tag
A tag lives in two places once you have pushed it: your local repo and the remote. Deleting it locally does not touch the remote, so you usually need both commands.
Delete the local tag:
git tag -d v1.2.0Delete it on the remote:
git push origin --delete v1.2.0You may still see the old syntax git push origin :refs/tags/v1.2.0 around. It does the same thing (pushing an empty source ref deletes the destination), but --delete is the readable modern form, so reach for that.
Check out a tag
You can check out the code at a tag to inspect or build it. Because a tag points at a fixed commit rather than a moving branch, this drops you into a detached HEAD state, where new commits belong to no branch and are easy to lose:
git checkout v1.2.0If you only want to look, that is fine. If you intend to make changes from that point, branch off it first so your work has a home:
git switch -c hotfix-1.2.1 v1.2.0For more on moving between branches and the modern switch command, see my essential git branch commands reference.
FAQ
See also
- How to undo things in git: the practical guide to restore, reset, revert, and amend when you need to walk something back.
- Essential git branch commands: current branch, default branch, switching, and deleting branches with the modern
switchcommand. - Git force push safely: why plain
--forceoverwrites a teammate's work and how--force-with-leaseprotects you.
Sources
Authoritative references this article was fact-checked against.
- git-tag (official documentation)git-scm.com
- Git Basics: Tagging (Pro Git book)git-scm.com
- git-push (official documentation)git-scm.com





