关于版本控制语义的一句话
说到更新,让我们来谈谈版本管理,尤其是语义版本管理。如果你要使用的外部库遵循语义版本管理规则,那么这将对你的开发和更新产生非常积极和令人放心的影响。让我们来看看这究竟意味着什么。
什么是版本控制语义?
版本管理就是给源代码的版本编号。我们都熟悉 1.0、1.5.0、2.0.0 等版本。语义版本管理为每个数字添加了语义,也就是精确的含义。我们以版本 2.3.15 为例。下面是语义版本管理对版本号的分解:
-
2 表示主要版本。主要版本可以引入新功能、修正错误,最重要的是可以打破向后兼容性。最后一点是最重要的。事实上,从一个主版本到另一个主版本,方法签名甚至整个类名都可能发生变化,有些还可能消失。因此,在升级到比当前版本更高的主要版本时,必须格外小心,并测试所有内容是否仍然有效。通常情况下,版本更新日志会提供你需要做出的更改,以便与新的主版本保持一致。
-
3 表示次版本。与主版本一样,次版本也会带来新功能和错误修复。主要区别在于,次要版本不能做出破坏向后兼容性的更改。这意味着你可以将依赖关系升级到下一个次要版本,以利用所有新功能和错误修复,而不必担心升级时会破坏你的代码。不过,在次版本升级时运行所有测试也不是多余的。你永远不会知道。小版本通常会触发代码弃用消息。这些信息会告诉你哪些方法不应该再使用,因为它们肯定会在下一个大版本中被删除。如果能在开发过程中考虑到弃用信息,就能在将依赖关系更新到下一个主版本时为自己省去很多工作。
-
15 表示补丁编号。补丁只包含错误修复和安全修复。它不包含新功能。你应该考虑始终在项目中安装依赖项的新补丁。
我们可以看到语义版本管理的优势:平滑、逻辑性和一致性。显然,还有其它一些变体,如 Alpha、Beta、Release Candidate 和 Golden Master。但这些都比较少见。
如何处理语义版本控制
语义版本管理还包括一种特殊的符号,可让依赖关系管理器知道如何安装新版本以及何时更新依赖关系。让我们以 Composer 用来安装依赖项的文件摘要为例(这也是许多依赖项管理器的原理):
{
"require": {
"php": ">=7.3",
"symfony/dotenv": "3.4.*",
"symfony/event-dispatcher-contracts": "~1.1",
"symfony/http-client": "^4.2.2"
}
}
该代码段描述了四个依赖项:PHP 的最小版本以及三个外部库。这些库是什么并不重要。我们可以在这里找到四种不同的方法来定义我们希望在依赖项中接受的版本。让我们看看它们是什么。
第一种方法是使用 >=
运算符来写入我们观察到的版本。这是最容易理解的一种方式:我们希望接受大于或等于指定版本的所有版本。在这里,我们的应用程序接受所有高于 7.3 版的 PHP 版本,以及 7.3 版本身。当然,依赖关系管理器也接受其它此类操作符: =
,<
,>
和 <=
。你还可以将这些操作符组合起来,以获得非常精确的版本限制。例如,可以写成 >=1.2.0 <2.0.0
。
第二个运算符非常有名,因为它在许多其它情况下都会用到。它就是通配符,用 *
表示。这个符号简单地表示,你可以用任何你想要的东西来替换它。在前面的示例中,我们接受了依赖项 3.4 版本的所有补丁版本。这样,依赖项就可以只从错误修复中受益,而无需更新次版本。这个通配符可以放在版本号的任何位置。例如,符号 3.*
将受益于主要版本 3 的所有次要版本。
下面的符号是使用波浪符号 ~
。该运算符表示你只受益于给定版本的补丁。在示例中,我们将受益于依赖关系 1.1 版本的所有补丁版本(即 1.1.0、1.1.1、1.1.2 等)。这与通配符非常相似,只是通配符不能放在版本号的任何位置,而只涉及补丁。此外,值得注意的是,Composer
对转折号的解释略有不同:它也允许使用次版本,而不仅仅是补丁。如果你在使用 Composer
时只想使用补丁版本,而不想使用次版本,就必须使用通配符操作符。
最后一个运算符是括号运算符,用 ^
表示。在上例中,该运算符允许所有补丁版本以及主版本 4 的次版本(即 4.2.2、4.2.3、4.4.0 等)。如果你想定义依赖关系的最小版本,同时接受新补丁和次版本,但拒绝在更新外部库时自动更新主版本(可能带来破坏性修改),那么这将是一个特别好的选择。这就是为什么它是最受欢迎的操作符之一。
它的可能性是无限的,一旦掌握了这种符号,就可以放心地更新项目的依赖关系。就良好实践而言,接受依赖项的所有新补丁和次版本始终是一个明智的选择。千万不要在没有任何更新条件或可能性的情况下,将依赖关系锁定在一个极其特定的版本上。事实上,如果外部库严格遵守语义版本,就不会与现有代码产生冲突。破坏性更改只保留给主要版本。因此,在更新你的依赖关系时,你不应自动接受主要版本:你有可能必须调整你的代码才能使其正常工作。