Git Tagging & Release Management — Semantic Versioning and Automation
In this tutorial, you'll learn about Git Tagging & Release Management. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
What You'll Learn
Use Git tags to mark releases — create annotated and lightweight tags, follow semantic versioning, sign tags with GPG, and automate release workflows in CI/CD.
Why It Matters
Tags are permanent landmarks in your project's history. Unlike branches, they don't move. A properly tagged release lets anyone checkout v1.2.3 and get exactly the same code that was deployed to production — essential for debugging, rollbacks, and Compliance.
Real-World Use
A CI pipeline automatically creates a v1.2.3 annotated tag when the release branch is merged to main. Operations deploys the tag to production, and when a bug is reported, developers can git checkout v1.2.3 to reproduce the exact deployed state.
Types of Tags
graph LR
A[Git Tags] --> B[Lightweight]
A --> C[Annotated]
B --> D[Just a pointer
to a commit]
C --> E[Full object with:
- Tagger name/email
- Date
- Message
- GPG signature]
C --> F[Recommended for releases]
# Lightweight tag (just a name pointing to a commit)
git tag v1.0.0
# Annotated tag (recommended for releases)
git tag -a v1.0.0 -m "Release v1.0.0 - Initial production release"
Creating Annotated Tags
# Tag the current commit
git tag -a v1.2.0 -m "Release v1.2.0 - Add payment processing"
git tag -a v1.2.1 -m "Release v1.2.1 - Fix login timeout bug"
# Tag a specific commit (not HEAD)
git tag -a v1.0.0 a1b2c3 -m "Release v1.0.0"
# View tag details
git show v1.2.0
# tag v1.2.0
# Tagger: Jane Doe <jane@example.com>
# Date: Mon Mar 4 14:30:00 2024
#
# Release v1.2.0 - Add payment processing
#
# commit a1b2c3d4e5f6...
# Author: John <john@example.com>
# Implement Stripe payment gateway
Semantic Versioning with Tags
Follow the MAJOR.MINOR.PATCH scheme:
# PATCH: backwards-compatible bug fixes
git tag -a v1.2.1 -m "v1.2.1 - fix login redirect"
# MINOR: backwards-compatible new features
git tag -a v1.3.0 -m "v1.3.0 - add dark mode"
# MAJOR: breaking changes
git tag -a v2.0.0 -m "v2.0.0 - complete API rewrite"
# Pre-release versions
git tag -a v2.0.0-beta.1 -m "v2.0.0-beta.1"
git tag -a v2.0.0-rc.1 -m "v2.0.0-rc.1 (release candidate)"
Listing and Searching Tags
# List all tags
git tag
# v1.0.0
# v1.1.0
# v1.2.0
# v2.0.0-beta.1
# Filter by pattern
git tag -l "v1.*"
# v1.0.0
# v1.1.0
# v1.2.0
# Show tags with commit metadata
git tag -n5
# v1.0.0 Release v1.0.0
# v1.1.0 Release v1.1.0 - Add user profiles
# v1.2.0 Release v1.2.0 - Add payment processing
Pushing and Fetching Tags
# Push a specific tag
git push origin v1.2.0
# Enumerating objects: 1, done.
# Writing objects: 100% (1/1), 162 bytes
# To github.com:user/project.git
# * [new tag] v1.2.0 -> v1.2.0
# Push all tags
git push origin --tags
# Fetch tags from remote
git fetch --tags
# From github.com:user/project
# * [new tag] v1.2.0 -> v1.2.0
# * [new tag] v1.3.0 -> v1.3.0
Signing Tags with GPG
# Configure GPG signing
git config user.signingkey ABCDEF1234567890
git config tag.gpgSign true
# Create a signed tag
git tag -s v1.2.0 -m "Release v1.2.0"
# You need a passphrase to unlock the secret key for...
# Verify the signature
git tag -v v1.2.0
# object a1b2c3d4e5f6...
# type commit
# tag v1.2.0
# tagger Jane Doe <jane@example.com> 1709553000 +0000
# gpg: Signature made Mon Mar 4 14:30:00 2024 UTC
# gpg: Good signature from "Jane Doe <jane@example.com>"
Automating Releases with CI
# Example GitHub Actions workflow (.github/workflows/release.yml)
name: Create Release Tag
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Bump version and push tag
id: tag
uses: anothrNick/github-tag-action@1.67.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEFAULT_BUMP: patch
WITH_V: true
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.tag.outputs.new_tag }}
generate_release_notes: true
Deleting Tags
# Delete a local tag
git tag -d v1.0.0
# Deleted tag 'v1.0.0'
# Delete a remote tag
git push origin --delete v1.0.0
# - [deleted] v1.0.0
# Or delete with colon syntax
git push origin :refs/tags/v1.0.0
Common Pitfalls
| Mistake | Solution |
|---|---|
| Using lightweight tags for releases | Always use git tag -a for releases — annotated tags have metadata |
| Forgetting to push tags | Tags are local until git push origin <tagname> or --tags |
| Moving a tag that was already pushed | Delete and recreate — never move a published release tag |
| Tagging the wrong commit | Delete the tag and re-tag the correct commit |
Practice Questions
- What is the difference between a lightweight tag and an annotated tag?
- What does
git push origin --tagsdo? - Why should you sign release tags with GPG?
FAQs
Next Steps
After mastering tagging and release management, set up Git Hooks to automate version bumping, or learn version control strategies for Monorepo releases.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro