GitCreating Tags

Creating Tags

Git gives you multiple ways to create a tag depending on what you need to mark, where in history it lives, and how much metadata you want to attach. This page covers every tagging method: lightweight, annotated, signed, on past commits, on branch tips, and batch workflows — so you have the complete picture in one place.

Overview of all tag creation methods

Method

Command

Use case

Lightweight on HEAD

git tag v1.0

Quick local marker, no metadata

Annotated on HEAD

git tag -a v1.0.0 -m "msg"

Official release with author & message

Signed (GPG) on HEAD

git tag -s v1.0.0 -m "msg"

Cryptographically verified release

On a specific commit

git tag v0.9 abc1234

Retroactive tagging of past work

On a branch tip

git tag v1.0.0 main

Tag the latest commit on a branch

From another tag

git tag v1.0.1 v1.0.0

Tag the same commit under a new name

Force overwrite

git tag -f v1.0.0

Move an existing tag to HEAD (dangerous)

Tag the current HEAD (lightweight)

The most common usage: you are on the commit you want to tag, you have verified it is the right one, and you want a simple named pointer with no extra ceremony.

Lightweight tag on current commit

Bash
# Confirm you are on the right commit
git log --oneline -1
# 7e4f1a2 (HEAD -> main) Finalize API contracts

git tag v1.0

# Verify
git tag
# v1.0

git rev-parse v1.0
# 7e4f1a2c3b4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f
Tag the current HEAD (annotated)

For any release you intend to push to a remote, use an annotated tag. The -a flag creates the tag object; -m provides the message inline.

Annotated tag on current commit

Bash
git tag -a v1.0.0 -m "Release 1.0.0"

# Multi-line message using shell quoting:
git tag -a v1.0.0 -m "Release 1.0.0

Highlights:
- New authentication flow
- Performance improvements (40% faster cold start)
- Deprecation of legacy /v0 endpoints"

# Open editor instead of inline message (omit -m):
git tag -a v1.0.0
Tag a specific commit hash

You can tag any reachable commit by providing its hash as the last argument. This is the go-to approach when you forgot to tag at release time, or when a CI/CD pipeline is tagging a commit that was built minutes ago.

Tag a commit that is not HEAD

Bash
# Find the commit in the log
git log --oneline
# 9a8b7c6 (HEAD -> main) Post-release docs update
# 5d4e3f2 Fix typo in README
# 3c2b1a0 Release commit for v0.9.0   ← tag this
# 1e0f9d8 Add CI pipeline

# Tag by abbreviated hash (lightweight)
git tag v0.9.0 3c2b1a0

# Tag by abbreviated hash (annotated)
git tag -a v0.9.0 3c2b1a0 -m "Release 0.9.0"

# Tag by full hash
git tag -a v0.9.0 3c2b1a09e8f7d6c5b4a3f2e1d0c9b8a7f6e5d4c3 -m "Release 0.9.0"

# Verify it points to the right commit
git log --oneline v0.9.0
# 3c2b1a0 Release commit for v0.9.0
Tag a branch tip

You can pass a branch name instead of a commit hash. Git resolves the branch to its current tip commit and tags that. This is useful when you want to tag the latest commit on a branch without first switching to it.

Tag the tip of a specific branch

Bash
# You are on develop, but want to tag the tip of main
git branch
# * develop
#   main

git tag -a v1.0.0 main -m "Release 1.0.0"

# Verify — should match the tip of main
git rev-parse v1.0.0^{}   # dereference tag to commit
# (equals git rev-parse main)

git rev-parse main
# same hash
Tag from another tag

You can tag the same commit that another tag already points to by using the existing tag name as the reference. This is occasionally useful when creating an alias (e.g., both v1.0 and v1.0.0 should point to the same release commit).

Create a tag based on another tag

Bash
# v1.0.0 already exists
git tag
# v1.0.0

# Create v1.0 pointing to the same commit
git tag v1.0 v1.0.0

# Verify both point to the same commit
git rev-parse v1.0^{}
git rev-parse v1.0.0^{}
# (both output the same commit SHA)
GPG-signed tags

Create a cryptographically signed annotated tag

Bash
# Prerequisite: GPG key configured
git config --global user.signingkey <your-key-id>

# Use -s instead of -a
git tag -s v1.0.0 -m "Release 1.0.0 (signed release)"

# Verify the signature
git tag -v v1.0.0
# gpg: Signature made ...
# gpg: Good signature from "Alice <alice@example.com>"
Batch tagging workflow

In some projects you might need to apply multiple tags at once — for example, tagging a monorepo where multiple packages have independent version numbers, or backfilling tags for historical commits.

Batch tagging multiple packages in a monorepo

Bash
# Tag each package's release commit individually
git tag -a api@1.2.0   abc1234 -m "api: release 1.2.0"
git tag -a ui@3.0.0    def5678 -m "ui: release 3.0.0"
git tag -a shared@0.5.1 ghi9012 -m "shared: release 0.5.1"

# List to confirm
git tag
# api@1.2.0
# shared@0.5.1
# ui@3.0.0

Batch tagging from a script

Bash
#!/bin/bash
# Script: retag-history.sh
# Reads a CSV of "hash,tagname,message" and creates annotated tags

while IFS=, read -r hash tagname message; do
  git tag -a "$tagname" "$hash" -m "$message"
  echo "Tagged $hash as $tagname"
done < releases.csv

# releases.csv example:
# 3c2b1a0,v0.1.0,First internal release
# 7e4f1a2,v0.2.0,Beta release
# 9a8b7c6,v0.3.0,Public preview
Cannot create duplicate tag names
Duplicate tag names are an error by default
Git will refuse to create a tag if a tag with the same name already exists. You must either delete the existing tag first, or use the force flag `-f`. Be very careful with `-f` on tags that have already been pushed to a remote — other developers may have fetched the old tag and your force-move will create an inconsistency.

Handling duplicate tag errors

Bash
# Attempting to create a tag that already exists:
git tag -a v1.0.0 -m "..."
# fatal: tag 'v1.0.0' already exists

# Option 1: delete and recreate (safe if not yet pushed)
git tag -d v1.0.0
git tag -a v1.0.0 -m "Release 1.0.0 (corrected)"

# Option 2: force overwrite
git tag -f -a v1.0.0 -m "Release 1.0.0 (corrected)"
# Updated tag 'v1.0.0' (was 3f8a2c9)

# If already pushed to remote, also update there:
git push origin --delete v1.0.0
git push origin v1.0.0
Verifying your tag before pushing

Checklist before pushing a release tag

Bash
# 1. Confirm the tag name is correct
git tag | grep v1

# 2. Confirm it points to the intended commit
git log --oneline v1.0.0 -1
# (should be your release commit, not a random commit)

# 3. For annotated tags, review the full metadata
git show v1.0.0
# (review tagger name, date, message)

# 4. Check that the working tree was clean when you tagged
# (no dirty files sneaked into the release snapshot)
git status

# 5. Push the tag
git push origin v1.0.0
Tags do not automatically follow branch moves
Once created, a tag is frozen. If you continue committing on a branch after tagging, the branch pointer advances but the tag stays where it was. This is the expected and desired behaviour — the tag marks a specific historical moment.
Standardise your tag naming convention
Agree on a naming convention with your team before you start tagging. Common choices are `v1.0.0` (SemVer with v-prefix), `release-2024-11-15` (date-based), or `pkg-name@1.0.0` (monorepo packages). Consistency makes listing, sorting, and automation much easier.