版本号排序为什么会错:semver 批量处理与正确比较的实操指南
一堆版本号按字符串排序会把 1.10.0 排在 1.9.0 前面。本文讲清 semver 该按主版本,次版本,修订号逐段当数字比较的规则,以及批量排序,找最新,补零规范化怎么在本地一次跑完。
版本号排序为什么会错:semver 批量处理与正确比较的实操指南
发版前我想从一堆 git 标签里挑出最新的那个,顺手把它们排个序。结果脚本告诉我最新版本是 1.9.0,可项目里明明已经有 1.10.0 了。问题不在标签,在排序方式:我用的是字符串排序,而版本号不是字符串。
字符串排序到底错在哪
把 1.9.0,1.10.0,1.2.0 当普通字符串排,计算机是逐个字符比的。比到第三个字符时,它看到的是 9 和 1。9 的字符编码比 1 大,于是 1.9.0 被判定"更大",排到了 1.10.0 后面。字符串眼里 9 > 10,因为它根本没把这两段当数字看。
正确做法是把版本号按点拆成三段:主版本,次版本,修订号。然后逐段当整数比较。1.9.0 和 1.10.0 主版本都是 1,平手;再比次版本,9 对 10,这时按数字 10 > 9,所以 1.10.0 更新。一旦改成逐段数字比较,1.10.0 立刻回到它该在的位置。
一个真实的排序例子
输入三个版本号,每行一个:
1.9.0
1.10.0
1.2.0
字符串升序排出来是这样的(错的):
1.10.0
1.2.0
1.9.0
按 semver 规则升序排,结果应该是:
1.2.0
1.9.0
1.10.0
逐段拆开看就清楚了。三个版本主版本都是 1,直接比次版本:2,9,10。当整数排是 2 < 9 < 10,修订号都是 0 不影响结果。所以最新版本是 1.10.0,不是字符串排序骗你的 1.9.0。要"找最新",其实就是取这个有序列表的最后一个。
补零规范化:为什么 1.2 不等于 1.2.0
批量处理时另一个坑是格式不齐。同一批标签里可能混着 v1.2,1.2.0,1.02.0 这几种写法。它们指向同一个版本,但字面不一样,去重时会被当成三个不同的值留下来。
规范化就是把它们统一成标准的三段形式:剥掉前缀 v,补齐缺的修订号(1.2 变 1.2.0),去掉数字前多余的零(1.02.0 变 1.2.0)。三个标签规范化后全是 1.2.0,去重就只剩一条。先规范化再去重,顺序不能反,否则那几个"看起来不一样"的同义标签会漏网。
把整套流程放在本地一次跑完
我现在的习惯是把所有版本号粘进 语义化版本号列表转换器,让它按 semver 规则排序,而不是自己写一段比较函数。它会逐段当数字比,所以 1.10.0 永远排在 1.9.0 后面;同时能补零规范化,把 v1.2 和 1.2.0 拉齐,最后导出成 JSON 数组,CSV 列或 TypeScript union,直接贴进脚本或 fixture。
如果只是想把重复标签压成唯一一份,可以用 版本号去重工具;只想把格式补齐补零,不动顺序,就用 版本号规范化工具。这三个解析,校验,去重都在浏览器本地完成,git 标签列表也好,包清单导出也好,文本不会发到任何服务器,几 MB 的输入跑起来很轻快。
给依赖管理和发版的几条建议
- 选最新版本时永远用 semver 比较,不要
sort一下取最后一行,字符串排序会在两位数次版本上翻车。 - 比较前先规范化:统一去掉
v前缀,补齐三段,去掉前导零,这样1.2和1.02.0不会被当成两个版本。 - 预发布标签要单独看。
1.0.0-rc.1按 semver 规则小于1.0.0,挑稳定版时记得把带连字符的预发布版排除掉。 - 批量交接时导出带原因和行号的 CSV,无效标签(比如
v1或1.0.0-+)别直接丢掉,带着原因转出来,下游才不会悄悄缺一格。
版本号比较的规则其实很简单:按主版本,次版本,修订号逐段当数字比。难的是手工处理一堆标签时容易忘记这条,顺手 sort 一下就埋了雷。把排序,找最新,补零这几步交给一个按 semver 规则跑的本地工具,既不出 1.10.0 排在 1.9.0 前面的错,也不用把发版清单上传到任何地方。
Made by Toolora · Updated 2026-06-13