Working Code
Example 1: Creating tags
git init
echo "v1 code" > app.py
git add app.py
git commit -m "feat: initial app implementation"
# Lightweight tag (simple pointer)
git tag v1.0.0
git tag
Output:
v1.0.0
# Annotated tag (recommended, includes metadata)
git tag -a v1.0.0 -m "First official release"
git tag
Output:
v1.0.0
View tag details:
git show v1.0.0
Output:
tag v1.0.0
Tagger: John Doe <john@example.com>
Date: Mon Jan 15 10:30:00 2024 +0900
First official release
commit a1b2c3d (HEAD -> main, tag: v1.0.0)
Author: John Doe <john@example.com>
...
Example 2: Listing tags
echo "v2 code" >> app.py && git add app.py && git commit -m "feat: add feature"
git tag -a v1.1.0 -m "Bug fixes and new features"
echo "v3 code" >> app.py && git add app.py && git commit -m "feat: major update"
git tag -a v2.0.0 -m "Major update, breaking changes"
git tag
Output:
v1.0.0
v1.1.0
v2.0.0
# Filter by pattern
git tag -l "v1.*"
Output:
v1.0.0
v1.1.0
Example 3: Tags in the log
git log --oneline --decorate
Output:
e4f5g6h (HEAD -> main, tag: v2.0.0) feat: major update
b2c3d4e (tag: v1.1.0) feat: add feature
a1b2c3d (tag: v1.0.0) feat: initial app implementation
Try It Yourself
Semantic Versioning (SemVer)
Version numbers follow the MAJOR.MINOR.PATCH format:
v2.1.3
| | +-- PATCH: Bug fix (backward compatible)
| +---- MINOR: New feature (backward compatible)
+------ MAJOR: Breaking change (not backward compatible)
When to bump which version?
| Change Type | Example | Version Change |
| --------------- | ------------------ | ------------------ |
| Bug fix | Fix login bug | 1.0.0 -> 1.0.1 |
| New feature | Add search feature | 1.0.1 -> 1.1.0 |
| Breaking change | Restructure API | 1.1.0 -> 2.0.0 |
PATCH resets to 0:
New feature: 1.2.3 -> 1.3.0 (MINOR up, PATCH resets to 0)
Breaking: 1.3.0 -> 2.0.0 (MAJOR up, MINOR and PATCH reset to 0)
Tagging a previous commit
git log --oneline
# e4f5g6h current commit
# b2c3d4e previous commit (want to tag this one)
git tag -a v1.1.0 b2c3d4e -m "v1.1.0 release"
git log --oneline --decorate
Pushing tags to the remote
Tags are not pushed by default:
# Push a specific tag
git push origin v1.0.0
# Push all tags
git push origin --tags
# Delete a tag
git tag -d v1.0.0 # Delete locally
git push origin --delete v1.0.0 # Delete on remote
Creating a GitHub release
# Create a release with GitHub CLI
gh release create v1.0.0 \
--title "v1.0.0 - First Official Release" \
--notes "## Changes
### New Features
- Login/logout functionality
- User profile page
### Bug Fixes
- Fixed session expiration error
### Notes
- Requires Node.js 18 or higher"
A GitHub release bundles a tag + description + file attachments. Users check release notes to decide whether to upgrade.
"Why?" -- Why tags and releases matter
Without tags:
Commit a1b2c3d -> "Was this v1.0?"
Commit e4f5g6h -> "When did we deploy v2.0?"
With tags:
# View v1.0.0 code
git show v1.0.0
# Check out v1.0.0
git checkout v1.0.0
# Compare v1.0.0 and v2.0.0
git diff v1.0.0 v2.0.0
# Create a hotfix branch from v1.0.0
git checkout -b hotfix/1.0.1 v1.0.0
Lightweight vs annotated tags
| | Lightweight | Annotated |
| ----------- | -------------------- | ------------------------------ |
| Stored data | Commit pointer only | Tagger, date, message |
| Command | git tag v1.0 | git tag -a v1.0 -m "message" |
| Signing | Not possible | Possible (-s) |
| Best for | Temporary, local use | Official releases |
Use annotated tags for official releases.
Common Mistakes
Mistake 1: Forgetting to push tags
git tag -a v1.0.0 -m "release"
git push # Tags are NOT pushed!
# Fix
git push --tags # or git push origin v1.0.0
Mistake 2: Tagging the wrong commit
# If you tagged the wrong commit
git tag -d v1.0.0 # Delete locally
git tag -a v1.0.0 correct-hash # Recreate on the correct commit
git push origin --delete v1.0.0 # Delete on remote
git push origin v1.0.0 # Push new tag
Mistake 3: Inconsistent version format
# Bad
v1.0
1.0.0
V1.0.0
version-1
# Good: always use the same format
v1.0.0
v1.1.0
v2.0.0
Agree on a version format with your team upfront.
Deep Dive
Signed tags (GPG)
# Create a signed annotated tag (requires GPG setup)
git tag -s v1.0.0 -m "Signed release"
# Verify the signature
git tag -v v1.0.0
Used in open-source projects to guarantee the integrity of distribution files.
Attaching files to a release
# Attach build artifacts to a release
npm run build # Generates dist/
gh release create v1.0.0 \
./dist/app-v1.0.0-linux.tar.gz \
./dist/app-v1.0.0-macos.tar.gz \
--title "v1.0.0" \
--notes "Release notes..."
Maintaining a CHANGELOG.md
# CHANGELOG
## [2.0.0] - 2024-01-15
### Added
- New feature description
### Changed
- Changed feature description (breaking)
### Fixed
- Fixed bug description
## [1.1.0] - 2024-01-01
### Added
- Another feature
Following the keepachangelog.com format is recommended.
- Create a repository with 3 commits.
- Tag the first commit with
git tag -a v0.1.0 -m "alpha version", the second withv1.0.0, and the third withv1.1.0. - Check tags with
git tagandgit log --oneline --decorate. - View tag details with
git show v1.0.0. - Check out a specific version with
git checkout v1.0.0, then return withgit checkout main.
Q1. When a version changes from 1.2.3 to 1.3.0 in Semantic Versioning, what does it mean?
- A) Only bug fixes were applied
- B) There were breaking changes
- C) A new feature was added while maintaining backward compatibility
- D) Only a security patch was applied