gomod
# go mod 包管理
知乎 go.mod 各版本差异 (opens new window)
# 1. go mod 命令
go help mod
Usage:
go mod <command> [arguments]
The commands are:
download download modules to local cache
edit edit go.mod from tools or scripts
graph print module requirement graph
init initialize new module in current directory
tidy add missing and remove unused modules
vendor make vendored copy of dependencies
verify verify dependencies have expected content
why explain why packages or modules are needed
常用命令
| 命令 | 作用 |
|---|---|
go list -m all | 列出当前模块依赖的所有模块 |
go list -u -m all | 列出当前模块依赖中可升级的模块 |
go get -u | 升级所有依赖至最新版本 |
go get -u=patch | 升级所有依赖至最新的修订版本 |
go mod tidy | 清理未使用/生效的依赖 |
# 2. go.mod 文件语法
在目前的版本当中,go.mod 文件中主要有四个部分组成:
- module
用来声明当前 module,如果当前版本大于 v1 的话,还需要在尾部显式的声明 /vN。
module /path/to/your/mod/v2
module github.com/Xuanwo/go-mod-intro/v2
2
3
- require
这是最为常用的部分,在 mod 之后可以写任意有效的、能指向一个引用的字符串,比如 Tag,Branch,Commit 或者是使用 latest 来表示引用最新的 commit。如果对应的引用刚好是一个 Tag 的话,这个字符串会被重写为对应的 tag;如果不是的话,这个字符串会被规范化为形如 v2.0.0-20180128182452-d3ae77c26ac8 这样的字符串。我们后面会发现这个字符串与底层的 mod 存储形式是相对应的。
require /your/mod tag/branch/commit
require github.com/google/go-github/v24 v24.0.1
require gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8
2
3
4
5
- replace
replace这边的花样比较多,主要是两种,一个是与 require 类似,可以指向另外一个 repo,另一种是可以指向本地的一个目录。加了 replace 的话,go 在编译的时候就会使用对应的项目代码来替换。需要注意的是这个只作用于当前模块的构建,其他模块的 replace 对它不生效,同理,它的 replace 对其他模块也不会生效。
需要额外注意的是,如果引用一个本地路径的话,那这个目录下必须要有 go.mod 文件,这个目录可以是绝对路径,也可以是相对路径。
replace original_name => real_name tag/branch/commit
replace original_name => local_path
replace test.dev/common => git.example.com/bravo/common.git v0.0.0-20190520075948-958a278528f8
replace test.dev/common => ../../another-porject/common-go
replace github.com/qiniu/x => github.com/Xuanwo/qiniu_x v0.0.0-20190416044656-4dd63e731f37
2
3
4
5
6
7
- exclude
这个用的比较少,主要是为了能在构建的时候排除掉特定的版本,跟 replace 一样,只能作用于当前模块的构建。
exclude /your/mod tag/branch/commit
# 3. 版本选择
Go Module 使用的最小版本选择(Minimal Version Selection)算法。我们为每个模块指定的依赖都是可用于构建的最低版本,最后实际选择的版本是所有出现过的最低版本中的最大值。
在 require 中指定的版本为依赖的最小版本号,所以最后会选择使用在所有依赖选择中出现最高版本。
# 4. 语义导入版本控制
这就为我们带来了语义导入版本控制(Semantic Import Versioning)。
例:V1.2.3
- 1 为主版本号
- 2 为次版本号
- 3 为补丁版本号
当主版本号大于 1 时,这个 Module 的 import path 必须在尾部加上 /vN
- 在 go.mod 文件中:
module github.com/my/mod/v2 - 在 require 的时候:
require github.com/my/mod/v2 v2.0.0 - 在 import 的时候:
import "github.com/my/mod/v2/mypkg"
当主版本号为 v0 或者 v1 时,尾部的 /v0 或 /v1 可以省略。
# 5. 伪版本
对于 go.mod 中形如下面的版本依赖,称为伪版本
tygit.touch4.me/rich-joy/common v1.6.3-0.20210419065331-0543c5926b64
github.com/labstack/echo v3.3.10+incompatible
2
更多伪版本格式如下:
- · Vx.0.0-yyyymmddhhmms-abcdefxyz 当在目标提交之前没有使用适当的主版本进行早期版本提交时
- · vX.Y.Z-pre.0.yyyymmddhhmms -abcdefxyz 当目标提交之前的最新版本提交是 vX.Y.Z-pre 时
- · vX.Y.(Z + 1) 0.yyyymmddhhmms -abcdefxyz 当目标提交之前的最新版本提交是 vX.Y.Z 时
作为一种最佳实践,伪版本字符串不应该是手工输入的。go 命令将接受普通的提交散列并自动将其转换为伪版本。此方法有助于根据生成的时间戳比较修订。
例如,一个 go get 命令可能只使用模块查询的提交散列(githash):3
% go get tygit.touch4.me/rich-joy/common@0543c5926b6441fdd31c9bd260f18809d65e7367
go: tygit.touch4.me/rich-joy/common 0543c5926b6441fdd31c9bd260f18809d65e7367 => v1.6.3-0.20210419065331-0543c5926b64
go: downloading tygit.touch4.me/rich-joy/common v1.6.3-0.20210419065331-0543c5926b64
# 最后版本依赖被更新为:
tygit.touch4.me/rich-joy/common v1.6.3-0.20210419065331-0543c5926b64
2
3
4
5
在本地开发中推荐使用replace,但提交代码时对于不能马上生成 tag 时,推荐使用伪版本