I have recently studied go mod
and have compiled it into an article. This article is a systematic look at go mod
, not a simple introduction to how to use it.
Before go mod
came out, the community used a model similar to NodeJS
, vendor
, which meant that all packages were stored in the vendor
directory, but this approach was obviously not elegant enough, and then came go mod
, which has become the standard for module management in the Go community until today.
Concepts
- module. a folder with
go.mod
is a module. in short, a module that contains several packages. - package. A number of
*.go
files in a folder make up a package, and at the top of each.go
file, there is apackage xxx
to declare what package it is.
Versions
module
has a version, Go adheres to Semantic Versioning 2.0.0, so versions will basically look like this: v0.0.1
, v1.2.3
, v2.0.0
and so on. Some repositories don’t have a tag, so Go will automatically generate a version number, called pseudo-version
, which is a pseudo-version number, e.g. v0.0.0-20191109021931-daa7c04131f5
. The approximate format is.
- vX.0.0 base version
- Timestamp in the format yyyymmddhhmmss, the time value is the UTC time of that commit
- The first 12 characters of the commit hash
The three above, linked together with -
.
Big versions
Maybe we have a program that comes up 2.0
, 3.0
, 4.0
, so what is the solution that Go offers? The answer is to create a subdirectory, e.g. for v2
versions, create a v2
subdirectory, for v3
create a v3
subdirectory. Or, if it’s at the top level, add a v2
or v3
suffix to the end of the path declared in go.mod
.
To be honest, it’s not very elegant. His decision was largely based on the guideline that
If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.
This means that code within the same major version number must be compatible.
One of the problems with Go is that, for example, when upgrading from v2
to v3
, there may be some incompatibilities, but most of the code is still compatible and not completely rewritten. When you do this, the caller was importing github.com/x/aaa
, but now you have to import github.com/x/aaa/v2
, and all the references to it have to be changed. In addition, the aaa.XXXStruct
in v2
and the aaa.XXXStruct
in v1
are incompatible and cannot be assigned to each other, which causes the caller to change a lot of things and creates a lot of work.
GOPROXY, GOPRIVATE and other common environment variables
-
In areas where the network is restricted (e.g. China)
go get
is basically not directly usable. So you can use theGOPROXY
environment variable to set up a proxy server.1
$ go env -w GOPROXY="https://goproxy.cn,direct"
-
If your project is private then you will also need to add the corresponding path to the
GOPRIVATE
variable.1
$ go env -w GOPRIVATE="github.com/your_name,git.example.com"
If there is only one, write one. If there are more than one, concatenate them with commas. For private repositories, it is recommended to also set a
GONOSUMDB
with the same value as it.
go.mod file
As we said above, where there is a go.mod
file, there is a module, so let’s look at the format of go.mod
, starting with an example.
module
declares the path to the module itselfgo
Declare the version of Go used by this modulerequire
Declare the modules that this module depends onexclude
tells the go mod not to load this modulereplace
Declare the replacement,=>
before the path in the code and after the path to replace it withretract
Declare the version of this module that is broken, or the version to be withdrawn so that people don’t use it. The effect is that the user will be prompted when they encounter the version they have set
All the paths in there are in the format of a URL with a space and a version number, e.g. example.com/new/thing/v2 v2.3.4
.
Version selection
How does go mod do version selection? In a nutshell: start with the current project main
, build a dependency tree, and when multiple submodules depend on the same module, choose the latest one.
As shown above, main
depends on A1.2
, B1.2
, A1.2
depends on C1.3
, while B1.2
depends on C1.4
and they also depend on D1.2
. Finally, go mod
will select main
, A1.2
, B1.2
, C1.4
, D1.2
.
Subdirectories
There are times when we want to give a version to a subdirectory, so how do we do that? The answer is to just prefix the version number with the path to the directory, e.g.
Then we run git tag A/B/v0.1.2
and that’s it. This way the user will give preference to the version of the subdirectory over the version number of the root directory when using the package.
Common commands
Let’s look at common commands that have something to do with version control.
go mod init github.com/xxx/yyy
Declare a modulego mod tidy
adds packages that the project depends on to the go.mod file, and removes packages in the go.mod file that the project doesn’t need.go mod why
Explain the dependency chain of an imported dependencygo build
build the binarygo get
add dependenciesgo get -u
update dependencies