总览
给定版本号MAJOR.MINOR.PATCH:
- MAJOR 在进行不兼容的API更改时增加
- MINOR 以向后兼容的方式添加功能
- PATCH 以向后兼容的方式修补bug
预发布和构建元数据的附加标签可作为MAJOR.MINOR.PATCH格式的扩展。
介绍
在软件管理的世界里,有一个可怕的地方叫做“依赖地狱”。您的系统越大,集成到软件中的包越多,您就越有可能发现自己有一天会陷入绝望的深渊。
在有许多依赖项的系统中,发布新的包版本很快就会变成一场噩梦。如果依赖性规范太紧,您将面临版本锁定的危险(无法升级包而不需要发布每个依赖包的新版本)。如果过于松散地指定依赖项,那么您将不可避免地被版本的杂乱无章(假设与更多的未来版本兼容而不是合理)所咬伤。依赖地狱是当版本锁定和/或版本混杂阻止您轻松安全地向前推进项目时所处的位置。
作为这个问题的解决方案,我们提出了一组简单的规则和需求,这些规则和需求决定版本号是如何分配和递增的。这些规则基于但不一定仅限于在封闭和开放源码软件中普遍使用的现有通用实践。要使这个系统正常工作,首先需要声明一个公共API。这可以由文档组成,也可以由代码本身强制执行。不管怎么说,这个API的清晰和精确是很重要的。一旦您标识了您的公共API,就可以通过对版本号的特定增量来传递对它的更改。考虑X.Y.Z(Major.Minor.Patch)的版本格式。Bug修复不影响API增量补丁版本,向后兼容API添加/更改增加次要版本,向后不兼容API更改增加主版本。
我们称这个系统为“语义版本控制”。在此方案下,版本号及其更改方式传达了底层代码的含义,以及从一个版本修改到下一个版本的含义。
语义版本控制说明
本文件中的关键词“必须”、“不得”、“要求”、“应”、“不”、“应”、“不应”、“建议”、“可能”和“可选”应解释为RFC 2119所述。
- 使用语义版本控制的软件必须声明公共API。这个API可以在代码本身中声明,也可以严格地存在于文档中。不管怎么做,它应该是精确和全面的。
- 普通版本号必须采用X.Y.Z形式,其中X、Y和Z是非负整数,不能包含前导零。X是主要版本,Y是次要版本,Z是补丁版本。每个元素必须在数字上增加。例如:1.9.0->1.10.0->1.11.0。
- 一旦发布了版本化的包,就不得修改该版本的内容。任何修改都必须作为新版本发布
- 主版本号为0的版本(0.y.z)用于初始开发。此版本中任何事情都可能在任何时候改变。公共API不应被认为是稳定的。
- 版本1.0.0定义了公共API。此版本之后版本号增加的方式取决于此公共API及其更改方式。
- 如果只引入了向后兼容的bug修复,则必须增加补丁版本Z(x.y.z\x>0)。错误修复定义为修复不正确行为的内部更改。
- 如果在公共API中引入了新的、向后兼容的功能,则必须增加次要版本Y(x.Y.z\x>0)。如果任何公共API功能被标记为不推荐使用,则必须对其进行增量。如果在私有代码中引入了大量的新功能或改进,则可能会增加。它可能包括补丁级别的更改。当次要版本增加时,必须将补丁版本重置为0。
- 如果在公共API中引入了任何向后不兼容的更改,则必须增加主版本X(X.y.z\x>0)。它还可能包括小的和补丁级别的更改。当主版本增加时,必须将修补程序和次要版本重置为0。
- 预发布版本可能通过紧接补丁版本之后附加连字符和一系列点分隔标识符来表示。标识符必须仅包括ASCII字母数字和连字符[0-9A-Za-z-]。标识符不得为空。数字标识符不得包括前导零。预发布版本的优先级低于关联的正常版本。预发布版本表示该版本不稳定,可能不满足其关联的正常版本所表示的预期兼容性要求。例子:1.0.0-alpha,1.0.0-α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-]。标识符不得为空。在确定版本优先级时,必须忽略生成元数据。因此,只有构建元数据不同的两个版本具有相同的优先级。例子:1.0.0-alpha+001,1.0.0+20130313144700,1.0.0-β+exp.sha.5114f85,1.0.0+21AF26D3-117B344092BD。
- 优先级是指在排序时如何将版本进行比较。
1)优先级必须通过按顺序将版本划分为主、次要、修补程序和发布前标识符来计算(构建元数据不显示为优先级)。
2)当从左到右比较每个标识符时,优先级由第一个差异决定,如下所示:主版本、次版本和补丁版本总是进行数字比较。
Example: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1.
3)当主要版本、次要版本和修补程序相等时,预发布版本的优先级低于正常版本:
Example: 1.0.0-alpha < 1.0.0.
4)具有相同的主版本、次版本和补丁版本的两个预发行版本的优先级必须通过比较从左到右的每个点分隔的标识符来确定,直到发现如下差异:
①对仅由数字组成的标识符进行了数值比较。
②使用字母或连字符的标识符按ASCII排序顺序进行词汇比较。
③数字标识符的优先级总是低于非数字标识符。
④如果前面的所有标识符相等,则较大的预释放字段集的优先级高于较小的字段集。
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.
为什么使用语义版本控制
这不是一个新的或革命性的想法。事实上,你可能已经做了一些接近这个的事情。问题是“亲近”不够好。如果不遵从某种形式的规范,版本号对依赖关系管理基本上是无用的。通过给出上述想法的名称和清晰的定义,就可以很容易地将您的意图传达给您的软件用户。一旦这些意图明确,就可以最终制定灵活(但不太灵活)的依赖规范。
一个简单的示例将演示语义版本控制如何使依赖关系成为过去。比如一个叫做“消防车”的库。它需要一个名为“梯子”的语义验证包。在创建消防车时,梯子的版本为3.1.0。由于消防车使用了一些最初在3.1.0中引入的功能,所以您可以安全地指定梯子依赖项大于或等于3.1.0但小于4.0.0。现在,当阶梯版本3.1.1和3.2.0可用时,您可以将它们发布到您的包管理系统中,并知道它们将与现有的依赖软件兼容。
当然,作为一个负责任的开发人员,您需要验证任何包的升级功能是否如广告所示。现实世界是个乱七八糟的地方,除了保持警惕之外,我们什么也做不了。您可以做的是让语义版本化为您提供一种合理的方法来发布和升级包,而不必提交新版本的依赖包,从而节省您的时间和麻烦。
如果所有这些听起来都是可取的,那么开始使用语义版本控制所需要做的就是声明正在这样做,然后遵循规则。链接到这个网站,从您的自述,以便其他人知道这些规则,并从中受益。