首页 > 解决方案 > 语义版本控制 2 零用例

问题描述

在查看SemVer 2.0 指南后,我有以下问题:

根据他们的 BNF 语法,提供的两个示例都被认为是有效的,但这有意义吗?

标签: versioningsemantic-versioning

解决方案


是的,0.0.0有效。是的,-alpha.0有效。

据我所知,包括 BNF 在内的整个规范“是有道理的”,但你必须在这个空间中生活一段时间才能真正理解为什么。规范并不冗长是有原因的。它只说它需要说的话,这样工具制造商可以编写兼容的代码,工具用户可以拥有尽可能多的自由。事实上,在编写规范之前,已经有一些工具在使用,它们使用类似的语法和语义。

所以,0.0.0是有效的,因为他们没有强烈的反对意见。这就像关于编程语言是否应该使用基于零或一的数组索引的无休止的争论。无论哪种方式,都有理由去做,而不是去做。规格不应该比它们绝对需要的更固执己见,这是最好将其留给最终用户的情况之一。工具必须接受0.0.0,但不必生成它。它实际上需要规范中的额外单词,以及工具中的额外代码来解释0.0.0为无效输入。没有客观的理由进行演习。

至于预发布标签中的零字段,可以,但-alpha.00不是。该规范禁止“前导零”,因为它们搞砸了排序。预发行标签由连字符“-”和一系列点分隔的数字或字母数字字段组成。您示例中的“.0”是规范定义的有效数字字段。

考虑项目的初始状态。也许您的 DevOps 团队有一个项目模板设置,其中包含所有构建配置,供您克隆,然后开始处理您的项目。此时有两种选择。要么没有与初始项目状态关联的版本,要么它被初始化为某个起始值。在后一种情况下,从什么版本号开始?0.0.0似乎这可能是个好主意。也许构建自动化会读取那个版本,读取提交消息,然后选择合适的下一个版本。如果必须处理丢失版本的情况,该工具会更加复杂。当一个有效的静态值可以存储在某个地方时,为什么还要为携带该代码而付出运行时的代价呢?


SemVer 规范实现了三个主要目标中的两个:

  1. 它定义了版本 Triple + 标签的语法和语义。
  2. 它定义了版本字符串应该如何排序。
  3. 尝试减轻依赖地狱(仅部分成功)。

#1的一些元素是为了支持#2。规范的“语义”部分是为了减轻依赖地狱的一些痛苦,但语法和其他规则是关于排序的,就像其他任何东西一样。让我们从#2开始:

#2正常版本号必须采用 XYZ 的形式,其中 X、Y 和 Z 是非负整数,并且不得包含前导零。X 是主要版本,Y 是次要版本,Z 是补丁版本。每个元素必须按数字增加。例如:1.9.0 -> 1.10.0 -> 1.11.0。

第一句话:“一个正常的版本号必须采用 XYZ 的形式,其中 X、Y 和 Z 是非负整数,并且不能包含前导零。

非负整数”部分明确允许在任何或所有字段中为零。请参阅0 Wikipedia,特别是“作为数字,0 用作位置值系统中的占位符”。SemVer 属于“地方价值系统”的范畴。

不得包含前导零”位用于支持排序。虽然规范稍后说数字字段是“数字比较”,但数字的 ASCII 码点是 48..57 (0..9),因此不必将每个字段转换为整数,以便排序它。排序时,我们一次一个地比较每个代码点,直到其中一个不同,或者我们发现它们的长度不同。前缀相等,两个字符串中较长的大于较短的。如果我们允许前导零,那么我们将需要首先将字段转换为整数(因为“00”>“0”没有意义),这将需要两倍以上的工作量。

#9预发布版本可以通过在补丁版本之后附加一个连字符和一系列点分隔标识符来表示。标识符必须仅包含 ASCII 字母数字和连字符 [0-9A-Za-z-]。标识符不得为空。数字标识符不得包含前导零。预发布版本的优先级低于相关的普通版本。预发布版本表明该版本不稳定,可能无法满足其相关正常版本所表示的预期兼容性要求。示例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92、1.0.0-xyz.–。

第二句:“标识符必须仅包含 ASCII 字母数字和连字符 [0-9A-Za-z-] ”将预发布标签字段限制为 ASCII 代码点范围 48..57、65..90 和 97..122。这条规则与#11相结合,有助于呈现排序几乎是微不足道的。

#11优先级是指订购时版本之间的比较方式。

  1. 必须通过按顺序将版本分为主要、次要、补丁和预发布标识符来计算优先级(构建元数据不考虑优先级)。

这条规则的原因很简单。如果您将标准 ASCII 排序规则应用于整个字符串而不考虑字段,则会出现许多异常情况,例如1.999.0排序高于2.0.0.

  1. 从左到右比较这些标识符中的每一个时,优先级由第一个差异确定,如下所示: 主要、次要和补丁版本始终以数字进行比较。

示例:1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。

现在这里是#9中的“标识符不得为空”位发挥作用的地方,也是某些字段不禁止零的原因之一。如果给你字符串“1.1”。甚至“1.1”,您可能会推断出次要字段为空,但如果您可以指望存在的值,则更方便,并且不必实现字段为 NULL (C/C++) 或一些的特殊情况其他“未初始化”状态。因此,规范要求在该字段中使用占位符“0”,对于您可能实现解析和比较代码的某些语言,它更容易处理。

请注意,某些工具一直支持缩写形式“1”和“1.1”,但这是实施者的选择并且不符合要求。该规范要求使用完整的三元组进行互操作,并减少人们查看字符串的认知负担。固定的三元组格式不太可能被人类误解。完整的三元组还提供了从其他版本控制方案中消除 SemVer 字符串的一点点歧义(也许是一些固执己见的规范)。

  1. 当主要、次要和补丁相等时,预发布版本的优先级低于普通版本:

示例:1.0.0-alpha < 1.0.0。

实际上有两种预发布版本,但作者显然认为没有必要指出 0.1.0-experimental < 0.1.0。在“0.1.0”上放置一个预发布标签可能看起来是多余的,但规范并没有禁止它,而且在某些情况下,这种事情对某些人来说是实用的。

  1. 必须通过从左到右比较每个点分隔标识符来确定具有相同主版本、次版本和补丁版本的两个预发布版本的优先级,直到发现如下差异:

仅由数字组成的标识符以数字方式进行比较。

带有字母或连字符的标识符按 ASCII 排序顺序进行词汇比较。

数字标识符的优先级始终低于非数字标识符。

如果所有前面的标识符都相等,则较大的预发布字段集比较小的集具有更高的优先级。

示例:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0- rc.1 < 1.0.0。

事情在这里变得有趣。给定 -alpha.1 和 alpha.a,您可能认为可以将最后一个字段视为字母数字(其中一个是对的?),因此我们可以将该字段解释为字母数字并按 ASCII 码点排序,在在这种情况下 -alpha.1 < alpha.a,但是规则是“数字标识符总是比非数字标识符具有更低的优先级”,但这也给了我们 -alpha.1 < alpha.a。即便如此,我们也不能放弃这条规则,因为如果我们有 -alpha.9999 和 -alpha.a 怎么办?

它没有在任何地方指定,但我怀疑以下标签历史似乎比规范可以定义的其他方式更自然:

-0 < -1 < -2 ... < -alpha < -beta

因为从最早的计算开始,这就是一个标准的排序规则。

现在大多数实现都尝试将字段转换为整数或其他数字类型,并将字段视为成功时的数字或失败时的字母数字。如果两个字段都是数字,他们最终会直接比较值,这似乎比比较单个代码点更快,但这忽略了转换开销。除了这些转换所需的非最佳空间和时间外,它们通常会导致不合规的实现。某些编程语言,至少在某些系统上,不能将“-20210313”转换为整数。

事实上,有一些合法的 SemVer 字段无法通过适合任何当前系统的寄存器的任何整数或浮点值精确表示。该规范没有限制任何字段的长度。FAQ 表达了对这一点的看法,但规范没有。即使遵循常见问题解答的建议,即 256 字节的版本字符串足够长,也很容易设计一个可以填充大部分空间的预发布标签字段。因此,要完全符合规范,我们必须:

  • 从左到右扫描正在比较的字段,一次一个代码点,
  • 注意是否包含非数字字符,
  • 并在遇到差异时立即应用上述规则。

当然,由于特定的比较算法将适用于 SemVer 字符串中的每个可能的字段,因此它也恰好是空间和时间最优的。


推荐阅读