Tags and Releases

While branches in Git are dynamic pointers that move forward as new commits are added, tags are static. A tag is a snapshot of a specific point in history, usually used to mark release versions (e.g., v1.0.0, v2.4.1-beta).


Visualizing Tags vs. Branches

Unlike a branch pointer that follows you as you commit, a tag stays exactly where you put it.

a1b2cd3e4fg5h6ij7k8lv1.0.0mainTags are anchors; Branches are explorers.


Semantic Versioning (SemVer)

Most professional projects follow Semantic Versioning, which uses a three-part number format: vMAJOR.MINOR.PATCH.

  • MAJOR: Incremented for incompatible API changes (breaking changes).
  • MINOR: Incremented for adding functionality in a backwards-compatible manner.
  • PATCH: Incremented for backwards-compatible bug fixes.

Example: v2.3.1 means Version 2, Minor update 3, Patch 1.


Types of Tags in Git

1. Lightweight Tags

A lightweight tag is just a pointer to a specific commit. It doesn't store any extra information like who created it or when. Think of it like a bookmark.

# Create a lightweight tag
git tag v1.0-beta

2. Annotated Tags (Recommended)

Annotated tags are stored as full objects in the Git database. They include the tagger name, email, date, and a message.

# Create an annotated tag with a message
git tag -a v1.0.0 -m "First stable release"

3. Signed Tags (Advanced)

For high-security projects, you can sign your tags with GPG (GNU Privacy Guard) to prove that the release actually came from you.

# Create a signed tag (-s)
git tag -s v1.0.0 -m "Verified release"

Working with Tags

Listing and Filtering Tags

git tag                # List all tags
git tag -l "v1.*"      # List tags matching a pattern
git show v1.0.0        # View tag details (especially for annotated tags)

Tagging Historical Commits

You don't have to be on the commit you want to tag. You can tag any commit from the past by providing its checksum (or a part of it).

# Tag a commit from 3 days ago
git tag -a v0.9.0 9fceb02 -m "Missed tagging this yesterday"

Pushing Tags

Tags are not pushed to the remote server by default when you run git push.

git push origin v1.0.0    # Push a specific tag
git push origin --tags     # Push all local tags

Switching to a Tag

Checking out a tag puts you in a Detached HEAD state, meaning any new commits you make won't belong to any branch.

git checkout v1.0.0

To make changes starting from a tag, always create a new branch:

git checkout -b fix-v1-bug v1.0.0

Advanced Tag Management

Verifying Signed Tags

If you receive a repository with signed tags, you can verify their authenticity using the -v flag.

# Verify a signed tag
git tag -v v1.0.0

Moving or Replacing a Tag

Technically, you can "move" a tag to a different commit using the -f (force) flag.

# Force a tag to point to a different commit
git tag -a -f v1.0.0 4b2c1d3 -m "Corrected release point"

⚠️ WARNING: Moving a tag that has already been pushed to a remote server is extremely dangerous. It can cause major confusion for other developers whose local copies still point to the old version. Only do this if you are absolutely sure and can coordinate with your entire team.


Git Tags vs. Platform Releases

It's important to distinguish between a Git Tag and a GitHub/GitLab Release:

  1. Git Tag: A technical pointer within the Git repository itself. It exists regardless of whether you use a hosting platform.
  2. Platform Release: A "wrapper" around a Git tag provided by services like GitHub. It allows you to:
    • Add a title and formatted release notes (Markdown).
    • Attach binary assets (e.g., .exe, .dmg, .zip).
    • Mark a release as a "Pre-release" or "Draft".

Tags in the Release Lifecycle

In modern DevOps, tags are the "Go" signal for deployment:

  1. Tagging: A developer creates v2.0.0 on the main branch.
  2. CI/CD Trigger: GitHub Actions or GitLab CI detects the new tag.
  3. Build: The system runs tests and compiles the code.
  4. Release: The system automatically creates a "Release" on GitHub/GitLab, attaches binaries, and deploys to production.

Pro Tip: Treat tags as permanent records. If a release has a bug, don't move the tag—instead, create a new patch release (e.g., v1.0.1).