每次发布新版本,开源项目都会有一个 v1.0.0、v2.1.3 这样的版本号。这些版本号在 Git 里对应一个叫标签(tag)的东西。本文讲清楚标签是什么、轻量标签和附注标签的区别、怎么打怎么删,以及版本号背后的语义化版本规范。
1. 标签是什么
标签是贴在某个提交上的固定标记,就像给历史里某一个瞬间拍了张照片,之后随时可以回到那一刻。它和分支最大的区别是:分支会移动,标签不会。分支指针跟着新提交往前走,而标签一旦打上,就永远指向同一个提交。
什么时候用标签?最常见的就是发布版本:代码测试通过、准备上线,就在这个提交上打一个 v1.0.0。以后用户报 bug 说"我用的 v1.0.0",你就能立刻切到那个提交复现问题。先看看仓库里有哪些标签:
git tag
# 按字母顺序列出所有标签
git tag -l "v1.*"
git tag 列出全部标签,-l 支持通配符过滤,比如只看 v1 开头的。仓库还没有标签时输出为空,这很正常。
2. 轻量标签与附注标签
Git 有两种标签,区别在于是否携带额外信息:
| 类型 | 命令 | 特点 |
|---|---|---|
| 轻量标签 | git tag v1.0.0 | 只是一个指针,没有作者、时间、说明 |
| 附注标签 | git tag -a v1.0.0 -m "说明" | 完整对象,含打标签者、时间、说明,可校验 |
打个轻量标签只需要一个名字:
git tag v1.0.0-light
git tag -l
轻量标签适合临时标记,比如"这个提交是我试出来的可用版本"。但正式发布建议用附注标签,它像一次"提交",记录了谁在什么时候、因为什么打了这个版本:
git tag -a v1.0.0 -m "第一个正式版本:支持用户登录"
git show v1.0.0
-a 表示 annotated(附注),-m 写说明;git show 能看到标签的完整信息——打标签的人、时间、说明,以及它指向哪个提交。这就是正式发布的推荐姿势。
3. 给历史提交打标签
有时候版本发完了才想起来忘了打标签,或者想给两个月前的某个提交补一个。找到那个提交的哈希值,直接指定即可:
git log --oneline
# 找到目标提交,比如 a1b2c3d 修复了登录 bug
git tag -a v0.9.0 -m "发布前修复版" a1b2c3d
git tag -n
git tag -a 标签名 提交哈希 会把标签贴到指定提交上,而不是当前最新的提交。git tag -n 能顺便看到每个标签说明的第一行,快速回顾各版本都干了什么。
补完标签,你可能想看看某个旧版本当时的代码长什么样,直接 git checkout v0.9.0 就能切过去。但注意,此时 Git 会进入游离的 HEAD(detached HEAD) 状态:你不在任何分支上,直接提交的改动会悬空、容易被垃圾回收。如果要在旧版本上继续开发,先 git switch -c fix-v0.9 从该标签创建一个分支再动手,这样改动才有"家"。
4. 推送与删除标签
标签默认不会跟着 git push 上传,需要单独推送:
git push origin v1.0.0 # 推送单个标签
git push origin --tags # 推送所有标签
发布版本时这两条很关键:标签只存在于本地的话,团队其他人是看不到的。删除则分两步,本地删了还得让远程也删:
git tag -d v1.0.0-light # 删除本地标签
git push origin :refs/tags/v1.0.0-light # 删除远程标签
git tag -d 删本地;git push origin :refs/tags/标签名 的写法是把"空"推给远程那个标签,相当于删除。也可以写 git push origin --delete 标签名,两种写法等价,记住一种即可。
5. 语义化版本号
版本号不是随便写的,业界通用的是语义化版本(SemVer) 规范,格式为 主版本号.次版本号.修订号,即 MAJOR.MINOR.PATCH:
- 主版本号:不兼容的 API 变更。比如
v2.0.0把v1.x的接口删了或改了。 - 次版本号:向后兼容的新功能。比如
v1.2.0加了新函数,旧代码不受影响。 - 修订号:向后兼容的 bug 修复。比如
v1.2.1只是修了个崩溃,不新增功能。
预发布版本可以加后缀,如 v2.0.0-rc.1(候选版)、v1.3.0-beta.2(测试版)。这样用户看到 v1.2.1 就知道可以放心升级,而看到 v2.0.0 就要检查兼容性了。规范的版本号是给机器和人一起看的:包管理器靠它判断依赖要不要更新,人靠它决定升级策略。
6. 一次真实的发布流程
把前面的知识串起来,一次标准发布长这样:
git checkout main
git pull
# 跑测试、确认代码就绪……
git tag -a v1.0.0 -m "发布 v1.0.0:第一个正式版本"
git push origin main
git push origin v1.0.0
先切到主分支并拉取最新代码,测试通过后打附注标签,然后把代码和标签分别推送。在 GitHub 上,推送标签后可以在 Releases 页面基于该标签创建 Release,附上安装包和更新说明,这就是用户看到的"发布"。
7. 总结与练习
标签是把"时间点"变成"里程碑"的工具:轻量标签随手标记,附注标签用于正式发布;标签要单独推送、分两步删除;版本号遵循语义化版本规范,让升级决策变得简单。练习:
- 给你最近的练习项目打一个
v0.1.0附注标签,推送到远程,再到 GitHub 的 Releases 页面创建一个 Release。 - 故意打一个错误的轻量标签,练习本地、远程两步删除。
- 用
git tag -a v0.0.1 -m "备份点" 某个历史提交给旧提交补标签,再用git checkout v0.0.1切回去看看那个时刻的代码。
💡 养成习惯:每次发布都在main分支上打附注标签,版本号按 SemVer 规则递增。一年后回看,git tag -n就是你的项目编年史。