什么是标签
标签(Tag)是指向特定 commit 的永久引用。与分支不同,标签一旦创建,通常不会移动——分支会随着新提交而前进,标签则始终指向它被创建时的那个 commit。
- 分支(Branch) 可移动的指针,随每次提交自动向前移动。代表"当前开发进度"。
- 标签(Tag) 固定的指针,永远指向创建时的 commit。代表"历史上的某个里程碑"(如版本发布)。
标签的典型用途:标记软件版本发布(v1.0.0、v2.3.1-beta.1),方便将来快速找到某个版本对应的代码。
两种标签类型
轻量标签(Lightweight Tag)
本质上只是一个指向某个 commit 的指针(就像分支一样),不包含任何额外信息。
# 在当前 HEAD 处创建轻量标签
git tag v1.0.0
# 在指定 commit 处创建
git tag v0.9.0 abc1234
附注标签(Annotated Tag)—— 推荐
附注标签是 Git 数据库中的一个完整对象,包含:标签者姓名和邮箱、创建日期、标签消息,以及可选的 GPG 签名。
# 创建附注标签(-a = annotated,-m = message)
git tag -a v1.0.0 -m "Release version 1.0.0"
# 创建并打开编辑器输入详细标签信息
git tag -a v1.0.0
# 创建 GPG 签名的标签(-s)
git tag -s v1.0.0 -m "Signed release v1.0.0"
为什么推荐附注标签?附注标签包含完整的元数据(谁打的、什么时候打的、为什么打),可以用 GPG 签名验证真实性,在 GitHub/GitLab 上可以自动触发 Release 页面的生成。
标签操作
# 列出所有标签
git tag
# 按模式过滤列出(支持通配符)
git tag -l "v1.*"
# 查看标签详情(显示标签信息 + 对应的 commit)
git show v1.0.0
# 检出到某个标签(进入 detached HEAD 状态)
git checkout v1.0.0
# 建议检出标签后立即创建分支
git switch -c hotfix-v1.0.x
给历史提交打标签
忘记打标签?没关系,可以对过去的任意提交补打标签。
# 先找到要打标签的提交哈希
git log --oneline
# 对历史提交打附注标签
git tag -a v0.9.0 abc1234 -m "Retroactively tag 0.9.0 release"
推送标签到远程
标签不会随 git push 自动推送,需要显式操作。
# 推送单个标签
git push origin v1.0.0
# 推送所有本地标签到远程
git push origin --tags
# 只推送附注标签(不推送轻量标签)
git push origin --follow-tags
GitHub Release:当你推送一个附注标签到 GitHub 后,可以在 GitHub 仓库的 "Releases" 页面找到它,并在那里添加发布说明、附上编译好的二进制文件,创建正式的 Release。
删除标签
# 删除本地标签
git tag -d v1.0.0
# 删除远程标签(方式一)
git push origin :refs/tags/v1.0.0
# 删除远程标签(方式二,更直观)
git push origin --delete v1.0.0
语义化版本(SemVer)
Semantic Versioning(语义化版本)是一套被广泛采用的版本号规范,格式为 MAJOR.MINOR.PATCH(如 2.1.3)。
| 字段 | 含义 | 递增时机 |
|---|---|---|
MAJOR(主版本) | 重大变更 | 有不兼容的 API 变更时(Breaking Change) |
MINOR(次版本) | 功能新增 | 新增了向后兼容的新功能时 |
PATCH(补丁) | Bug 修复 | 向后兼容的 bug 修复时 |
预发布版本与构建元数据
2.1.3-alpha.1 # alpha 版本,内部测试
2.1.3-beta.2 # beta 版本,公测
2.1.3-rc.1 # Release Candidate,发布候选
2.1.3+build.456 # 构建元数据(不影响版本优先级)
版本号规则示例
- 当前版本
1.4.2,修复了一个登录 bug →1.4.3 - 当前版本
1.4.3,新增了搜索功能(API 向后兼容)→1.5.0 - 当前版本
1.5.0,重构了认证系统(API 接口变更)→2.0.0 0.x.x版本代表初始开发阶段,可以随意变更 API
版本号从 1.0.0 开始意味着你的公开 API 已稳定,承诺向后兼容性。在 API 稳定之前,使用 0.x.x 版本以表明仍处于初期开发阶段。
CHANGELOG 与自动化发布
CHANGELOG 是一个记录每个版本"做了什么变化"的文档,帮助用户快速了解升级影响。
手动维护 CHANGELOG
# CHANGELOG.md
## [2.1.0] - 2024-03-15
### Added
- Dark mode support
- Export to PDF feature
### Fixed
- Fix crash when user has no avatar
### Changed
- Redesigned settings page
## [2.0.0] - 2024-01-10
### BREAKING CHANGES
- Renamed `getUserById` to `getUser`
- Removed deprecated `legacyLogin` endpoint
Conventional Commits → 自动生成 CHANGELOG
遵循 Conventional Commits 规范的项目,可以使用工具自动生成 CHANGELOG 并自动递增版本号:
# 使用 standard-version(传统方案)
npx standard-version
# 使用 release-please(Google 推荐,GitHub Actions 集成)
# 在 GitHub Actions 中配置,自动分析 commit,创建 Release PR
# 使用 semantic-release(全自动方案)
npx semantic-release
这些工具会分析 commit 信息(feat: → MINOR,fix: → PATCH,feat!: 或 BREAKING CHANGE → MAJOR),自动决定下一个版本号,生成 CHANGELOG,打标签,发布 Release。这就是为什么 Conventional Commits 规范如此重要。
标签的底层原理
与分支一样,标签在磁盘上也是引用文件,存储在 .git/refs/tags/ 目录中:
# 查看轻量标签:内容就是 commit 哈希
cat .git/refs/tags/v1.0.0
# a3f2c1d8e5b9f2130459a781cd13f58cc6f88c9
# 附注标签:内容是 tag 对象的哈希(不是 commit 哈希!)
cat .git/refs/tags/v1.0.0-annotated
# b7e3d9f2c4a1...
# 查看 tag 对象内容
git cat-file -p v1.0.0
# object a3f2c1d... ← 指向的 commit
# type commit
# tag v1.0.0
# tagger 张三 <zhang@example.com> 1710000000 +0800
# Release version 1.0.0
# 查看对象类型差异
git cat-file -t v1.0.0-annotated # 输出:tag
git cat-file -t v1.0.0-lightweight # 输出:commit(直接指向 commit)
常见标签操作误区
标签默认不会随 push 推送 — 执行 git push origin main 时,标签不会自动推送到远程。必须显式推送:git push origin v1.0.0 或 git push origin --tags。很多人发布后才发现远程没有标签,建议在发布流程文档中明确这一步。
不要移动已发布的标签 — 标签一旦推送到远程并被他人拉取,就不应该再移动(即使技术上可以用 git tag -f 强制覆盖)。如果打错了标签,应该删除旧标签并创建新标签,同时通知所有协作者。移动已发布标签会导致他人的本地缓存与远程不一致。
检出标签会进入 Detached HEAD — git checkout v1.0.0 后,HEAD 不再指向任何分支,处于"游离"状态。在此状态下的任何提交都不属于任何分支,很容易丢失。如果需要在某个标签版本上做修改,应立即创建新分支:git switch -c hotfix-v1.0.x。
SemVer 边界情况
| 情况 | 正确处理 |
|---|---|
| 添加了新的可选参数 | MINOR(向后兼容) |
| 废弃了某个 API(但仍然可用) | MINOR + 文档标注 deprecated |
| 移除了废弃的 API | MAJOR(Breaking Change) |
| 修复了 Bug 但改变了行为 | 视情况:如果行为改变是有意的且有文档,可能是 MINOR |
| 性能优化,无 API 变化 | PATCH |
| 安全修复 | PATCH(紧急发布) |
本章小结 — 标签是指向特定 commit 的永久引用,分轻量标签(只有哈希)和附注标签(完整对象,包含元数据,推荐使用)。语义化版本(MAJOR.MINOR.PATCH)约定了版本号的递增规则,与 Conventional Commits 结合可以实现自动化发布流程。标签不会自动 push,需要显式推送;已发布的标签不应移动;检出标签会进入 Detached HEAD 状态,记得创建分支。