大部分语言都有版本管理工具,比如nodejs的npm,python中的pip,java里的maven,但是go语言的版本管理经历了漫长的演进历程:
开始Golang这么设计是有原因的,因为Google是一个实践Mono Repo(把所有的相关项目都放在一个仓库中)的公司。但更多的公司和组织更多的是用Multi Repo(按模块分为多个仓库),GOPATH 至少解决了第三方源码依赖的问题,虽然它还不够完美。
go vendor
命令。当然,如果构建程序的时候,希望使用vendor中的依赖go build -mod vendor
在go1.12以前,我们知道golang的依赖包管理仅仅只是可用而已。go1.12之后,go mod才真正解决了依赖包管理的几个核心问题。
go 通过go mod 命令来管理依赖包。其中提供了以下几个命令,一般我用的比较多的是go mod tidy
download | download modules to local cache(下载依赖包) |
---|---|
edit | edit go.mod from tools or scripts(编辑go.mod) |
graph | print module requirement graph (打印模块依赖图) |
verify | initialize new module in current directory(在当前目录初始化mod) |
tidy | add missing and remove unused modules(拉取缺少的模块,移除不用的模块) |
vendor | make vendored copy of dependencies(将依赖复制到vendor下) |
verify | verify dependencies have expected content (验证依赖是否正确) |
why | explain why packages or modules are needed(解释为什么需要依赖) |
Go module最重要的是go.mod文件的定义,它用来标记一个module和它的依赖库以及依赖库的版本。作用有点类似于Maven里面的pom.xml文件。
Go module遵循语义化版本规范 2.0.0。语义化版本规范 2.0.0规定了版本号的格式,版本格式定义为:主版本号.次版本号.修订号,版本号递增规则如下:
先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。
下面我们来看一下go.mod的文件到底长啥样:
module github.com/panicthis/modfile
go 1.15
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
)
go.mod下还会有一个go.sum文件,其中包含特定模块版本内容的预期加密哈希,go命令使用go.sum文件确保这些模块的未来下载检索与第一次下载相同的位,以确保项目所依赖的模块不会出现意外更改,无论是出于恶意、意外还是其他原因。
go.mod的第一行是module path, 一般采用仓库+module name的方式定义。这样我们获取一个module的时候,就可以到它的仓库中去查询,或者让go proxy到仓库中去查询。
如果你的版本已经大于等于2.0.0,按照Go的规范,你应该加上major的后缀,module path改成下面的方式:
module github.com/panicthis/modfile/v2
module github.com/panicthis/modfile/v3
而且引用代码的时候,也要加上v2
、v3
、vx
后缀,以便和其它major版本进行区分。这是一个很奇怪的约定,带来的好处是你一个项目中可以使用依赖库的不同的major版本,它们可以共存。
第二行是go directive。格式是 go 1.xx
,它并不是指你当前使用的Go版本,而是指名你的代码所需要的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为它生成的一个类似符合语义化版本2.0.0版本,实际这个库并没有发布这个版本。正式因为这个依赖库没有发布版本,而go module需要指定这个库的一个确定的版本,所以才创建的这样一个伪版本号。
go module的目的就是在go.mod中标记出这个项目所有的依赖以及它们确定的某个版本。
这里的20200330080233
是这次提交的时间,格式是yyyyMMddhhmmss
, 而e4ea8bd1cbed
就是这个版本的commit id,通过这个字段,就可以确定这个库的特定的版本。
而前面的v0.0.0可能有多种生成方式,主要看你这个commit的base version:
伪版本号之所以存在主要是由于提交的记录没有打tag, 打了tag就会直接使用tag的版本号。
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后缀:
有些库后面加了incompatible后缀,但是你如果看这些项目,它们只是发布了v2.2.1的tag,并没有+incompatible后缀。
github.com/cenk/backoff v2.2.1+incompatible
这些库采用了go.mod的管理,但是不幸的是,虽然这些库的版major版本已经大于等于2了,但是他们的module path中依然没有添加v2, v3这样的后缀。
所以go module把它们标记为incompatible的,虽然可以引用,但是实际它们是不符合规范的。
如果你想在你的项目中跳过某个依赖库的某个版本,你就可以使用这个段。
exclude (
go.etcd.io/etcd/client/v2 v2.305.0-rc.0
go.etcd.io/etcd/client/v3 v3.5.0-rc.0
)
这样,Go在版本选择的时候,就会主动跳过这些版本,比如你使用go get -u ......
或者go get github.com/xxx/xxx@latest
等命令时,会执行version query的动作,这些版本不在考虑的范围之内。
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
replace github.com/coreos/bbolt => ../R
比如etcd v3.3.x的版本中错误的使用了github.com/coreos/bbolt
作为bbolt的module path,其实这个库在它自己的go.mod中声明的module path是go.etcd.io/bbolt
,又比如etcd使用的grpc版本有问题,你也可以通过replace替换成所需的grpc版本。甚至你觉得某个依赖库有问题,自己fork到本地做修改,想调试一下,你也可以替换成本地的文件夹。
replace可以替换某个库的所有版本到另一个库的特定版本,也可以替换某个库的特定版本到另一个库的特定版本。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。