Git 标签完全指南(超级详细版)

3 阅读10分钟

在 Git 中,标签(Tag)就像为项目历史中一个特定的提交点拍下的快照,是一个指向该提交的静态指针。与随着新提交而移动的分支(Branch)不同,标签一旦创建就固定不变,是标记版本发布(如 v1.0.0)、重要里程碑等关键节点的最佳方式。

在深入了解所有命令之前,理解两种标签类型的区别至关重要。

🤔 标签类型:轻量标签 vs. 附注标签

  • 轻量标签 (Lightweight Tag):一个特定提交的引用,像是一个不会移动的分支指针。它不包含作者信息、日期和标签信息。
  • 附注标签 (Annotated Tag):一个独立的 Git 对象,存储在对象数据库中。它包含了标签名、作者信息、日期、一个可选的标签信息(Message)以及可选的 GPG 签名。
特性轻量标签 (Lightweight Tag)附注标签 (Annotated Tag)
本质一个指向提交的指针一个独立的 Git 对象
元数据❌ 无✅ 包含(作者、日期、信息、签名)
推荐用途临时、私有或个人标记正式发布、公共里程碑
示例命令git tag v1.0.0git tag -a v1.0.0 -m "信息"

核心原则:用于发布的正式版本,应始终使用附注标签

📝 核心操作:创建、查看与删除

创建标签

1. 创建轻量标签

# 为当前提交创建
git tag <tag-name>

# 示例:为当前提交创建轻量标签 v1.0.0
git tag v1.0.0

# 为历史提交创建
git tag <tag-name> <commit-hash>

# 示例:为指定提交创建轻量标签
git tag v0.9.0 9fceb02

轻量标签适合创建临时或私有的标签。

2. 创建附注标签

# 创建附注标签
git tag -a <tag-name> -m "<tag-message>"

# 示例:为当前提交创建附注标签
git tag -a v1.0.0 -m "正式发布 1.0.0 版本"

# 创建包含多行信息的附注标签
git tag -a v1.0.0 -m "正式发布 1.0.0 版本" -m "新增功能: 用户认证模块" -m "修复问题: 登录超时问题"

# 为历史提交创建附注标签
git tag -a <tag-name> <commit-hash> -m "<tag-message>"

# 示例:为指定提交创建附注标签
git tag -a v0.9.0 9fceb02 -m "预发布 0.9.0 版本"

-a (--annotate) 参数用于创建附注标签。 -m (--message) 参数用于提供标签信息。如果省略 -m,Git 会打开一个文本编辑器让你编写。

3. 创建 GPG 签名标签

# 使用默认 GPG 密钥签名
git tag -s <tag-name> -m "<tag-message>"

# 示例:使用默认密钥创建签名标签
git tag -s v1.0.0 -m "签名发布版本"

# 指定 GPG 密钥签名
git tag -u <key-id> <tag-name> -m "<tag-message>"

# 示例:使用指定密钥创建签名标签
git tag -u 0A46826A v1.0.0 -m "签名发布版本"

# 配置始终使用签名
git config --global tag.gpgSign true

-s (--sign) 参数使用默认邮箱地址的 GPG 密钥创建签名标签。 -u <key-id> (--local-user=) 参数使用指定的 GPG 密钥。 tag.gpgSign 配置为 true 后,git tag -a 命令会自动创建签名标签,可被 --no-sign 参数覆盖。

4. 从文件读取标签信息

# 创建附注标签,信息来自文件
git tag -a <tag-name> -F <file>

# 示例:从文件 version-note.txt 读取信息创建标签
git tag -a v1.0.0 -F version-note.txt

-F <file> (--file=) 参数从指定文件中读取标签信息。

5. 强制创建标签

# 强制替换同名标签
git tag -f <tag-name>

# 示例:强制将 v1.0.0 标签指向当前提交
git tag -f v1.0.0

# 强制创建附注标签
git tag -a -f <tag-name> -m "<message>"

# 示例:强制将附注标签 v1.0.0 指向当前提交
git tag -a -f v1.0.0 -m "重新发布的 1.0.0 版本"

-f (--force) 参数允许替换一个已存在的同名标签。通常不建议更改已公开的标签。

查看标签

1. 列出所有标签

# 列出所有标签
git tag

# 或使用 --list 参数
git tag --list

git tag 命令不加参数,会列出所有标签。

2. 按模式列出标签

# 列出匹配模式的标签
git tag --list "<pattern>"

# 示例:列出所有 v1 开头的标签
git tag --list "v1.*"

--list 配合模式参数(如 "v1.*")可以列出所有匹配的标签。模式是通配符,支持 fnmatch 规则。

3. 查看标签详细信息

# 查看标签的详细信息
git show <tag-name>

# 示例:查看 v1.0.0 标签的详细信息
git show v1.0.0

git show 命令会显示标签的元数据(对于附注标签)和它所指向的提交的差异信息。

4. 在日志中查看标签

# 在日志中显示标签
git log --oneline --decorate

# 显示所有分支和标签的图形化日志
git log --oneline --graph --all --decorate

# 查看两个标签之间的提交
git log <tag1>..<tag2>

# 示例:查看 v1.0.0 和 v2.0.0 之间的提交
git log v1.0.0..v2.0.0

--decorate 参数会让 git log 显示指向每个提交的引用(分支、标签),是查看标签位置最直观的方式。

5. 查看远程标签

# 查看远程仓库的所有标签
git ls-remote --tags origin

# 查看远程仓库的特定标签
git ls-remote --tags origin <tag-name>

# 示例:查看远程仓库的 v1.0.0 标签
git ls-remote --tags origin v1.0.0

git ls-remote --tags 命令可以列出远程仓库的所有标签,无需先拉取到本地。

删除标签

1. 删除本地标签

# 删除本地标签
git tag -d <tag-name>

# 示例:删除本地标签 v1.0.0
git tag -d v1.0.0

-d (--delete) 参数用于删除本地标签。

2. 删除远程标签

# 方法一:推送空引用
git push origin :refs/tags/<tag-name>

# 示例:推送空引用以删除远程 v1.0.0 标签
git push origin :refs/tags/v1.0.0

# 方法二:使用 --delete 参数
git push --delete origin <tag-name>

# 示例:使用 --delete 删除远程 v1.0.0 标签
git push --delete origin v1.0.0

删除远程标签需要两步:先删除本地标签,再推送到远程。推荐使用更直观的 --delete 方式。

🔄 与远程仓库交互

推送标签

# 推送单个标签到远程
git push origin <tag-name>

# 示例:推送 v1.0.0 到远程
git push origin v1.0.0

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

# 推送所有本地标签,但自动检查上游
git push --follow-tags

# 推送所有引用和标签
git push --all --tags

默认情况下,git push 不会推送标签。--tags 参数会推送所有本地标签,但也可能推送不需要的标签,建议谨慎使用。--follow-tags 是一个更安全的选择,它只推送附注标签,以及被推送的提交所指向的标签。

拉取标签

# 克隆仓库时会自动拉取所有标签
git clone <repository-url>

# 拉取远程仓库的所有标签
git fetch --tags

# 拉取特定标签
git fetch origin tag <tag-name>

# 示例:拉取远程仓库的 v1.0.0 标签
git fetch origin tag v1.0.0

# 拉取远程仓库的所有内容(包括标签)
git fetch --all --tags

git fetch --tags 命令会从远程仓库拉取所有标签。git fetch <remote> tag <tag-name> 可以拉取特定的标签。

同步标签

# 同步远程标签(删除本地不存在的远程标签)
git fetch --prune --tags

# 删除本地不存在的远程标签引用
git remote prune origin

# 强制同步远程标签
git fetch origin --prune --tags

git fetch --prune --tags 会在拉取前,先删除本地已经不存在的远程标签引用。

🚀 高级操作与实用技巧

检出标签

# 检出标签(进入分离头指针状态)
git checkout <tag-name>

# 示例:检出 v1.0.0 标签
git checkout v1.0.0

# 基于标签创建新分支
git checkout -b <branch-name> <tag-name>

# 示例:基于 v1.0.0 标签创建 hotfix 分支
git checkout -b hotfix v1.0.0

检出标签会使仓库进入"分离头指针"(detached HEAD)状态,意味着你不在任何分支上,所做的任何新提交都不会属于任何分支,除非你基于此创建一个新分支。

比较标签

# 比较两个标签的差异
git diff <tag1> <tag2>

# 示例:比较 v1.0.0 和 v2.0.0 的差异
git diff v1.0.0 v2.0.0

# 查看两个标签之间的提交列表
git log <tag1>..<tag2> --oneline

# 查看标签相对于当前分支的差异
git diff <tag-name>

# 示例:查看 v1.0.0 标签与当前分支的差异
git diff v1.0.0

git diff 命令可以比较两个标签之间的文件差异,git log 可以查看两个标签之间的提交历史。

编辑/重命名标签

# 重命名本地标签(先创建新标签,再删除旧标签)
git tag <new-tag-name> <old-tag-name>
git tag -d <old-tag-name>

# 示例:将 v1.0.0 重命名为 v1.0.1
git tag v1.0.1 v1.0.0
git tag -d v1.0.0

# 更新远程标签(需要强制推送)
git push origin :<old-tag-name> <new-tag-name>
git push origin <new-tag-name>

# 示例:更新远程仓库的标签名称
git push origin :v1.0.0 v1.0.1
git push origin v1.0.1

Git 没有直接重命名标签的命令,需要组合使用。

验证签名标签

# 验证标签的 GPG 签名
git tag -v <tag-name>

# 示例:验证 v1.0.0 标签的签名
git tag -v v1.0.0

# 验证多个标签
git tag -v <tag1> <tag2>

# 验证并显示格式
git tag -v --format="%H %(tagger)" <tag-name>

-v (--verify) 参数用于验证标签的 GPG 签名。

高级筛选与格式化

# 按包含的提交筛选标签
git tag --contains <commit>

# 示例:列出包含指定提交的标签
git tag --contains 9fceb02

# 按指向的对象筛选标签
git tag --points-at <object>

# 示例:列出指向当前提交的标签
git tag --points-at HEAD

# 按合并状态筛选标签
git tag --merged <commit>
git tag --no-merged <commit>

# 排序标签
git tag --sort=<key>

# 示例:按版本号排序
git tag --sort=-version:refname

# 示例:按标签名排序
git tag --sort=refname

# 自定义输出格式
git tag --format="%(refname:short) %(taggerdate:short)"

# 示例:显示标签名和创建日期
git tag --format="%(refname:short) was tagged on %(taggerdate:short)"

--contains 参数可以查找包含特定提交的标签。 --points-at 参数可以列出指向特定对象的标签。 --merged--no-merged 用于筛选已合并或未合并的标签。 --sort 参数用于排序,支持 refnametaggerdateversion:refname 等键。 --format 参数提供了强大的自定义输出功能。

⚙️ 自动化与集成

Git 钩子集成

# 示例:post-update 钩子(服务器端)
#!/bin/bash
# 当 master 分支更新时,自动创建发布标签
if [ "$1" = "refs/heads/master" ]; then
    RELEASE_TAG="release-$(date +'%Y-%m-%d')"
    git tag -a "$RELEASE_TAG" -m "自动发布标签 $RELEASE_TAG"
    echo "自动创建了标签:$RELEASE_TAG"
fi

# 示例:pre-push 钩子(客户端)
#!/bin/bash
# 在推送前检查标签是否存在
protected_tags=("v*")
for tag in "${protected_tags[@]}"; do
    if git tag -l "$tag" | grep -q .; then
        echo "错误:不能删除保护标签 $tag"
        exit 1
    fi
done

Git 钩子可以用于在特定事件发生时自动执行标签操作。 post-update 钩子可以在服务器端接收推送后自动创建标签。 pre-push 钩子可以在客户端推送前进行检查,例如禁止删除特定标签。

CI/CD 集成

# GitHub Actions 示例
name: Create Release Tag
on:
  push:
    branches: [ main ]

jobs:
  tag:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Get version from package.json
        id: get_version
        run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV
      - name: Create and push tag
        run: |
          git tag -a "v${{ env.VERSION }}" -m "Release v${{ env.VERSION }}"
          git push origin "v${{ env.VERSION }}"

CI/CD 系统可以自动化标签创建过程。在 CI 流程中,可以从 package.jsonVERSION 文件读取版本号,然后创建并推送标签。

📊 最佳实践

  1. 使用语义化版本控制 (SemVer):格式如 v<major>.<minor>.<patch>(例如 v1.2.3),清晰表达版本变更程度。
  2. 优先使用附注标签:始终为正式发布创建附注标签,以便在审计或排查问题时提供完整的元数据。
  3. 为历史提交打标签:如果忘记为某个版本打标签,可以事后补充,只需指定该版本的提交哈希即可。
  4. 定期同步标签:团队协作时,确保在 pullfetch 时使用 --tags 参数,保持本地与远程标签同步。
  5. 避免修改已推送的标签:不应修改或删除已公开的标签。如确有错误,应创建新标签,并使用 -f 等操作时需格外小心。

💎 总结

Git 标签是管理软件版本的核心工具。通过掌握其创建、查看、管理以及与远程仓库交互的全套操作,你可以为项目建立清晰、可靠的版本发布流程。始终遵循最佳实践,尤其是为正式发布使用附注标签,将使你的版本管理更加专业和高效。