关于版本控制语义的一句话

说到更新,让我们来谈谈版本管理,尤其是语义版本管理。如果你要使用的外部库遵循语义版本管理规则,那么这将对你的开发和更新产生非常积极和令人放心的影响。让我们来看看这究竟意味着什么。

什么是版本控制语义?

版本管理就是给源代码的版本编号。我们都熟悉 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 等)。如果你想定义依赖关系的最小版本,同时接受新补丁和次版本,但拒绝在更新外部库时自动更新主版本(可能带来破坏性修改),那么这将是一个特别好的选择。这就是为什么它是最受欢迎的操作符之一。

当然,关于 Composer 依赖库版本的表示,我们可以更详细地解释一下。

在 Composer 的世界中,包的版本控制是非常重要的,它确保你的项目依赖的库版本是可控的,以便你的项目可以稳定运行。

确切版本号

当你指定一个确切的版本号时,Composer 会精确安装这个版本。这对于那些需要确保稳定性的项目非常有用。例如,"1.2.3" 就代表你需要安装的这个库的版本必须是 1.2.3。

范围版本号

这种方式允许你指定一个版本范围,Composer 会在这个范围内选择一个最合适的版本进行安装。例如,">=1.2.3 <2.0.0" 表示 Composer 可以选择 1.2.3 到 2.0.0 之间的任何一个版本进行安装。

波浪线版本号

波浪线 "~" 是用来表示最小稳定版本的。例如,"~1.2" 表示 Composer 可以安装 1.2 以上的任何版本,但是不包含 2.0 版本。所以,它实际上等同于 ">=1.2,<2.0"。

插入号版本号

插入号 "^" 是用来表示兼容性的。例如,"^1.2" 表示 Composer 可以安装 1.2 以上的版本,并且如果主版本号大于 0,则不包含主版本号+1 的版本。比如,如果主版本号是 1,那么它不会安装 2.0 的版本。但如果主版本号是 0,比如 0.3,那么 "^0.3" 就等同于 ">=0.3 <0.4"。

这些就是 Composer 中依赖库版本表示的主要方式。在 "composer.json" 文件中,你可以根据你的需求,灵活选择这些版本表示方式,来管理你的项目依赖。同时,你也可以通过 "composer show" 命令查看已经安装的依赖库及其版本信息。

它的可能性是无限的,一旦掌握了这种符号,就可以放心地更新项目的依赖关系。就良好实践而言,接受依赖项的所有新补丁和次版本始终是一个明智的选择。千万不要在没有任何更新条件或可能性的情况下,将依赖关系锁定在一个极其特定的版本上。事实上,如果外部库严格遵守语义版本,就不会与现有代码产生冲突。破坏性更改只保留给主要版本。因此,在更新你的依赖关系时,你不应自动接受主要版本:你有可能必须调整你的代码才能使其正常工作。