跳转到内容
关系图谱

Git tag 详解

4,816 字 19 分钟

属性 1
status
学习中

Git tag 详解

git tag 是 Git 中用于为特定提交打上永久标记的命令。标签通常用于标记发布版本(如 v1.0.0v2.3.1),是版本管理和发布流程的核心工具。

一句话理解

tag 就像给提交贴一个「书签」——分支会不断前进,但标签永远指向同一个提交,方便你随时回溯到某个确定的版本。

基本语法

bash
git tag                              # 列出标签
git tag <tagname>                    # 创建轻量标签
git tag -a <tagname> -m "message"    # 创建附注标签
git tag -d <tagname>                 # 删除本地标签
git push origin <tagname>            # 推送标签到远程
git push origin --tags               # 推送所有标签

核心概念

两种标签类型

类型创建方式存储方式包含信息适用场景
轻量标签git tag <name>只是一个指向提交的引用无额外信息临时标记、本地使用
附注标签git tag -a <name>独立的 Git 对象标签者、日期、信息、可签名正式发布、团队协作
推荐使用附注标签

附注标签包含创建者信息、创建日期和标签说明,是正式发布版本的最佳选择。轻量标签适合个人临时标记。

标签 vs 分支

对比项标签(Tag)分支(Branch)
移动性不移动(永远指向同一提交)随提交前进
用途标记版本快照开发线
推送需要显式推送git push 即可
删除需要显式删除本地和远程git branch -d

创建标签

轻量标签

bash
# 为当前 HEAD 创建轻量标签
git tag v1.0.0-beta

# 为指定提交创建
git tag v0.9.0 abc1234

附注标签(推荐)

bash
# 创建附注标签(打开编辑器写信息)
git tag -a v1.0.0

# 直接指定信息
git tag -a v1.0.0 -m "Release v1.0.0: 首个正式版本"

# 为历史提交打标签
git tag -a v0.9.0 abc1234 -m "Release v0.9.0: beta 版本"

# 多行标签信息
git tag -a v2.0.0 -m "Release v2.0.0

主要变更:
- 全新的支付模块
- 性能优化 50%
- 修复 23 个 bug

Breaking Changes:
- API v1 已弃用,请迁移到 v2"

签名标签(GPG)

bash
# 创建 GPG 签名的标签(用于验证发布者身份)
git tag -s v1.0.0 -m "Signed release v1.0.0"

# 验证签名
git tag -v v1.0.0
签名标签的用途

签名标签通过 GPG 密钥证明「这个版本确实是授权发布者创建的」,常用于:

  • 开源项目的官方发布
  • 需要安全审计的企业项目
  • 软件供应链安全

查看标签

bash
# 列出所有标签
git tag

# 按模式过滤
git tag -l "v1.*"       # v1.x 版本
git tag -l "v2.0.*"     # v2.0.x 版本

# 按版本号排序(语义化排序)
git tag -l --sort=version:refname
git tag -l --sort=-version:refname   # 倒序

# 查看标签详情
git show v1.0.0
# 附注标签:显示标签信息 + 对应的提交
# 轻量标签:只显示对应的提交

# 查看标签指向的提交
git rev-parse v1.0.0

# 查看所有标签及其指向的提交
git tag -l --format='%(refname:short) → %(objectname:short) %(subject)'

推送标签

bash
# 推送单个标签
git push origin v1.0.0

# 推送所有本地标签
git push origin --tags

# 只推送附注标签(不推送轻量标签)
git push origin --follow-tags

# 配置默认推送附注标签
git config --global push.followTags true
标签不会被 git push 自动推送

与分支不同,标签需要显式推送。推荐在 .gitconfig 中设置 push.followTags = true,这样 git push 会自动推送附注标签。

删除标签

bash
# 删除本地标签
git tag -d v1.0.0-beta

# 删除远程标签
git push origin --delete v1.0.0-beta
# 或
git push origin :refs/tags/v1.0.0-beta

# 批量删除本地标签
git tag -l "v0.*" | xargs git tag -d

# 批量删除远程标签
git tag -l "v0.*" | xargs -I {} git push origin --delete {}

修改标签

Git 标签默认不可修改。如果需要修改,要先删除再重建:

bash
# 移动标签到不同的提交
git tag -d v1.0.0                                  # 删除本地
git push origin --delete v1.0.0                     # 删除远程
git tag -a v1.0.0 <new-commit> -m "Release v1.0.0" # 重建
git push origin v1.0.0                              # 推送

# 强制覆盖本地标签(不推荐)
git tag -f v1.0.0 <new-commit>
git push --force origin v1.0.0
不要移动已发布的标签

其他人可能已经基于原始标签进行了工作。移动标签会导致混乱。如果发布有误,更好的做法是:

  • 创建新的修复版本标签(如 v1.0.1
  • 或在 GitHub Releases 中标记该版本为 "pre-release" 或 "yanked"

版本号规范(Semantic Versioning)

SemVer 格式

v<MAJOR>.<MINOR>.<PATCH>[-<prerelease>][+<buildmetadata>]

版本号递增规则

变更类型递增位置示例
Bug 修复PATCH1.0.0 → 1.0.1
新功能(向后兼容)MINOR1.0.1 → 1.1.0
破坏性变更MAJOR1.1.0 → 2.0.0
预发布版本prerelease2.0.0-alpha.1
候选发布版本prerelease2.0.0-rc.1

常见的预发布版本标识

bash
v2.0.0-alpha.1    # 内部测试版
v2.0.0-alpha.2
v2.0.0-beta.1     # 公开测试版
v2.0.0-beta.2
v2.0.0-rc.1       # 候选发布版(Release Candidate)
v2.0.0-rc.2
v2.0.0            # 正式版

实际项目中的 tag 场景

场景 1:标准发布流程

bash
# 1. 确保 main 分支是最新的
git checkout main
git pull origin main

# 2. 确认要发布的版本(查看上次发布以来的修改)
git log $(git describe --tags --abbrev=0)..HEAD --oneline

# 3. 更新版本号和 CHANGELOG
vim package.json        # 更新 version 字段
vim CHANGELOG.md        # 更新变更日志
git add .
git commit -m "chore: bump version to v2.0.0"

# 4. 创建标签
git tag -a v2.0.0 -m "Release v2.0.0

Features:
- 全新支付模块 (#123)
- 用户权限系统 (#456)

Bug Fixes:
- 修复登录超时 (#789)"

# 5. 推送代码和标签
git push origin main --follow-tags

场景 2:基于标签的 CI/CD 自动发布

大多数 CI/CD 系统支持在创建标签时自动触发发布:

yaml
# GitHub Actions 示例
name: Release
on:
  push:
    tags:
      - 'v*' # 匹配 v 开头的标签

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: npm run build
      - name: Create GitHub Release
        uses: softprops/action-gh-release@v1
        with:
          generate_release_notes: true
bash
# 推送标签 → 自动触发构建和发布
git tag -a v2.0.0 -m "Release v2.0.0"
git push origin v2.0.0
# CI/CD 自动:构建 → 测试 → 发布到 npm/Docker/etc

场景 3:紧急 hotfix 发布

bash
# 1. 从出问题的版本创建 hotfix 分支
git checkout -b hotfix/v2.0.1 v2.0.0

# 2. 修复 bug
git commit -m "fix: 修复支付金额计算错误"

# 3. 合并到 main
git checkout main
git merge --no-ff hotfix/v2.0.1

# 4. 创建补丁版本标签
git tag -a v2.0.1 -m "Hotfix v2.0.1: 修复支付金额计算错误"

# 5. 推送
git push origin main --follow-tags

# 6. 清理
git branch -d hotfix/v2.0.1

场景 4:预发布版本管理

bash
# Alpha 阶段
git tag -a v3.0.0-alpha.1 -m "v3.0.0 Alpha 1: 初始功能集"
# 内部测试...发现问题修复后
git tag -a v3.0.0-alpha.2 -m "v3.0.0 Alpha 2: 修复初始问题"

# Beta 阶段
git tag -a v3.0.0-beta.1 -m "v3.0.0 Beta 1: 功能冻结,开始公测"
# 公测反馈修复后
git tag -a v3.0.0-beta.2 -m "v3.0.0 Beta 2: 修复公测反馈"

# RC 阶段
git tag -a v3.0.0-rc.1 -m "v3.0.0 RC1: 候选发布版"
# 最终确认
git tag -a v3.0.0 -m "v3.0.0: 正式发布"

场景 5:使用 git describe 自动生成版本信息

bash
# git describe 基于最近的标签生成版本描述
git describe --tags
# v2.0.0-15-gabc1234
# 含义:距离 v2.0.0 有 15 个提交,当前 SHA 是 abc1234

# 用于构建版本号
VERSION=$(git describe --tags --always)
echo "当前版本: $VERSION"

# 在代码中注入版本信息
echo "export const VERSION = '$VERSION';" > src/version.ts

# 常用于 CI/CD 构建
docker build -t myapp:$VERSION .

场景 6:回滚到指定版本

bash
# 查看所有发布版本
git tag -l "v*" --sort=-version:refname

# 检出特定版本(detached HEAD)
git checkout v1.5.0

# 基于旧版本创建修复分支
git checkout -b hotfix/from-v1.5 v1.5.0

# 对比两个版本的差异
git diff v1.5.0 v2.0.0 --stat
git log v1.5.0..v2.0.0 --oneline

场景 7:多项目/Monorepo 标签管理

bash
# monorepo 中为不同项目使用不同的标签前缀
git tag -a api/v2.0.0 -m "API v2.0.0"
git tag -a web/v3.1.0 -m "Web v3.1.0"
git tag -a sdk/v1.5.0 -m "SDK v1.5.0"

# 查看特定项目的标签
git tag -l "api/*"
git tag -l "web/*"

# 推送特定项目的标签
git push origin api/v2.0.0

高级用法

使用 git describe 计算版本距离

bash
# 距离最近标签的提交数
git describe --tags --abbrev=0    # 只显示标签名
# v2.0.0

git describe --tags               # 完整描述
# v2.0.0-3-gabc1234
# 3 = 距离 v2.0.0 有 3 个新提交

# 只匹配特定模式的标签
git describe --tags --match="v2.*"

# 在脚本中使用
if git describe --tags --exact-match HEAD 2>/dev/null; then
    echo "当前 HEAD 有标签,执行发布流程"
else
    echo "当前 HEAD 没有标签,跳过发布"
fi

检查标签是否已在远程

bash
# 列出远程标签
git ls-remote --tags origin

# 检查特定标签是否存在于远程
git ls-remote --tags origin | grep "v2.0.0"

# 获取远程标签(不自动获取)
git fetch origin --tags

标签与 GitHub/GitLab Releases

bash
# GitHub:基于标签创建 Release
# 1. 推送标签
git push origin v2.0.0

# 2. 在 GitHub 上从标签创建 Release(可附加二进制文件)
# 或使用 GitHub CLI
gh release create v2.0.0 --generate-notes

# 3. 附加构建产物
gh release upload v2.0.0 dist/app.zip dist/app.dmg

# GitLab:基于标签触发 Release
# .gitlab-ci.yml 中配置
# release:
#   tag_name: $CI_COMMIT_TAG
#   description: ./CHANGELOG.md

实用技巧

技巧 1:自动化版本号递增

bash
# 获取最新标签
LATEST=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "当前版本: $LATEST"

# 解析版本号
MAJOR=$(echo $LATEST | cut -d. -f1 | tr -d v)
MINOR=$(echo $LATEST | cut -d. -f2)
PATCH=$(echo $LATEST | cut -d. -f3)

# 递增 PATCH
NEW_VERSION="v$MAJOR.$MINOR.$((PATCH + 1))"
echo "新版本: $NEW_VERSION"

# 创建新标签
git tag -a $NEW_VERSION -m "Release $NEW_VERSION"

技巧 2:标签命名规范

bash
# ✅ 推荐的标签命名
v1.0.0              # 语义化版本号
v1.0.0-alpha.1      # 预发布版本
v1.0.0-rc.1         # 候选发布
api/v2.0.0          # monorepo 中的项目前缀

# ❌ 不推荐的标签命名
release-20260308    # 基于日期(不符合 SemVer)
latest              # 语义模糊
stable              # 不够具体
1.0.0               # 缺少 v 前缀(约定俗成用 v)

技巧 3:配置 Git alias

bash
[alias]
    # 列出标签(按版本号排序)
    tags = tag -l --sort=-version:refname
    # 查看最新标签
    latest = describe --tags --abbrev=0
    # 查看距最新标签的距离
    ver = describe --tags
    # 创建附注标签
    release = "!f() { git tag -a v$1 -m \"Release v$1\"; }; f"
    # 推送标签
    push-tags = push origin --follow-tags
    # 查看两个标签间的提交
    between = "!f() { git log $1..$2 --oneline --no-merges; }; f"

# 使用
git tags                    # 列出所有标签
git latest                  # 最新标签
git release 2.0.0           # 创建 v2.0.0
git push-tags               # 推送
git between v1.0.0 v2.0.0   # 两个版本间的提交

技巧 4:使用 npm version / cargo release 等工具

bash
# Node.js 项目:npm version 自动处理版本号 + 标签
npm version patch   # 1.0.0 → 1.0.1,自动创建 v1.0.1 标签
npm version minor   # 1.0.1 → 1.1.0
npm version major   # 1.1.0 → 2.0.0

# npm version 做了什么:
# 1. 更新 package.json 的 version 字段
# 2. git add package.json
# 3. git commit -m "v1.0.1"
# 4. git tag v1.0.1

# Rust 项目
cargo release patch

技巧 5:清理过时的标签

bash
# 同步远程标签到本地(删除本地多余的)
git fetch origin --prune --prune-tags

# 或手动清理
# 1. 查看本地有但远程没有的标签
git tag -l | while read tag; do
    git ls-remote --tags origin | grep -q "$tag" || echo "本地多余: $tag"
done

# 2. 删除本地多余的标签
git tag -l "v0.0.*" | xargs git tag -d

更多实战场景

场景 8:语义化发布自动化(semantic-release)

使用 semantic-release 等工具,根据 Conventional Commits 自动决定版本号并打标签:

bash
# 安装
npm install -D semantic-release

# 工作原理:
# 1. 分析 commit messages
# 2. feat: → MINOR bump
# 3. fix: → PATCH bump
# 4. BREAKING CHANGE: → MAJOR bump
# 5. 自动创建标签 + GitHub Release + CHANGELOG

# .releaserc.json 配置
{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/changelog",
    "@semantic-release/npm",
    "@semantic-release/git",
    "@semantic-release/github"
  ]
}

场景 9:多环境标签管理

不同环境使用不同的标签前缀标记部署版本:

bash
# 标签策略
# v*.*.* — 正式发布版本
# staging-* — staging 部署版本
# canary-* — 金丝雀部署版本

# 部署到 staging
git tag staging-$(date +%Y%m%d-%H%M) HEAD
git push origin staging-$(date +%Y%m%d-%H%M)

# 部署到 canary(灰度发布)
git tag canary-$(git rev-parse --short HEAD) HEAD
git push origin canary-$(git rev-parse --short HEAD)

# 正式发布
git tag -a v3.0.0 -m "Release v3.0.0"
git push origin v3.0.0

# 清理旧的环境标签(保留最近 10 个)
git tag -l "staging-*" | sort | head -n -10 | xargs git tag -d
git tag -l "staging-*" | sort | head -n -10 | xargs -I {} git push origin --delete {}

场景 10:使用标签进行 A/B 测试

bash
# 两个版本同时部署,标签标记对应版本
git tag -a v3.0.0-variant-a -m "A/B 测试变体 A:新支付流程"
git tag -a v3.0.0-variant-b -m "A/B 测试变体 B:旧支付流程"

# CI/CD 根据标签部署到不同的服务实例
# variant-a → 50% 流量
# variant-b → 50% 流量

# A/B 测试结束后,胜出的变体成为正式版本
git tag -a v3.0.0 -m "Release v3.0.0: 采用变体 A 的支付流程"

场景 11:版本回退与紧急降级

bash
# 生产环境出问题,需要快速回退到上一个版本

# 1. 查看最近的版本标签
git tag -l "v*" --sort=-version:refname | head -5
# v3.0.0    ← 当前有问题的版本
# v2.9.0
# v2.8.1
# v2.8.0

# 2. 确认回退目标
git log v2.9.0..v3.0.0 --oneline  # 查看这之间改了什么

# 3. 方式一:部署旧标签
# CI/CD 重新部署 v2.9.0 标签(不修改代码)

# 4. 方式二:从旧标签创建 hotfix
git checkout -b hotfix/rollback v2.9.0
# 可能需要 cherry-pick 一些 v3.0.0 中的关键修复
git cherry-pick <safe-fixes-from-v3>
git tag -a v2.9.1 -m "Hotfix v2.9.1: 从 v3.0.0 回退"
git push origin v2.9.1

场景 12:标签与 Docker 镜像版本对应

bash
# 构建时用 Git 标签作为 Docker 镜像版本
VERSION=$(git describe --tags --always)

docker build -t myapp:$VERSION .
docker tag myapp:$VERSION registry.example.com/myapp:$VERSION
docker push registry.example.com/myapp:$VERSION

# 正式版本额外标记 latest
if git describe --tags --exact-match HEAD 2>/dev/null | grep -q "^v[0-9]"; then
    docker tag myapp:$VERSION registry.example.com/myapp:latest
    docker push registry.example.com/myapp:latest
fi

# Kubernetes 部署时引用标签版本
# deployment.yaml
# image: registry.example.com/myapp:v3.0.0

标签与版本管理工作流

完整的版本生命周期

版本号决策辅助脚本

bash
#!/bin/bash
# bump-version.sh — 交互式版本号递增工具

LATEST=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "当前版本: $LATEST"
echo ""

MAJOR=$(echo $LATEST | sed 's/v//' | cut -d. -f1)
MINOR=$(echo $LATEST | sed 's/v//' | cut -d. -f2)
PATCH=$(echo $LATEST | sed 's/v//' | cut -d. -f3 | cut -d- -f1)

echo "选择版本类型:"
echo "  1) patch  → v$MAJOR.$MINOR.$((PATCH + 1))"
echo "  2) minor  → v$MAJOR.$((MINOR + 1)).0"
echo "  3) major  → v$((MAJOR + 1)).0.0"
echo "  4) 自定义"
read -p "选择 [1-4]: " choice

case $choice in
    1) NEW="v$MAJOR.$MINOR.$((PATCH + 1))" ;;
    2) NEW="v$MAJOR.$((MINOR + 1)).0" ;;
    3) NEW="v$((MAJOR + 1)).0.0" ;;
    4) read -p "输入版本号 (如 v3.0.0-beta.1): " NEW ;;
esac

echo ""
echo "即将创建标签: $NEW"
echo "变更日志 ($LATEST..$NEW):"
git log $LATEST..HEAD --oneline --no-merges
echo ""

read -p "确认? [y/N]: " confirm
if [ "$confirm" = "y" ]; then
    git tag -a $NEW -m "Release $NEW"
    echo "✅ 标签 $NEW 已创建"
    read -p "推送到远程? [y/N]: " push
    if [ "$push" = "y" ]; then
        git push origin $NEW
        echo "✅ 已推送"
    fi
fi

常见陷阱

陷阱 1:忘记推送标签
bash
# ❌ 创建了标签但忘记推送
git tag -a v2.0.0 -m "Release v2.0.0"
git push origin main
# 标签没有被推送!CI/CD 不会触发发布

# ✅ 推送代码时一起推送标签
git push origin main --follow-tags

# 或配置默认行为
git config --global push.followTags true
陷阱 2:轻量标签无法签名和附加信息
bash
# ❌ 轻量标签没有创建者信息
git tag v1.0.0
git show v1.0.0
# 只显示提交信息,不显示谁创建了标签

# ✅ 使用附注标签
git tag -a v1.0.0 -m "Release v1.0.0"
git show v1.0.0
# 显示标签创建者、日期和信息
陷阱 3:标签名与分支名冲突
bash
# ❌ 标签和分支同名会导致歧义
git tag release
git branch release

# 引用时需要显式指定
git checkout tags/release      # 检出标签
git checkout refs/heads/release # 检出分支

# ✅ 使用 v 前缀避免冲突
git tag v1.0.0    # 标签
git branch release/v1.0  # 分支
陷阱 4:移动已发布的标签
bash
# ❌ 移动已推送的标签
git tag -f v2.0.0 <new-sha>
git push --force origin v2.0.0
# 其他人的 v2.0.0 还指向旧提交!需要所有人重新 fetch

# ✅ 创建新的补丁版本
git tag -a v2.0.1 -m "v2.0.1: 修复 v2.0.0 中的问题"
陷阱 5:检出标签后在 detached HEAD 上提交
bash
# 检出标签会进入 detached HEAD
git checkout v1.0.0
# 你正处于 'detached HEAD' 状态

# ❌ 在这里提交会丢失
git commit -m "fix something"
git checkout main   # 提交变成悬空对象!

# ✅ 从标签创建分支再提交
git checkout -b hotfix/from-v1.0.0 v1.0.0
git commit -m "fix something"

常见问题

标签打错了怎么办?
  • 还没推送:直接删除重建
    bash
    git tag -d v1.0.0
    git tag -a v1.0.0 -m "正确的标签信息"
  • 已经推送:删除远程再推送
    bash
    git push origin --delete v1.0.0
    git tag -d v1.0.0
    git tag -a v1.0.0 <correct-sha> -m "正确的标签信息"
    git push origin v1.0.0
  • 已经被其他人使用:创建新版本标签(如 v1.0.1
如何查看两个标签之间修改了什么?
bash
# 提交列表
git log v1.0.0..v2.0.0 --oneline --no-merges

# 代码差异
git diff v1.0.0 v2.0.0 --stat

# 详细 diff
git diff v1.0.0 v2.0.0

详见 Git log 与 diff

git fetch 会自动获取标签吗?

默认情况下,git fetch 会获取你已有分支所指向的标签,但不会获取所有标签。要获取所有标签:

bash
git fetch --tags
git fetch origin --prune --prune-tags   # 同步并清理
如何基于标签创建分支?
bash
git checkout -b hotfix/from-v1.0 v1.0.0
# 或
git switch -c hotfix/from-v1.0 v1.0.0
轻量标签和附注标签能转换吗?

不能直接转换。需要删除轻量标签,重新创建附注标签:

bash
# 记录轻量标签指向的提交
SHA=$(git rev-parse v1.0.0)
# 删除轻量标签
git tag -d v1.0.0
# 创建附注标签
git tag -a v1.0.0 $SHA -m "Release v1.0.0"
在 Windows PowerShell 中的注意事项?
powershell
# 创建多行标签信息
git tag -a v2.0.0 -m "Release v2.0.0`n`n- Feature A`n- Feature B"

# 或使用编辑器
git tag -a v2.0.0   # 打开默认编辑器

速查表

目标命令
列出所有标签git tag
按模式过滤git tag -l "v2.*"
按版本排序git tag -l --sort=-version:refname
创建轻量标签git tag <name>
创建附注标签git tag -a <name> -m "message"
为历史提交打标签git tag -a <name> <sha> -m "message"
创建签名标签git tag -s <name> -m "message"
查看标签详情git show <tagname>
删除本地标签git tag -d <tagname>
删除远程标签git push origin --delete <tagname>
推送单个标签git push origin <tagname>
推送所有标签git push origin --tags
推送附注标签git push origin --follow-tags
获取远程标签git fetch --tags
检出标签git checkout <tagname>
从标签创建分支git checkout -b <branch> <tagname>
最新标签git describe --tags --abbrev=0
当前版本描述git describe --tags
两标签间的提交git log <tag1>..<tag2> --oneline
强制覆盖标签git tag -f <name> <sha>
同步远程标签git fetch origin --prune --prune-tags

决策流程图

相关命令

  • git merge — 合并分支后打 release 标签
  • git log — 查看标签间的提交历史
  • git reset — 重置到某个标签版本
  • git cherry-pick — 将修复 cherry-pick 后打补丁标签
  • git revert — 回滚后发布新补丁版本
  • git describe — 基于标签生成版本描述
  • git archive — 基于标签导出代码归档
  • git config — GPG 签名、tag.gpgSign 等标签相关配置
  • Git hooks — 标签推送时可通过 hooks 触发 CI/CD

参考资料