最小版本选择 (MVS)

Minimal version selection (MVS) 最小版本选择 (MVS)

原文:https://go.dev/ref/mod#minimal-version-selection

​ Go 使用一种叫做最小版本选择(MVS)的算法来选择一组模块的版本,以便在构建包时使用。在Russ Cox的Minimal Version Selection(最小版本选择)中详细描述了MVS

​ 从概念上讲,MVS在模块的有向图上操作,用go.mod文件指定。图中的每个顶点代表一个模块版本。每条边代表一个依赖的最小需求版本,用require指令指定。该图可以通过主模块的go.mod文件中的excludereplace指令以及go.work文件中的replace指令来修改。

​ MVS 生成build list(构建列表)作为输出,即用于构建的模块版本列表。

​ MVS 从主模块(图中没有版本的特殊顶点)开始,遍历图,跟踪每个模块所需的最高版本。在遍历结束后,所需的最高版本组成了构建列表:它们是满足所有requirements的最小版本。

​ 可以用 go list -m all 命令来检查构建列表。与其他依赖项管理系统不同,构建列表不保存在一个 “lock"文件中。MVS是确定性的,当新版本的依赖项发布时,构建列表不会改变,因此在每个模块感知命令的开头使用MVS进行计算。

​ 考虑下图中的示例。主模块要求模块 A 的版本为 1.2 或更高,模块 B 的版本为 1.2 或更高。A 1.2和B 1.2分别需要C 1.3和C 1.4。C 1.3和C 1.4都需要D 1.2。

Module version graph with visited versions highlighted

​ Module version graph with visited versions highlighted

​ 模块版本图,访问过的版本突出显示

​ MVS访问并加载用蓝色突出显示的每个模块版本的go.mod文件。在该图遍历结束时,MVS返回一个包含加粗版本的构建列表:A 1.2, B 1.2, C 1.4, 和 D 1.2。注意,B和D的更高版本是可用的,但MVS并没有选择它们,因为没有任何东西需要它们。

Replacement 替换

​ 模块的内容(包括它的go.mod文件)可以用主模块的go.mod文件或工作区的go.work文件中的replace指令来替换。replace指令可以应用于模块的特定版本或模块的所有版本。

​ 替换会改变模块图,因为替换的模块可能与被替换的版本有不同的依赖项。

​ 考虑下面的例子,其中 C 1.4被 R 替换。R依赖于D 1.3而不是D 1.2,所以MVS返回一个包含A 1.2、B 1.2、C 1.4(用R替换)和D 1.3的构建列表。

Module version graph with a replacement

​ Module version graph with a replacement

​ 带替换的模块版本图

Exclusion 排除

​ 模块也可以用主模块go.mod文件中的exclude 指令在特定的版本中被排除。

​ 排除也会改变模块图。当一个版本被排除时,它被从模块图中移除,对它的 requirements 被重定向到下一个更高版本。

​ 考虑下面的例子。C 1.3 已经被排除。MVS 将表现得好像 A 1.2 需要 C 1.4(下一个更高版本)而不是 C 1.3 。

Module version graph with an exclusion

​ Module version graph with an exclusion

​ 带排除的模块版本图

Upgrades 升级

go get 命令可以用来升级一组模块。为了执行升级,go 命令在运行 MVS 之前更改了模块图,增加了从访问的版本到升级的版本的 edges (边)。

Consider the example below. Module B may be upgraded from 1.2 to 1.3, C may be upgraded from 1.3 to 1.4, and D may be upgraded from 1.2 to 1.3.

​ 考虑下面的例子。模块 B 可以从 1.2 升级到 1.3,C 可以从 1.3 升级到 1.4,D 可以从 1.2 升级到 1.3。

Module version graph with upgrades

​ Module version graph with upgrades

​ 带有升级的模块版本图

​ 升级(和降级)可能会增加或移除间接依赖项。在这种情况下,E 1.1和F 1.1在升级后出现在构建列表中,因为E 1.1是B 1.3所需要的。

​ 为了保护升级,go命令更新了go.mod中的 requirements 。它将更改B的 requirement 为1.3版本。它还将添加对C 1.4和D 1.3的 requirements ,并加上// indirect注释,因为这些版本在其他情况下不会被选中。

Downgrade 降级

go get 命令也可以用来降级一组模块。为了执行降级,go命令通过移除降级后的版本来更改模块图。它也会移除依赖于被移除版本的其他模块的版本,因为它们可能与降级后的依赖版本不兼容。如果主模块需要一个被降级移除的模块版本,该 requirement 将被更改为未被移除的先前版本。如果没有先前的版本,该 requirement 将被放弃。

​ 考虑下面的例子。假设发现 C 1.4 有问题,所以我们降级到 C 1.3。C 1.4被从模块图中移除。B 1.2 也被移除,因为它需要 C 1.4 或更高版本。主模块对B的 requirement 被改为1.1。

Module version graph with downgrade

​ Module version graph with downgrade

​ 带有降级的模块版本图

go get也可以完全移除依赖项,在参数后使用@none后缀。这与降级的工作原理类似。所有被命名的模块的版本都会从模块图中移除。

最后修改 October 10, 2024: 更新 (a4b8f85)