go.mod
go.modgo.mod
一个go.mod内容类似下面的格式:
module github.com/panicthis/modfile
go 1.16
require (
github.com/cenk/backoff v2.2.1+incompatible
github.com/coreos/bbolt v1.3.3
github.com/edwingeng/doublejump v0.0.0-20200330080233-e4ea8bd1cbed
github.com/stretchr/objx v0.3.0 // indirect
github.com/stretchr/testify v1.7.0
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/etcd/client/v2 v2.305.0-rc.1
go.etcd.io/etcd/client/v3 v3.5.0-rc.1
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b // indirect
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 // indirect
)
exclude (
go.etcd.io/etcd/client/v2 v2.305.0-rc.0
go.etcd.io/etcd/client/v3 v3.5.0-rc.0
)
retract (
v1.0.0 // 废弃的版本,请使用v1.1.0
)
虽然是一个简单的文件,但是里面的乾坤不少,让我们依次介绍它们。
语义化版本 2.0.0
Go module遵循语义化版本规范 2.0.0。语义化版本规范 2.0.0规定了版本号的格式,每个字段的意义以及版本号比较的规则等等。
v1.3.0v2.0.0-rc.1
module path
go.mod的第一行是module path, 一般采用仓库+module name的方式定义。这样我们获取一个module的时候,就可以到它的仓库中去查询,或者让go proxy到仓库中去查询。
module github.com/panicthis/modfile
如果你的版本已经大于等于2.0.0,按照Go的规范,你应该加上major的后缀,module path改成下面的方式:
module github.com/panicthis/modfile/v2
module github.com/panicthis/modfile/v3
v2v3vx
这是一个很奇怪的约定,带来的好处是你一个项目中可以使用依赖库的不同的major版本,它们可以共存。
go directive
go 1.xx
go 1.16
因为Go的标准库也有所变化,一些新的API也被增加进来,如果你的代码用到了这些新的API,你可能需要指名它依赖的go版本。
这一行不是必须的,你可以不写。
require
v1.3.0
正式的版本号我们就不需要介绍了,大家都懂:
github.com/coreos/bbolt v1.3.3
伪版本号
github.com/edwingeng/doublejump v0.0.0-20200330080233-e4ea8bd1cbed
v0.0.0-20200330080233-e4ea8bd1cbed
正是因为这个依赖库没有发布版本,而go module需要指定这个库的一个确定的版本,所以才创建的这样一个伪版本号。
go module的目的就是在go.mod中标记出这个项目所有的依赖以及它们确定的某个版本。
20200330080233yyyyMMddhhmmsse4ea8bd1cbed
v0.0.0
-
vX.0.0-yyyymmddhhmmss-abcdefabcdef: 如果没有base version,那么就是vX.0.0的形式
-
vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef: 如果base version是一个预发布的版本,比如vX.Y.Z-pre,那么它就用vX.Y.Z-pre.0的形式
-
vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef: 如果base version是一个正式发布的版本,那么它就patch号加1,如vX.Y.(Z+1)-0
indirect注释
go.etcd.io/bbolt v1.3.6 // indirect
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b // indirect
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 // indirect
indirect
如果用一句话总结,间接的使用了这个库,但是又没有被列到某个go.mod中,当然这句话也不算太准确,更精确的说法是下面的情况之一就会对这个库加indirect后缀:
-
当前项目依赖A,但是A的go.mod遗漏了B, 那么就会在当前项目的go.mod中补充B, 加indirect注释
-
当前项目依赖A,但是A没有go.mod,同样就会在当前项目的go.mod中补充B, 加indirect注释
-
当前项目依赖A,A又依赖B,当对A降级的时候,降级的A不再依赖B,这个时候B就标记indirect注释
incompatible
+incompatible
github.com/cenk/backoff v2.2.1+incompatible
这些库采用了go.mod的管理,但是不幸的是,虽然这些库的版major版本已经大于等于2了,但是他们的module path中依然没有添加v2、v3这样的后缀。
incompatible
exclude
如果你想在你的项目中跳过某个依赖库的某个版本,你就可以使用这个字段。
exclude (
go.etcd.io/etcd/client/v2 v2.305.0-rc.0
go.etcd.io/etcd/client/v3 v3.5.0-rc.0
)
go get -u ......go get github.com/xxx/xxx@latest
replace
replace也是常用的一个手段,用来解决一些错误的依赖库的引用或者调试依赖库。
replace github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.3
replace github.com/panicthis/A v1.1.0 => github.com/panicthis/R v1.8.0
github.com/coreos/bboltgo.etcd.io/bbolt
甚至你觉得某个依赖库有问题,自己fork到本地做修改,想调试一下,你也可以替换成本地的文件夹。
replace可以替换某个库的所有版本到另一个库的特定版本,也可以替换某个库的特定版本到另一个库的特定版本。
- 引用未发布的本地模块场景
replace github.com/coreos/bbolt => ../R
github.com/coreos/bbolt../R
retract
retract是go 1.16中新增加的内容,借用学术界期刊撤稿的术语,宣布撤回库的某个版本。
如果你误发布了某个版本,或者事后发现某个版本不成熟,那么你可以推一个新的版本,在新的版本中,声明前面的某个版本被撤回,提示大家都不要用了。
撤回的版本tag依然还存在,go proxy也存在这个版本,所以你如果强制使用,还是可以使用的,否则这些版本就会被跳过。
和exclude的区别是retract是这个库的owner定义的, 而exclude是库的使用者在自己的go.mod中定义的。