语义版本控制 2 零个案例

Semantic Versioning 2 zero cases

看完SemVer 2.0 guidelines后,我有以下问题:

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

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

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

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

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

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


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

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

#1 的一些元素是用来支持#2 的。规范的“语义”位可以减轻 依赖地狱 的一些痛苦,但语法和其他规则与排序有关,与其他任何东西一样多。让我们从 #2:

开始

#2 A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: 1.9.0 -> 1.10.0 -> 1.11.0.

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

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

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

#9 A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphens [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes. Pre-release versions have a lower precedence than the associated normal version. A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. Examples: 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-x-y-z.–.

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

#11 Precedence refers to how versions are compared to each other when ordered.

  1. Precedence MUST be calculated by separating the version into major, minor, patch and pre-release identifiers in that order (Build metadata does not figure into precedence).

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

  1. Precedence is determined by the first difference when comparing each of these identifiers from left to right as follows: Major, minor, and patch versions are always compared numerically.

Example: 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. When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version:

Example: 1.0.0-alpha < 1.0.0.

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

  1. Precedence for two pre-release versions with the same major, minor, and patch version MUST be determined by comparing each dot separated identifier from left to right until a difference is found as follows:

Identifiers consisting of only digits are compared numerically.

Identifiers with letters or hyphens are compared lexically in ASCII sort order.

Numeric identifiers always have lower precedence than non-numeric identifiers.

A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal.

Example: 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

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

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

事实上,有些合法的 SemVer 字段无法用适合任何当前系统的寄存器的任何整数或浮点值来准确表示。该规范对任何字段的长度没有限制。 FAQ 表达了对这一点的看法,但 spec 没有。即使遵循 FAQ 的建议,即 256 字节的版本字符串已经足够长,也很容易设计出可以填充其中大部分内容的预发布标签字段 space。因此,要完全符合规范,我们必须:

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

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