Git Tags, Releases & Semantic Versioning Guide
In this tutorial, you'll learn about Git Tags, Releases & Semantic Versioning Guide. We cover key concepts, practical examples, and best practices.
A Git tag is a reference that points to a specific commit to mark version numbers, and unlike branches, tags do not move when new commits are added.
In this tutorial, you'll learn Git tags and how they integrate with release management and semantic versioning. Tags mark specific points in history as important — typically releases. By the end, you'll create annotated and signed tags, manage releases on GitHub and GitLab, and automate version bumps with CI/CD.
flowchart LR
A[Commit 1] --> B[Commit 2]
B --> C[Commit 3]
C --> D[Commit 4]
C --> E{v1.0.0 Tag}
D --> F[Commit 5]
F --> G{v1.1.0 Tag}
E -.-> H[Release v1.0.0]
G -.-> I[Release v1.1.0]
Semantic Versioning (SemVer)
SemVer uses the format MAJOR.MINOR.PATCH:
| Component | When to Bump | Example |
|---|---|---|
| MAJOR | Incompatible API changes | 1.0.0 to 2.0.0 |
| MINOR | Backward-compatible new features | 1.0.0 to 1.1.0 |
| PATCH | Backward-compatible bug fixes | 1.0.0 to 1.0.1 |
Pre-release tags: 1.0.0-alpha.1, 1.0.0-beta.2, 1.0.0-rc.1
Creating Tags
Lightweight Tags
A lightweight tag is just a pointer to a commit:
git tag v1.0.0
git tag v1.0.0 a1b2c3d # Tag a specific commit
Annotated Tags
An annotated tag stores the tagger name, email, date, and a message:
git tag -a v1.0.0 -m "Release version 1.0.0"
git tag -a v1.0.0 -m "Release version 1.0.0" a1b2c3d
Expected output:
$ git tag -a v1.0.0 -m "Release version 1.0.0"
$ git show v1.0.0
tag v1.0.0
Tagger: Your Name <you@example.com>
Date: Mon Jun 22 10:00:00 2026 +0000
Release version 1.0.0
commit a1b2c3d...
Pushing Tags
Tags are not automatically pushed with commits:
git push origin v1.0.0 # Push one tag
git push origin --tags # Push all tags
Signed Tags
Sign tags with GPG for verification:
git tag -s v1.0.0 -m "Release 1.0.0"
Verify a signed tag:
git tag -v v1.0.0
Expected output:
object a1b2c3d...
type commit
tag v1.0.0
tagger Your Name <you@example.com> ...
gpg: Signature made Mon Jun 22 10:00:00 2026
gpg: Good signature from "Your Name <you@example.com>"
Deleting Tags
git tag -d v1.0.0 # Delete local tag
git push origin --delete v1.0.0 # Delete remote tag
Releases on GitHub
Create a release from a tag using the GitHub CLI:
gh release create v1.0.0 --title "v1.0.0" --notes "Initial release"
Automated Versioning with CI/CD
Use CI/CD to auto-tag releases:
# .github/workflows/release.yml
name: Create Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Bump version
id: bump
run: |
git tag -a v1.0.${{ github.run_number }} -m "Release"
git push origin v1.0.${{ github.run_number }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Comparison: Tag Types
| Feature | Lightweight | Annotated | Signed |
|---|---|---|---|
| Storage | Just a ref | Full object | Full object + signature |
| Metadata | None | Name, email, date, message | Same + GPG signature |
| Use case | Temporary markers | Official releases | Verified releases |
| Command | git tag v1.0 |
git tag -a v1.0 -m "msg" |
git tag -s v1.0 -m "msg" |
Common Errors
| Error | Cause | Fix |
|---|---|---|
fatal: tag 'v1.0' already exists |
Tag already created locally | Delete and recreate or use a new tag |
Everything up-to-date on tag push |
Tag not pushed | Use git push origin <tagname> |
gpg: signing failed |
Missing GPG key | Generate and configure GPG key |
No tag exactly matches |
Wrong tag name | Check with git tag -l |
Tag not found on fetch |
Remote tags not fetched | git fetch --tags |
detached HEAD on checkout |
Checked out a tag | Create a branch: git switch -c branch-name |
Cannot delete tag |
Tag is release protected | Delete GitHub release first |
Practice Questions
Challenge
Create a repository with five commits. Tag the third commit as v0.1.0 (annotated). Make two more commits, then tag the latest as v0.2.0-beta (lightweight). Push both tags. Create a release on GitHub for v0.1.0 with release notes describing the features. Delete the v0.2.0-beta tag locally and remotely.
Real-World Task
Implement an automated release workflow using GitLab CI/CD that creates a tag and release when code is merged to main. The pipeline should: bump the version based on commit messages (using semantic-release or a custom script), create an annotated and signed tag, generate release notes from commit history, and push the tag. This automated release process is used at DodaTech for DodaZIP to ensure every version is traceable and signed.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro