Tags, Releases & Semantic Versioning
Versioning samjho, toh software ka itihaas samajh aa gaya.
Git tags are named references to specific commits — like bookmarks that never move. When you mark a commit as v1.0.0, that tag permanently points to that exact commit, even as the branch continues to grow with new commits.
Unlike branches, tags are IMMUTABLE — once created, they always point to the same commit. Branches move forward with each new commit; tags stay fixed forever.
Two types of tags:
- Lightweight tag: just a pointer (like a branch that doesn't move). No metadata — just the commit hash.
- Annotated tag: stores metadata (tagger name, date, message, GPG signature) — RECOMMENDED for releases.
Tags are used to mark release points: v1.0.0, v2.1.3, etc. They give human-readable names to specific commits in your project's history.
Important: tags are NOT pushed by default with git push. You must explicitly push them to the remote.
# Create a lightweight tag (just a pointer)
git tag v1.0.0
# Create an annotated tag (RECOMMENDED — has metadata)
git tag -a v1.0.0 -m "Release 1.0.0: First public release"
# List tags
git tag
git tag -l "v1.*"
# See tag details
git show v1.0.0
# Tagger: Sai <sai@devinhyd.com>
# Date: Mon Jan 15 10:00:00 2024 +0530
# Release 1.0.0: First public release
# Tag a specific past commit
git tag -a v0.9.0 abc1234 -m "Pre-release beta"
# Delete a tag
git tag -d v1.0.0 # local
git push origin --delete v1.0.0 # remote
# Push tags to remote
git push origin v1.0.0 # push specific tag
git push origin --tags # push ALL tags
git tag -a). The Git documentation itself recommends annotated tags because they contain additional information.Semantic Versioning (SemVer) is a versioning system that gives meaning to version numbers: MAJOR.MINOR.PATCH (e.g., 2.1.3).
Each number has a specific meaning:
- MAJOR version (2): incompatible API changes (breaking changes). Users must update their code.
- MINOR version (1): new backward-compatible features. Users can upgrade safely.
- PATCH version (3): backward-compatible bug fixes. Users should always upgrade.
The rules of SemVer:
- Initial development:
0.x.x— anything can change at any time. - Version
1.0.0: the public API is now defined. This is your first stable release. - Patch: increment for bug fixes (
2.1.3→2.1.4) - Minor: increment for new features, reset patch (
2.1.4→2.2.0) - Major: increment for breaking changes, reset minor+patch (
2.2.0→3.0.0)
Pre-release versions use hyphens: 1.0.0-alpha.1, 1.0.0-beta.2, 1.0.0-rc.1
Build metadata uses plus: 1.0.0+build.123 (ignored for version precedence)
# Semantic Versioning examples
# 0.1.0 → Initial development (unstable API)
# 0.2.0 → New feature, still unstable
# 1.0.0 → First stable release! Public API defined.
# 1.0.1 → Bug fix (PATCH)
# 1.1.0 → New feature (MINOR)
# 1.1.1 → Another bug fix (PATCH)
# 2.0.0 → Breaking change! (MAJOR)
# 2.0.1 → Bug fix for the breaking change (PATCH)
# 2.1.0 → New feature (MINOR)
# 3.0.0-beta.1 → Pre-release for next major
# Conventional Commits + SemVer mapping:
# feat: → MINOR bump (1.1.0 → 1.2.0)
# fix: → PATCH bump (1.2.0 → 1.2.1)
# feat!: or BREAKING CHANGE → MAJOR bump (1.2.1 → 2.0.0)
# chore, docs, test, ci → NO bump (no release needed)
A release is a tag + release notes + (optionally) binary artifacts. GitHub Releases are built on top of Git tags — they add a UI, release notes, and downloadable assets.
The release workflow:
- 1) Update version in code (package.json, VERSION file)
- 2) Commit the version change
- 3) Create an annotated tag
- 4) Push the tag to remote
- 5) Create a GitHub Release from the tag
Pre-release tags: v2.0.0-alpha.1, v2.0.0-beta.1, v2.0.0-rc.1 — these are NOT considered stable. Package managers won't install them by default.
Tag naming convention: always use the v prefix (v1.0.0 not 1.0.0) — this is the industry standard.
# Manual release workflow
# Step 1: Update version in package.json / version file
npm version patch # 1.0.0 → 1.0.1 (bug fix)
npm version minor # 1.0.1 → 1.1.0 (new feature)
npm version major # 1.1.0 → 2.0.0 (breaking change)
# This automatically:
# - Updates package.json version
# - Creates a git commit ("1.1.0")
# - Creates a git tag (v1.1.0)
# Step 2: Push commit + tag
git push --follow-tags origin main
# Step 3: Create GitHub Release
gh release create v1.1.0 \
--title "v1.1.0 - New Analytics Dashboard" \
--notes "## What's New
- Real-time analytics dashboard
- Export to CSV feature
## Bug Fixes
- Fixed timeout on slow networks
- Resolved null pointer in payment module
**Full Changelog**: https://github.com/user/repo/compare/v1.0.0...v1.1.0"
# Step 4: Attach release assets (optional)
gh release upload v1.1.0 ./dist/app.zip ./dist/checksums.txtPre-release versions allow testing before the stable release. They follow a clear progression: alpha → beta → rc → stable.
- Alpha (
v2.0.0-alpha.1): internal testing, features may be incomplete, bugs expected. Don't share with external users. - Beta (
v2.0.0-beta.1): external testing, features complete, some bugs remain. Share with a limited group of testers. - RC — Release Candidate (
v2.0.0-rc.1): final testing, expected to be the stable release unless critical bugs found. Nearly production-ready.
Pre-release precedence: alpha < beta < rc < stable (1.0.0-alpha < 1.0.0-beta < 1.0.0)
Package managers (npm, pip) don't install pre-releases by default — users must explicitly opt in.
GitHub Releases can be marked as "Pre-release" to warn users that this version is not stable.
# Pre-release workflow
# Alpha release (internal testing)
git tag -a v2.0.0-alpha.1 -m "Alpha: new API v2"
git push origin v2.0.0-alpha.1
gh release create v2.0.0-alpha.1 --prerelease \
--title "v2.0.0-alpha.1" \
--notes "Pre-release for internal testing only"
# Beta release (external testing)
git tag -a v2.0.0-beta.1 -m "Beta: API v2 feature-complete"
git push origin v2.0.0-beta.1
gh release create v2.0.0-beta.1 --prerelease
# Release Candidate (final testing)
git tag -a v2.0.0-rc.1 -m "RC: final testing before stable"
git push origin v2.0.0-rc.1
gh release create v2.0.0-rc.1 --prerelease
# Stable release
git tag -a v2.0.0 -m "Release 2.0.0: New API"
git push origin v2.0.0
gh release create v2.0.0 \
--title "v2.0.0 - New API" \
--notes "## Breaking Changes
- API responses now use JSON instead of XML
- Authentication endpoint changed"
# Install pre-release (npm)
npm install package@2.0.0-beta.1 # explicit
npm install package@latest # WON'T install pre-release
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-rc.1 < 1.0.0. Package managers use this to decide which version to install. npm install package@latest will skip all pre-releases and give you the latest stable. This protects production systems from accidentally running unstable code.standard-version automates the entire release process using conventional commits. No more manual version bumps or changelogs.
What it does:
- 1) Analyzes commits since last tag
- 2) Determines version bump (feat→minor, fix→patch, breaking→major)
- 3) Updates CHANGELOG.md with categorized changes
- 4) Bumps version in package.json
- 5) Creates a git commit with the version bump
- 6) Creates an annotated git tag
Usage: npx standard-version — patch/feat/breaking determines the bump automatically.
npx standard-version --release-as minor — force a minor bump
npx standard-version --first-release — for the first release (no version bump, just changelog)
# Automated release with standard-version
# Install
npm install --save-dev standard-version
# Add script to package.json
# "scripts": { "release": "standard-version" }
# Run it (analyzes commits since last tag)
npm run release
# Outputs:
# ✔ bumping version in package.json from 1.1.0 to 1.2.0
# ✔ outputting changes to CHANGELOG.md
# ✔ committing package.json and CHANGELOG.md
# ✔ tagging release v1.2.0
# Push commit + tag
git push --follow-tags origin main
# GitHub Release from tag
gh release create v1.2.0 --generate-notes
# First release
npx standard-version --first-release
# Force specific bump
npx standard-version --release-as major # 1.2.0 → 2.0.0
npx standard-version --release-as minor # 1.2.3 → 1.3.0
npx standard-version --release-as patch # 1.2.3 → 1.2.4
# Dry run (see what would happen)
npx standard-version --dry-run
# Full CI automation (GitHub Actions)
# .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # need full history
- run: npm ci
- run: npm test
- run: npx standard-version
- run: git push --follow-tags origin main
release script to package.json: "release": "standard-version". Then run npm run release before pushing. This ensures consistent version bumps and changelogs without manual intervention. Never edit CHANGELOG.md manually — let the tool generate it. Your commits ARE your changelog.Lo kar liya — Key Points:
- ✅ Git tags are immutable named references to specific commits — unlike branches, they never move
- ✅ Annotated tags (
git tag -a) store metadata and are recommended over lightweight tags - ✅ Semantic Versioning (SemVer) uses MAJOR.MINOR.PATCH format (2.1.3)
- ✅ MAJOR = breaking changes, MINOR = new features, PATCH = bug fixes
- ✅ Tags are NOT pushed by default — use
git push --tagsorgit push origin <tag> - ✅ Pre-release versions (alpha, beta, rc) allow testing before stable release
- ✅
standard-versionautomates version bump + changelog + tag creation from conventional commits - ✅ Never delete or modify published tags — other systems and users depend on them
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login