乐趣区

关于golang:golang学习之旅解开心中的go-mod疑惑

原文地址

https://github.com/anqiansong/golang-notes/blob/main/go-module.md

github

https://github.com/anqiansong

go module

在 go1.16 版本公布后,go module 由原来的默认值 auto 变为 on 了,这意味着后续开发中,go 更举荐用 go module 模式开发,而不是 gopath 模式开发了。

在之前,我也是大多数以 go module 模式进行 golang 开发,但至今对其不相熟,仅仅停留在:他人是这样做的,我跟着做就是了,这都算不上会应用 go module,更不用说相熟或者精通了;在此之前,我会存在这些疑难:

  • go mod 文件中定义的各项内容代表什么;
  • 除了常见的 require、偶然见 replace 关键字外,excluderetract(1.16)这些关键字是什么,怎么用;
  • go mod 文件语法格局是什么,目前除了跟着他人写,如同也不明确其中的语法
  • github.com/tal-tech/go-zero v1.1.5github.com/antlr/antlr4 v0.0.0-20210105212045-464bcbc32de2
    google.golang.org/protobuf v1.25.0 // indirect 等格局别离代表什么,为什么有的还有
    // indirect 润饰;
  • go.mod 上面为什么有一个 go.sum,其有什么作用;

不晓得有多少人和我一样,对 go module 的理解微不足道。

最近,带着这些纳闷,去学习了官网的参考手册,这些纳闷就引刃而解了。

project

在正式进入 module 介绍前,有必要首先理解一下 project 和 module 的关系,置信开发过 Android 或者 Java 的同学对 module 有十分好的了解,艰深的讲,一个 project 能够有多个 module 组成,module 能够作为独立
的 project 被别的 project 作为依赖援用,如下 golang 工程 demo 中就蕴含了 foobar 两个 module

demo
├── bar
│   └── go.mod
└── foo
    └── go.mod

module 介绍

go module(以下称:module、模块、工程模块)
是 golang 中已公布版本的 package 的汇合,是 Go 治理依赖的一种形式,相似 Android 中的 Gradle,Java 中的 Maven,当然,他们的治理模式必定是天壤之别,然而目标都是统一的,对依赖进行治理。

在 go.mod 中,其蕴含了 main module 的 module 门路、module 依赖及其关联信息(版本等),如果一个工程模块须要以
go module mode(module 模式)开发,在工程模块的根目录下必须蕴含 go.mod 文件。

module path(module 门路)

module 门路是一个工程模块中的名称,在 go.mod 中以 module 命令申明,其也是工程模块中 package import 的前缀,咱们来看一下 demo/foo 下的 module 门路:

$ cat demo/foo/go.mod
module github.com/foo

go 1.16

require github.com/tal-tech/go-zero

github.com/foo 为 main module foo 模块的模块门路,github.com/tal-tech/go-zero 也是 module path, 他们也是 foo 中 package
import 的前缀,我在 foo 下的 base 包下增加了一个 Echo 函数,而后在 main.go 中调用,咱们察看一下其 package import 的前缀

目录树

foo
├── base
│   └── base.go
├── go.mod
└── main.go

main.go

package main

import "github.com/foo/base" // github.com/foo 为 module path
import "fmt"

func main() {msg := base.Echo("go-zero")
    fmt.Println(msg)
}

module 门路次要作用是形容一个工程模块的作用是什么,在哪里能够找到,因而,module path 的组成元素就蕴含了

  • repo 门路
  • repo 文件夹
  • 版本

其表现形式如: {{.repo_url}}/{{.nameOfDir}}/{{.version}},示例:github.com/tal-tech/go-zero/v2github.com/tal-tech
明确告知了 repo 的门路,go-zero 即为 repo 的文件夹,v2 即为版本号 版本个别 v1 个别都默认不写了,只有大于 v1 时则须要用来辨别。

版本号

这里的版本号是指 go.mod 文件中依赖 module 的版本,这和上文的 {{.version}} 会有关联,如下示例中的 v1.1.5 即为本次所说的 module 依赖版本号。

module github.com/foo

go 1.16

require github.com/tal-tech/go-zero v1.1.5

版本号组成及规定

版本号由 major version(次要版本)、minor version(主要版本)、patch version(订正版本)组成;

  • major version:指 module 中内容作了向后不兼容的更改后,则版本会 upgrade,在此版本号 upgrade 时,minor versionpatch version 要归零;
  • minor version:指在新的性能公布 (features) 或者作了向后兼容的内容变更后,此版本号会 upgrade,在此版本号 upgrade 时,patch version 要归零;
  • patch version:指有 bug 修复或者性能优化时,此版本号能够进行 upgrade,在有 pre-release 公布需要时也能够变更此版本号

示例:v0.0.0v1.2.3v1.2.10-pre

如果一个版本的 major version0 或者 patch-version 有版本后缀(如:pre),则认为这个版本是不稳固的,如 v0.2.0v1.5.0-prev1.1.3-beta
更多对于 version 语义定义能够参考《Semantic Versioning 2.0.0》

Golang 除此之外,还能够用一些标记、分支来代表某一个版本,如:github.com/tal-tech/go-zero 39540e21d249e91f89d96d015a6e3795cfb2be44
github.com/tal-tech/go-zero v1.1.6-0.20210303091609-39540e21d249
github.com/tal-tech/go-zero@master

其中github.com/tal-tech/go-zero v1.1.6-0.20210303091609-39540e21d249 这种版本在 golang 外面称为 Pseudo-versions
(伪版本),其没有齐全遵循上文中的版本规定,伪版本由三个局部组成:

  • 版本根本前缀(vX.Y.Z-0vX.0.0),如 v1.1.6-0
  • 工夫戳:即 revision 的创立工夫戳,如 20210303091609
  • revision 标识符,如 39540e21d249

依据根本前缀的不同,伪版本会有三种模式:

  • vX.0.0-yyyymmddhhmmss-abcdefabcdef:在没有 release 版本时应用
  • vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef:当根本版本是预公布版本时应用
  • vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef:当 release 版本相似 vX.Y.Z
    时应用,如 github.com/tal-tech/go-zero v1.1.6-0.20210303091609-39540e21d249release 版本为 v1.1.5

伪版本不须要手动输出,其会在执行局部 go 命令获取某一次提交记录版本 (revision) 的代码作为依赖时,会主动将其转换为伪版本

上文中的 github.com/tal-tech/go-zero v1.1.6-0.20210303091609-39540e21d249 则是在执行 go get github.com/tal-tech/go-zero 39540e21d249e91f89d96d015a6e3795cfb2be44 后主动转换的后果

版本后缀

为了向前兼容,如果 major version 降级到 2 时,模块门路必须要指定一个版本后缀 v2(其数值放弃和版本中的 major version 的值统一),在 major version 小于 2 时,不容许应用版本后缀。

咱们来看一个例子,我在 demo/foo 模块工程中用到了 miniRedis 这个库,该库的 major version 曾经降级到 2 了,假如我在 go.mod 中援用如下版本会怎么样?

module github.com/foo

go 1.16

// 正确引入
// require github.com/alicebob/miniredis/v2 v2.14.1 

// 谬误引入
require github.com/alicebob/miniredis v2.14.1
invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2

下面是 go mod 应用时的场景,咱们来看一下当 main module 的 release 版本升级到 v2.x.x 时 (前提先发一个 v1.0.0 的版本),module path 没有增加版本后缀,在另一个 module 去应用它会有什么成果:
示例 module foo
foo 工程目前有 release 版本

  • v1.0.0
  • v2.0.1

应用 module 的工程 bar

  • 未增加版本后缀前

foo 工程目录树

foo
├── echo
│   └── echo.go
└── go.mod

module path 为 github.com/anqiansong/foo

module github.com/anqiansong/foo

go 1.16

在工程 bar 的 go.mod 应用 v2.0.0 版本

require github.com/anqiansong/foo v2.0.0

你会发现报错内容为

 require github.com/anqiansong/foo: reading https://goproxy.cn/github.com/anqiansong/foo/@v/v2.0.0.info: 404 Not Found
    server response: not found: github.com/anqiansong/foo@v2.0.0: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2

如果 require github.com/anqiansong/foo v1.0.0 是能够的。

  • 增加版本后缀后

foo 工程目录树

foo
├── echo
│   └── echo.go
└── go.mod

module path 为 github.com/anqiansong/foo/v2

module github.com/anqiansong/foo/v2

go 1.16

在工程 bar 的 go.mod 应用 v2.0.1 版本

require github.com/anqiansong/foo/v2 v2.0.1

bar 运行失常

如果 major version 降级至 v2 时,如果该版本没有打算向前兼容,且不想把 module path 增加版本后缀,则能够在 build tag 时以 +incompatible 结尾即可,
则别的工程援用示例为 require github.com/anqiansong/foo v2.0.0+incompatible

如何解析 package 中的 module

Go 命令首先在构建列表中搜寻具备包门路前缀的模块。例如,如果导入了包 example.com/a/b,而模块 example.com/a 位于构建列表中,则 go 命令将查看 example.com/a
是否蕴含目录 b 中的包。且该目录中至多蕴含一个 go 文件,这样能力被视为 package。生成束缚不利用于此目标。如果生成列表中只有一个模块提供包,则应用该模块。如果没有模块提供包,或者有两个或多个模块提供包,则 go
命令报告谬误。mod=mod 标记批示 go 命令尝试查找提供失落包的新模块,并更新 go.modgo.sumgo getgo mod tidy 命令会主动执行此操作。

当 go 命令更新或者获取 module 依赖时,其会查看 GOPROXY 环境变量,GOPROXY 的值是一个逗号宰割的 url 列表,或者是关键字 directoff

  • 逗号宰割的具体 url 为代理地址,其会告知 go 命令以此值去发动连贯
  • direct:指定 module 依赖通过版本控制系统去获取
  • off:示意不尝试连贯获取 module

如果 GOPROXY 设置了具体的 url,假如 go 命令要寻找一个 github.com/tal-tech/go-zero/zrpcpackagego 命令会并行的去查找一下 module

  • github.com/tal-tech/go-zero/zrpc
  • github.com/tal-tech/go-zero
  • github.com/tal-tech
  • github.com

如果其中有一个或者多个匹配到蕴含满足 github.com/tal-tech/go-zero/zrpc 的内容,则取最长的 module作为依赖,在找到适合的 module 和版本后,go 命令会向 go.mod
go.sum 文件中填写 require,,如果解析到的 module 不是 main module 被动引入的,则会在 require 的值前面增加 // direct
正文,如果一个都没有匹配到,则报错;如果 GOPROXY 有多个 url 代理,在后面失败的状况下,会顺次 向前面代理执行下面的步骤。

go.mod 文件

一个 module(模块工程)的标识是在其根目录下蕴含一个编码为 UTF-8、名称为 go.mod 的文本文件,go.mod 文件中的内容是面向 的,每一行蕴含一个指令,且每行均由一个 关键字 参数
组成,就像:

module github.com/anqiansong/foo

go 1.16

require github.com/tal-tech/go-zero
require github.com/tal-tech/go-queue

replace go.etcd.io/etcd => go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698

retract [v1.0.0, v1.0.1]

当然,领有雷同关键字的内容能够分离出来,用 关键字 + block 组成,就像:

module github.com/anqiansong/foo

go 1.16

require (
    github.com/tal-tech/go-zero
    github.com/tal-tech/go-queue
)

replace go.etcd.io/etcd => go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698

retract [v1.0.0, v1.0.1]

go.mod是机器可写的,像执行一些命令(如:go getgo mod edit)可能会自动更新 go.mod文件。

module 组成元素

在解析 go.mod文件中的内容时,其会被解析为

  • 空白符 :蕴含空格(U+0020)、制表符(U+0009)、回车(U+000D) 和换行符(U+000A)
  • 正文:正文仅反对单行正文 //
  • 标点:标点符号有 (),=>
  • 关键字gorequirereplaceexcluderetract
  • 标识符 :由非 空白符 组成的字符序列,如 module path、语义版本
  • 字符串:由英文双引号 "(U+0022)包裹的解释字符串或者有 <(U+0060)包裹的原始字符串。如"github/com/tal-tech/go-zero"、“

标识符 字符串 go.mod语法中能够替换

module 语法词法

go.mod 语法是通过Extended Backus-Naur Form (EBNF 范式) 定义的,就像

GoMod = {Directive} .
Directive = ModuleDirective |
            GoDirective |
            RequireDirective |
            ExcludeDirective |
            ReplaceDirective |
            RetractDirective .

module 指令

module 关键字定义了 main module 的 module path,在 go.mod 文件中有且只有一个 module 指定。

语法规定:

ModuleDirective = "module" (ModulePath | "(" newline ModulePath newline ")" newline .

示例:

module github.com/tal-tech/go-zero

go 指令

go 关键字定义了 module 设置预期应用的 go 语言版本,版本必须是一个无效的 go 版本(能够了解为合乎 version 规定,也能够了解为为 Go 曾经 release 的版本)

通过 go 关键字定义版本后,编译器在编译包时就晓得应该应用哪个 go 版本去编译,除此外,go 关键字定义版本还能够用于 是否启用 go命令的一些个性,如是否主动开始 vendoring 在版本 1.14 及当前。

语法规定:

GoDirective = "go" GoVersion newline .
GoVersion = string | ident .  /* valid release version; see above */

示例:

go 1.16

require 指令

require 申明了 module 依赖的最小版本,在 require 指定版本后,go 相干命令会依据 MVS
规定依据此值来加载依赖。

go 寻找依赖时,如果该依赖不是 main module 间接依赖的,则会在该 module path 前面增加 // direct 正文内容。

语法规定:

RequireDirective = "require" (RequireSpec | "(" newline { RequireSpec} ")" newline ) .
RequireSpec = ModulePath Version newline .

示例:

module github.com/tal-tech/go-zero

go 1.16

require (
    golang.org/x/crypto v1.4.5 // indirect
    golang.org/x/text v1.6.7
)

excule 指令

excule 会疏忽内容中的指定版本,从 go 1.16 后,exclude 指定的 module 会被疏忽,在 go 1.16 前,如果 require 的 module 被 exclude
指定后,会列出并获取更改的为被 exclude 的版本。

语法规定:

ExcludeDirective = "exclude" (ExcludeSpec | "(" newline { ExcludeSpec} ")" ) .
ExcludeSpec = ModulePath Version newline .

示例:

module github.com/tal-tech/go-zero

go 1.16

exclude golang.org/x/net v1.2.3

excule (
    golang.org/x/crypto v1.4.5
    golang.org/x/text v1.6.7
)

replace 指令

replace 指令用于将 module 的指定版本或者 module 应用其余的 module 或者版本来替换,如果 => 右边质指定了版本,则替换 这个版本至指标内容,否则替换替换 module 的所有版本至指标内容

语法规定:

ReplaceDirective = "replace" (ReplaceSpec | "(" newline { ReplaceSpec} ")" newline ")" ) .
ReplaceSpec = ModulePath [Version] "=>" FilePath newline
            | ModulePath [Version] "=>" ModulePath Version newline .
FilePath = /* platform-specific relative or absolute file path */

示例:

replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5

=> 左边的内容能够是无效的 module path,也能够是绝对或者绝对路径,如果是绝对或者绝对路径,这该门路的根目录必须蕴含 go.mod 文件。

示例:

require github.com/foo v1.0.0

replace github.com/foo v1.0.0 => ../bar

retract 指令(1.16 新增)

retract 申明的内容,用于标记某些版本或者某个版本范畴(闭区间)标记为撤回,个别 retract 申明前须要写一条正文用于阐明,当执行 go get 命令时,如果援用了被标记为 retract 的版本,或者
retract 标记的版本范畴内,则会提醒一条正告(其内容为 retract 的正文内容),通过go list -m -versions 获取版本时也会暗藏该版本。

语法规定:

RetractDirective = "retract" (RetractSpec | "(" newline { RetractSpec} ")" ) .
RetractSpec = (Version | "[" Version "," Version "]" ) newline .

示例:

// someting wrong
retract v1.0.0

// someting wrong in range of versions => v1.0.0~v1.2.0 
retrace [v1.0.0,v1.2.0]

咱们来看一个例子,目前 github.com/anqiansong/retract 曾经有 v1.0.0 等版本了,咱们 增加一行 retract 指令标记
v1.0.0 撤回:

// someting wrong
retract v1.0.0

而后 release 一个版本为 v1.0.1,接下来在 github.com/anqiansong/bar 中援用 v1.0.0版本

require github.com/anqiansong/retract v1.0.0

而后执行 go get github.com/anqiansong/retract@v1.0.0,不出意外,会失去一个提醒蕴含 something wrong 和 提醒更新到 v1.0.1 的信息

$ go get github.com/anqiansong/retract@v1.0.0
go: warning: github.com/anqiansong/retract@v1.0.0: retracted by module author: someting wrong
go: to switch to the latest unretracted version, run:
        go get github.com/anqiansong/retract@latestgo get: downgraded github.com/anqiansong/retract v1.0.1 => v1.0.0

获取 github.com/anqiansong/retract 所有 module release 版本

$ go list -m -versions github.com/anqiansong/retract
github.com/anqiansong/retract v1.0.1

局部命令查看 github.com/anqiansong/retract@v1.0.0的后果:

  • go get

    $ go get github.com/anqiansong/retract@v1.0.0
    go: warning: github.com/anqiansong/retract@v1.0.0: retracted by module author: someting wrong
    go: to switch to the latest unretracted version, run:
    go get github.com/anqiansong/retract@latest
  • go list -m -u

    $ go get github.com/anqiansong/retract@v1.0.0
    github.com/anqiansong/retract v1.0.0 (retracted) [v1.0.1]
  • go list -m -versions

      $ go list -m -versions github.com/anqiansong/retract
    github.com/anqiansong/retract v1.0.1

阐明:

retract 管制的是 main module 的版本,而非依赖的 module 版本。

retract 标记的版本其余 module 还是能够援用的,只是局部 go 命令执行时会有 retract 的不同后果,如上。

自动更新

如果 go.mod 短少信息或者不能精确反映理论状况,大多数 go 命令都会报告谬误。go getgo mod tidy 命令能够用来修复大多数这类问题。此外,-mod=mod
标记能够用于大多数模块感知命令(go buildgo test 等),以批示 go 命令主动修复 go.modgo.sum 中的问题。

module 感知

大多数 go 命令能够在 Module-ware 模式或 GOPATH 模式下运行。在 Module-ware 模式下,go 命令应用 go.mod 文件来查找版本相关性,
它通常从模块缓存中加载包,如果短少模块,则下载模块。在 GOPATH 模式下,go 命令疏忽 module;它在 vendorGOPATH 目录中查找依赖项。

在 Go 1.16 中,无论是否存在 go.mod 文件,Module-ware 模式默认是启用的。在低版本中,当工作目录文件或任何父目录中存在 go.mod 文件时,启用 Module-ware 模式。

Module-ware 模式能够通过 GO111MODULE 环境变量管制,能够设置为 onoffauto

  • offgo 相干命令会疏忽 go.mod文件,而后以 GOPATH 模式运行
  • onon 或者空字符串,相干命令会 Module-ware 模式运行
  • auto:如果以后文件夹存在 go.mod 文件,则会以 Module-ware 模式运行,在 Go 1.15 及更低版本,此值为默认值,

局部 go module 相干命令

这里命令必须要在 Module-ware 模式才无效

命令 用法 备注 示例
go list -m go list -m [-u] [-retracted] [-versions] [list flags] [modules] 查看 module 信息 go list -m all
go mod init go mod init [module-path] 在工作目录初始化并创立一个 go.mod 文件 go mod init demo
go mod tidy go mod tidy [-e] [-v] 整顿 go.mod 文件 go mode tidy
go clean -modcache go clean [-modcache] 革除 module 缓存 go clean -modcache

Proxy

模块代理是一个反对 GET 申请响应 的 HTTP 服务器,该申请没有 query 参数,甚至不须要特定的 header 信息,即便 该值是一个固定的文件系统站点 (如:file:// URL ) 也是能够的。

模块代理的 HTTP 响应状态码必须蕴含 200(OK),3xx4xx5xx4xx5xx被认为是响应谬误,404410
示意所有的 module 申请是不可用的,留神,谬误的响应的 contentType 应该设置为 text/plain,字符集为 utf-8 或者 us-ascii

URLs

go 命令能够通过读取 GOPROXY 环境变量配置来连贯连贯代理服务器或者版本控制系统,GOPROXY 承受一个逗号 (,) 或者竖线 (|) 宰割的多个 url 值,当以英文逗号 (,)
宰割时,只有响应状态码为 404 或者 410 时就会尝试前面的代理地址,如果是以竖线 (|) 宰割,则在 http 呈现任何谬误(蕴含超时)都会跳过去尝试前面的代理地址。也能够是 direct 或者 off 关键字。

上面的表格为一个代理地址必须要实现且有申请响应的 path(即一个代理服务器必须要要反对一下路由的实现)

  • $base为代理服务器地址,如:https://goproxy.cn
  • $module为 module path,如:github.com/tal-tech/go-zero
  • $version为 module 版本
path 形容 示例 示例后果
$base/$module/@v/list 以纯文本模式返回给定模块的已知版本的列表,每行一个。此列表不应包含伪版本 curl -X GET https://goproxy.cn/github.com… v1.0.0
v1.0.1
v1.0.2
v1.0.3
v1.0.4
$base/$module/c/$version.info 返回对于某个模块的特定版本的 json 格局的元数据。响应必须是一个 JSON 对象,对应于上面的 Go 数据结构:
`
type Info struct {
Version string // version string
Time time.Time // commit time
}
`
curl -X GET https://goproxy.cn/github.com… `{
“Version”: “v1.1.5”,
“Time”: “2021-03-02T03:02:57Z”
}`
$base/$module/@v/$version.mod 返回指定版本的 go.mod 中的信息 /td> curl -X GET https://goproxy.cn/github.com… `
module github.com/tal-tech/go-zero
go 1.14
require (
github.com/ClickHouse/clickhouse-go v1.4.3
github.com/DATA-DOG/go-sqlmock v1.4.1
github.com/alicebob/miniredis/v2 v2.14.1
github.com/antlr/antlr4 v0.0.0-20210105212045-464bcbc32de2
….
)`
$base/$module/@v/$version.zip 返回指定版本的 go module 的 zip 文件 wget https://goproxy.cn/github.com… v1.1.5.zip
$base/$module/@latest 返回无关模块的最新已知版本的 json 格局的元数据 curl -X GET https://goproxy.cn/github.com… {
“Version”: “v1.1.5”,
“Time”: “2021-03-02T03:02:57Z”
}

在获取 module 最新版本时,go 相干命令会优先申请 $base/$module/@v/list 地址,如果没有找到适宜的版本,
则申请 $base/$module/@latest 获取并匹配是否满足,go 相干命令会依照 release 版本、pre-release 版本、pseudo 版本排序。

$base/$module/$version.mod$base/$module/$version.zip 地址必须要提供,因为这些信息能够用于和 go.sum 进行数据校验。

go module 下载后的内容个别会存储在 $GOPATH/pkg/mod/cache/download 门路下,包含版本控制系统下载的也是如此。

direct

如果 GOPROXY 设置了 direct 值,则在执行相干 go 命令时会从版本控制系统 (gitsvn 等)下载 module 资源,当然还须要另外两个环境
变量 GOPRIVATEGONOPROXY 配合,更多环境变量信息请参考这里

module 文件大小束缚

对模块 zip 文件的内容有许多限度。这些束缚确保能够在宽泛的平台上平安和统一地提取压缩文件。

一个模块最多能够达到 500MiB(不论是压缩模式还是未压缩模式文件)

go.mod文件要求显示在 16MiB 以内

go.sum

在 module 的根目录下可能有一个名为 go.sum的文件,其是间接附丽在 go.mod 上面的,当执行 go 相干命令获取 module 时,
其会查看 zip 文件和 go.sum 中的值是否统一。

go.sum 中的值由一个 module pathversion 和一个 hash 值组成,如:

github.com/anqiansong/retract v1.0.1 h1:jxcsUM/6tvxM7p14/XMeZPFbql5KAAZJfFqiHG+YKxA=

总结

花了 3 天工夫,第一天看英文原文档,第二、三天翻译成中文来验证本人的了解,并写下这篇日志,通过学习,对于 go module 手册的学习解开了不少纳闷,
至多开篇的几个问题算是解开了,本文也是将手册中的局部内容作了 搬运 联合了本人的一些了解,
以及用实在例子去验证,其中还有很多内容我并未齐全照搬,如果有趣味的同学,能够参考官网手册,本文局部内容蕴含集体了解,也是联合 google 翻译加上
对一些翻译不合理的中央进行人工翻译,必定存在不合理的中央,如果有了解不统一的,欢送提出和探讨。

最初

有播种能够到 github 点个小❤️❤️ 哈。

参考文档

  • 《Semantic Versioning 2.0.0》
  • 《Go Module 官网文档》
退出移动版