Skip to content

tag Git Command Guide

The git tag command creates, lists, deletes, and verifies tags in a Git repository. Tags are references that point to specific commits and are commonly used to mark release points, important milestones, or specific versions in project history.

Terminal window
git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]
<tagname> [<commit> | <object>]
git tag -d <tagname>...
git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
[--points-at <object>] [--column[=<options>]] [--create-reflog]
[--sort=<key>] [--format=<format>] [--merged <commit>]
[--no-merged <commit>] [<pattern>...]
git tag -v [--format=<format>] <tagname>...
OptionDescription
-a, --annotateCreate annotated tag with message
-s, --signCreate GPG-signed tag
-u <key-id>, --local-user=<key-id>Use specific GPG key
-f, --forceReplace existing tag
-m <msg>, --message=<msg>Tag message
-F <file>, --file=<file>Read message from file
-e, --editEdit tag message
OptionDescription
-l, --listList tags matching pattern
-n[<num>], --list <num>Show tag messages
--column[=<options>]Display in columns
--sort=<key>Sort tags by key
--format=<format>Custom output format
--contains <commit>Show tags containing commit
--no-contains <commit>Show tags not containing commit
--points-at <object>Show tags pointing to object
--merged <commit>Show tags merged to commit
--no-merged <commit>Show tags not merged to commit
OptionDescription
-d, --deleteDelete tags
-v, --verifyVerify GPG signatures
ParameterDescription
<tagname>Name for the new tag
<commit>Commit to tag (default: HEAD)
<object>Object to tag
<pattern>Pattern to match tags
Tag Type Comparison:
├── Lightweight Tag: Simple pointer to commit
│ ├── No metadata stored
│ ├── Just a name pointing to commit
│ ├── Cannot have messages
│ └── git tag <name> [<commit>]
└── Annotated Tag: Full Git object with metadata
├── Stored as full objects in .git/objects
├── Contains tagger, date, message
├── Can be GPG signed
└── git tag -a <name> [<commit>]
Repository Tag Storage:
├── .git/refs/tags/ = Lightweight tags (files)
├── .git/objects/ = Annotated tags (objects)
├── refs/tags/v1.0 -> commit-hash (lightweight)
└── refs/tags/v1.0 -> tag-object-hash -> commit-hash (annotated)
Tag Resolution Chain:
Tag Reference (refs/tags/v1.0)
├── Lightweight: Direct commit hash
└── Annotated: Tag object containing:
├── Object type: commit/tree/blob/tag
├── Object hash: Target object
├── Tagger: Name and email
├── Date: Creation timestamp
├── Message: Tag description
└── GPG signature (optional)
Terminal window
# Create lightweight tag on current commit
git tag v1.0
# Create annotated tag with message
git tag -a v1.0 -m "Release version 1.0"
# Create tag on specific commit
git tag -a v0.9 abc123
# Create tag with multi-line message
git tag -a v1.1 -F release-notes.txt
# Force replace existing tag
git tag -f v1.0 new-commit-hash
Terminal window
# List all tags
git tag
# List tags with messages
git tag -n
# List tags matching pattern
git tag -l "v1.*"
# List tags in columns
git tag --column
# Sort tags by version
git tag --sort=version:refname
Terminal window
# Create GPG signed tag
git tag -s v1.0 -m "Signed release"
# Create signed tag with specific key
git tag -u ABC123 v1.0 -m "Signed with specific key"
# Verify signed tag
git tag -v v1.0
Terminal window
# Delete local tag
git tag -d v1.0
# Delete multiple tags
git tag -d v1.0 v1.1 v2.0
# Delete remote tags
git push origin --delete v1.0
# Rename tag (delete and recreate)
git tag new-name old-name
git tag -d old-name
Terminal window
# Find tags containing specific commit
git tag --contains abc123
# Find tags pointing to specific object
git tag --points-at HEAD
# Find tags merged into branch
git tag --merged main
# Find tags not merged
git tag --no-merged develop
Terminal window
# Push specific tag to remote
git push origin v1.0
# Push all tags to remote
git push origin --tags
# Fetch tags from remote
git fetch --tags
# List remote tags
git ls-remote --tags origin
Terminal window
# Show tag details
git show v1.0
# Show tag object information
git cat-file -p v1.0
# Show tag commit information
git show --format=fuller v1.0
Terminal window
# Configure tag behavior
git config tag.sort version:refname # Sort tags by version
git config tag.forceSignAnnotated true # Sign all annotated tags
# Configure GPG signing
git config user.signingkey ABC123 # Default GPG key
git config gpg.program gpg2 # GPG program to use
# Configure tagger information
git config user.name "Your Name"
git config user.email "your.email@example.com"
Terminal window
# Use semantic versioning
git tag -a v1.2.3 -m "Release v1.2.3"
# Include release notes in tag messages
git tag -a v2.0.0 -F CHANGELOG.md
# Sign important tags
git tag -s v1.0.0 -m "Major release"
# Use descriptive tag names
git tag -a release/2023-q4 v3.1.0
# Document tag purposes
git tag -a milestone/phase-1-complete abc123
Terminal window
# Verify tag before operations
git tag -v tag-name
# Check tag existence
git show-ref --tags | grep tag-name
# Backup before deleting tags
git tag backup-tag-$(date +%Y%m%d)
# Verify remote operations
git ls-remote --tags origin | grep tag-name
#!/bin/bash
# Release tagging workflow
create_release_tag() {
local version="$1"
local release_notes="${2:-RELEASE_NOTES.md}"
echo "Creating release tag v$version..."
# Validate version format
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Invalid version format. Use: major.minor.patch"
return 1
fi
# Check if tag already exists
if git tag -l | grep -q "^v$version$"; then
echo "Tag v$version already exists"
return 1
fi
# Create tag message
tag_message="Release v$version
$(cat "$release_notes" 2>/dev/null || echo "Release notes not found")"
# Create signed tag
git tag -s "v$version" -m "$tag_message"
# Push tag to remote
git push origin "v$version"
echo "Release tag v$version created and pushed"
}
create_release_tag "1.2.3" "CHANGELOG.md"
Terminal window
# Automated version tagging
automated_version_tag() {
echo "Creating automated version tag..."
# Get current version from package.json or similar
current_version=$(node -p "require('./package.json').version" 2>/dev/null || echo "0.0.0")
# Increment patch version
IFS='.' read -ra VERSION_PARTS <<< "$current_version"
((VERSION_PARTS[2]++))
new_version="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${VERSION_PARTS[2]}"
# Create tag
git tag -a "v$new_version" -m "Automated release v$new_version
Generated: $(date)
Previous: v$current_version"
echo "Version tag v$new_version created"
}
automated_version_tag
Terminal window
# Clean up old tags
cleanup_old_tags() {
local keep_versions="${1:-10}"
echo "Cleaning up old tags, keeping $keep_versions versions..."
# Get all version tags, sort by version
version_tags=$(git tag -l "v*" | sort -V | head -n -"$keep_versions")
if [ -z "$version_tags" ]; then
echo "No old tags to clean up"
return 0
fi
echo "Tags to be deleted:"
echo "$version_tags"
# Delete local tags
echo "$version_tags" | xargs git tag -d
# Delete remote tags
echo "$version_tags" | xargs -I {} git push origin --delete {}
echo "Tag cleanup complete"
}
cleanup_old_tags 5
Terminal window
# Handle existing tag
git tag -f v1.0 # Force replace
# or
git tag -d v1.0 # Delete first
git tag v1.0
# Fix tag message
git tag -d v1.0
git tag -a v1.0 -m "Correct message"
# Tag wrong commit
git tag -d v1.0
git tag v1.0 correct-commit-hash
Terminal window
# Check GPG setup
gpg --list-keys
# Configure GPG key
git config user.signingkey ABC123
# Test signing
echo "test" | gpg --clearsign
# Create signed tag
git tag -s v1.0 -m "Signed tag"
Terminal window
# Sync tags with remote
git fetch --tags
# Push all tags
git push origin --tags
# Push specific tag
git push origin v1.0
# Check remote tags
git ls-remote --tags origin
Terminal window
# Delete local and remote tags
git tag -d v1.0
git push origin --delete v1.0
# Handle protected tags
# May need repository admin access
# Recover accidentally deleted tag
git fsck --unreachable | grep tag
git update-ref refs/tags/v1.0 <tag-object-hash>
Terminal window
# Show all tags with details
git tag -n --format="%(refname:short) %(subject) %(creator)"
# Filter tags by date
git tag --format="%(creatordate) %(refname:short)" | sort
# Find tags by author
git tag --format="%(tagger) %(refname:short)" | grep "Author Name"
#!/bin/bash
# Complete release workflow with tagging
release_workflow() {
local version="$1"
local branch="${2:-main}"
echo "=== Release Workflow: v$version ==="
# Switch to release branch
git checkout "$branch"
git pull origin "$branch"
# Run tests
if ! run_ci_tests; then
echo "Tests failed - aborting release"
exit 1
fi
# Update version files
update_version_files "$version"
# Commit version changes
git add .
git commit -m "Release v$version
- Updated version numbers
- Release notes included"
# Create annotated tag
git tag -a "v$version" -m "Release v$version
## Changes
$(git log --oneline --no-merges HEAD~10..HEAD)
## Testing
- Unit tests: ✓
- Integration tests: ✓
- Performance tests: ✓
## Deployment
Ready for production deployment"
# Push changes and tag
git push origin "$branch"
git push origin "v$version"
# Create GitHub release (if applicable)
create_github_release "v$version"
echo "Release v$version completed successfully"
}
release_workflow "2.1.0" "main"
Terminal window
# Semantic versioning with tags
semantic_versioning() {
echo "=== Semantic Versioning Management ==="
# Get current version
current_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
current_version="${current_tag#v}"
# Parse version components
IFS='.' read -ra VERSION <<< "$current_version"
major="${VERSION[0]}"
minor="${VERSION[1]}"
patch="${VERSION[2]}"
# Determine next version type
echo "Current version: $current_version"
echo "Select version bump:"
echo "1. Patch ($major.$minor.$((patch + 1)))"
echo "2. Minor ($major.$((minor + 1)).0)"
echo "3. Major ($((major + 1)).0.0)"
read -p "Choice (1-3): " choice
case "$choice" in
1) new_version="$major.$minor.$((patch + 1))" ;;
2) new_version="$major.$((minor + 1)).0" ;;
3) new_version="$((major + 1)).0.0" ;;
*) echo "Invalid choice"; return 1 ;;
esac
# Create new tag
git tag -a "v$new_version" -m "Version $new_version
Semantic version bump from $current_version
## Changes
$(git log --oneline --no-merges "$current_tag..HEAD")"
# Push tag
git push origin "v$new_version"
echo "Version bumped to v$new_version"
}
semantic_versioning
Terminal window
# Tag-based deployment system
tag_deployment() {
local environment="$1"
local tag_pattern="${2:-v*}"
echo "=== Tag-Based Deployment: $environment ==="
# Find latest tag matching pattern
latest_tag=$(git tag -l "$tag_pattern" | sort -V | tail -1)
if [ -z "$latest_tag" ]; then
echo "No tags found matching pattern: $tag_pattern"
return 1
fi
echo "Latest tag: $latest_tag"
# Validate tag
if ! git tag -v "$latest_tag" >/dev/null 2>&1; then
echo "Warning: Tag $latest_tag is not signed"
fi
# Check deployment readiness
tag_commit=$(git rev-parse "$latest_tag")
deploy_commit=$(git rev-parse "origin/$environment" 2>/dev/null || echo "")
if [ "$tag_commit" = "$deploy_commit" ]; then
echo "Environment $environment is already at $latest_tag"
return 0
fi
# Deploy to environment
case "$environment" in
staging)
deploy_to_staging "$latest_tag"
;;
production)
# Extra validation for production
if ! production_checks "$latest_tag"; then
echo "Production checks failed"
exit 1
fi
deploy_to_production "$latest_tag"
;;
*)
echo "Unknown environment: $environment"
return 1
;;
esac
# Update environment branch/tag
git push origin "$latest_tag:refs/heads/$environment"
echo "Deployment to $environment completed: $latest_tag"
}
tag_deployment "staging" "v1.*"
Terminal window
# Tag analytics and reporting
tag_analytics() {
echo "=== Tag Analytics Report ==="
# Tag statistics
total_tags=$(git tag | wc -l)
annotated_tags=$(git tag -l | xargs -n1 git cat-file -t 2>/dev/null | grep -c tag)
lightweight_tags=$((total_tags - annotated_tags))
echo "Tag Statistics:"
echo " Total tags: $total_tags"
echo " Annotated tags: $annotated_tags"
echo " Lightweight tags: $lightweight_tags"
# Tag frequency by time
echo ""
echo "Tag Creation Timeline:"
git tag --format="%(creatordate:format:%Y-%m) %(refname:short)" | sort | uniq -c | sort -k2
# Most active taggers
echo ""
echo "Most Active Taggers:"
git tag --format="%(taggername)" | sort | uniq -c | sort -nr | head -5
# Tag patterns analysis
echo ""
echo "Tag Pattern Analysis:"
echo "Version tags: $(git tag -l "v*" | wc -l)"
echo "Release tags: $(git tag -l "release*" | wc -l)"
echo "Hotfix tags: $(git tag -l "hotfix*" | wc -l)"
echo "Milestone tags: $(git tag -l "milestone*" | wc -l)"
# Recent tags
echo ""
echo "Recent Tags:"
git tag --sort=-creatordate --format="%(creatordate:relative) %(refname:short) - %(subject)" | head -10
# Tag gaps analysis
echo ""
echo "Version Gaps Analysis:"
version_tags=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" | sort -V)
if [ -n "$version_tags" ]; then
echo "Version progression:"
echo "$version_tags" | while read -r tag; do
commit_date=$(git tag --format="%(creatordate:format:%Y-%m-%d)" "$tag")
echo " $tag - $commit_date"
done
fi
echo "Analytics complete"
}
tag_analytics
Terminal window
# Collaborative tagging with approval
collaborative_tagging() {
local tag_name="$1"
local tag_message="$2"
local approvers="$3"
echo "=== Collaborative Tagging: $tag_name ==="
# Validate tag doesn't exist
if git tag -l | grep -q "^$tag_name$"; then
echo "Tag $tag_name already exists"
return 1
fi
# Create tag with approval metadata
full_message="$tag_message
## Approval Information
Approved by: $approvers
Approval date: $(date)
Approval method: Collaborative review
## Tag Details
Created by: $(git config user.name) <$(git config user.email)>
Created at: $(date)
Target commit: $(git rev-parse HEAD)
"
# Create signed tag
git tag -s "$tag_name" -m "$full_message"
# Verify tag
if git tag -v "$tag_name" >/dev/null 2>&1; then
echo "✓ Tag $tag_name created and signed"
else
echo "✗ Tag signing failed"
git tag -d "$tag_name"
return 1
fi
# Push tag
git push origin "$tag_name"
# Notify collaborators
echo "Tag $tag_name created and pushed"
echo "Approved by: $approvers"
# Create release notes if applicable
if [[ "$tag_name" =~ ^v[0-9] ]]; then
create_release_notes "$tag_name"
fi
}
collaborative_tagging "v2.0.0" "Major release with new features" "alice@example.com, bob@example.com"

What’s the difference between lightweight and annotated tags?

Section titled “What’s the difference between lightweight and annotated tags?”

Lightweight tags are simple pointers to commits; annotated tags are full Git objects with metadata, messages, and optional GPG signatures.

How do I create a tag for a specific commit?

Section titled “How do I create a tag for a specific commit?”

git tag -a -m “Tag message” creates an annotated tag pointing to the specified commit.

Tags are immutable. To change a tag, delete it with git tag -d and recreate it. For remote tags, force push may be needed.

How do I list all tags in chronological order?

Section titled “How do I list all tags in chronological order?”

git tag —sort=creatordate lists tags by creation date. Use —sort=version:refname for semantic version sorting.

What’s the difference between git tag and git branch?

Section titled “What’s the difference between git tag and git branch?”

Tags are immutable pointers to specific commits; branches are mutable pointers that move as you commit. Tags mark points in history, branches track development lines.

How do I delete a tag from remote repository?

Section titled “How do I delete a tag from remote repository?”

git push origin —delete deletes the tag from remote. Also delete locally with git tag -d .

Yes, git tag creates a lightweight tag pointing to any Git object, though this is uncommon.

git tag -v verifies the GPG signature. Requires the signer’s public key in your GPG keyring.

What’s the best practice for tag naming?

Section titled “What’s the best practice for tag naming?”

Use semantic versioning (v1.2.3) for releases, descriptive names for milestones, and consistent prefixes for different tag types.

Tags point to commits, not branches. A tag can point to a commit that’s on any branch or even not on any current branch.

How do I find which commit a tag points to?

Section titled “How do I find which commit a tag points to?”

git rev-parse shows the commit hash. git show shows detailed tag and commit information.

No, tags must point to existing commits. Create the commit first, then tag it.

How do I list tags that contain a specific commit?

Section titled “How do I list tags that contain a specific commit?”

git tag —contains lists all tags that contain the specified commit in their history.

What’s the impact of deleting a published tag?

Section titled “What’s the impact of deleting a published tag?”

Deleting published tags requires force operations and affects collaborators. Avoid deleting tags that others may depend on.

Yes, tags are commonly used to mark deployment points. CI/CD systems can trigger deployments based on new tags.

How do I create a tag with a multi-line message?

Section titled “How do I create a tag with a multi-line message?”

Use git tag -a -F where the file contains the multi-line message, or use -m with escaped newlines.

Yes, Git allows tags and branches with the same name since they’re in different namespaces (refs/tags/ vs refs/heads/).

How do I find the latest tag in the repository?

Section titled “How do I find the latest tag in the repository?”

git describe —tags —abbrev=0 shows the most recent tag. For semantic versions, use sorting: git tag —sort=-version:refname | head -1

  1. Release Management: Mark software releases and versions with descriptive tags
  2. Milestone Tracking: Mark important project milestones and achievements
  3. Deployment Points: Mark commits that are deployed to production environments
  4. Version Control: Implement semantic versioning for software projects
  5. Historical Reference: Create permanent references to important commits
  6. Collaboration: Share tagged points with team members and external contributors
  7. Documentation: Associate release notes and changelogs with specific versions
  8. Compliance: Maintain audit trails of important code states and releases