A tag marks a specific commit with a name (usually a version like v1.8.3), so you can always find, build, or deploy exactly that state of the code. Tags are the backbone of a clean release process: CI builds images from them, release pages attach to them, and rollbacks point to them.
Lightweight vs annotated tags
| Type | Command | Contains |
|---|---|---|
| Lightweight | git tag v1.8.3 |
Just a pointer to a commit |
| Annotated | git tag -a v1.8.3 -m "Release 1.8.3" |
Tagger, date, message (and optional signature) |
Use annotated tags for releases. Many tools (git describe, release automation) expect them.
Create and push tags
git switch main && git pull
git tag -a v1.8.3 -m "Release 1.8.3: invoice export, login fix"
git push origin v1.8.3 # tags aren't pushed by default
git push --follow-tags # push commits plus annotated tags reachable from them
Tag an older commit:
git tag -a v1.8.2 9fceb02 -m "Release 1.8.2"
List and inspect
git tag -l "v1.8.*"
git show v1.8.3
git describe --tags # e.g. v1.8.3-4-g1a2b3c4 (4 commits after v1.8.3)
git log --oneline v1.8.2..v1.8.3 # what changed between releases
Semantic versioning
MAJOR.MINOR.PATCH (for example 2.4.1):
- MAJOR: incompatible changes (breaking APIs, migrations that need care);
- MINOR: new backwards-compatible features;
- PATCH: backwards-compatible bug fixes.
Pre-releases: v2.0.0-rc.1, v2.0.0-beta.2. For web apps deployed continuously, date-based versions (2026.11.05) also work. Consistency matters more than the scheme.
Delete or move a tag
git tag -d v1.8.3 # local
git push origin --delete v1.8.3 # remote
Avoid moving published release tags. Others (and your CI, and image registries) may already have built from them. Release a new patch version instead.
Signed tags
git tag -s v1.8.3 -m "Release 1.8.3" # GPG (or SSH signing with gpg.format=ssh)
git tag -v v1.8.3 # verify
Signed tags prove who created the release. That’s useful for open-source projects and supply-chain security. See Linux checksums and GPG.
Release notes and changelogs
- Generate notes from commit messages between tags:
git log --pretty='- %s' v1.8.2..v1.8.3. - With Conventional Commits, tools like release-please, semantic-release or git-cliff create changelogs and version bumps automatically. See Git hooks.
GitHub, GitLab and Gitea releases
A “release” on these platforms is a tag, plus release notes and optional files (binaries, archives):
gh release create v1.8.3 --title "1.8.3" --notes-file CHANGELOG-1.8.3.md ./dist/app-linux-amd64
Gitea and GitLab have equivalent pages and APIs. See Gitea.
Tag-triggered builds and deploys
Tags are the cleanest trigger for production deploys:
# GitHub Actions
on:
push:
tags: ["v*.*.*"]
CI builds an image tagged with the same version (myapp:1.8.3), and deploys it. Rolling back means redeploying the previous tag. See GitHub Actions Docker builds and Docker image tags best practices.
Quick reference
| Task | Command |
|---|---|
| Annotated tag | git tag -a v1.2.3 -m "msg" |
| Push one / all | git push origin v1.2.3 / git push --follow-tags |
| List | git tag -l "v1.*" |
| Changes since | git log v1.2.2..v1.2.3 |
| Delete | git tag -d v1.2.3 && git push origin --delete v1.2.3 |
| Signed | git tag -s / git tag -v |
Related: Git branching strategies and deployment strategies.