DaleSchool

Tags and Releases

Beginner20min

Learning Objectives

  • Understand the difference between lightweight and annotated tags
  • Understand and apply Semantic Versioning rules
  • Manage versions with git tag and push them to the remote
  • Create releases on GitHub

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.

  1. Create a repository with 3 commits.
  2. Tag the first commit with git tag -a v0.1.0 -m "alpha version", the second with v1.0.0, and the third with v1.1.0.
  3. Check tags with git tag and git log --oneline --decorate.
  4. View tag details with git show v1.0.0.
  5. Check out a specific version with git checkout v1.0.0, then return with git 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