很久没更新了,一是工作琐碎,二是处在舒适区,但最近看着身边的同事一个个离开,危机感骤然而生,不得不重拾书本,毕竟生活还得继续,不卷是不可能的,谁让我们生在这个卷中卷的国度,只能活到老卷到老…
说完题外话,说说正题,笔者使用 Golang 也有两三年了,基本还停留在会用就行,没有深挖细节,不符合我刨根问底的学习习惯,接下来一段时间,准备系统的看看 go 官方文档,毕竟这是最新最权威的资料,然后顺手写点东西,就当是加深映像吧。
今天要说的是我们怎么在项目里使用fork的别人的模块,因为fork别人的项目之后,项目的路径和 go.mod 里声明的路径就不一样了,直接 go get 是引入不了的,比如这里引入我 fork 后的 github.com/zhyee/gin 框架会报错:
$ go get github.com/zhyee/gin
go: downloading github.com/zhyee/gin v0.0.0-20240119001857-857db39f82fb
go: github.com/zhyee/gin@v0.0.0-20240119001857-857db39f82fb: parsing go.mod:module declares its path as: github.com/gin-gonic/ginbut was required as: github.com/zhyee/gin
那是不是我把 go.mod 里的 module 声明 github.com/gin-gonic/gin
改成fork 后的项目地址 github.com/zhyee/gin
就完事了呢,当然不是,通常一个 go模块下的各个子 package 之间的相互引用也是用的带模块名的绝对路径而不是相对路径(参考下方截图),所以除了要改 go.mod 里的 module 声明,还要修改子package相互导入的包名路径,这对于一个较大型的项目涉及到的修改地方就太多了,即使你可以全文替换把整个项目里出现的原模块名全部替换掉,并且确实也能正常使用了,那假如将来你希望把自己的fork版本提个PR合并进原始项目时,是不是又要把模块名改回来呢,这种做法显然不太科学,go官方当然也考虑到了这种常见的使用场景。
使用 replace 指令
比较通用的做法是使用 replace 指令,比如
$ cat go.mod
module example.com/hellogo 1.20require golang.org/x/example/hello v0.0.0-20231013143937-1d6d2400d402require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/gin-gonic/gin v1.9.1 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.14.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.0.8 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.9.0 // indirectgolang.org/x/net v0.10.0 // indirectgolang.org/x/sys v0.8.0 // indirectgolang.org/x/text v0.9.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)
我们可以使用命令 go mod edit -replace=old[@v]=new[@v]
来添加 replace 指令,也可以直接编辑 go.mod 文件来添加,old[@v]
中的版本号如果省略,则表明所有的版本都应该被替换,否则只有指定的版本会执行替换,new[@v]
模块中的版本号如果被省略,则用于替换的模块应该是一个本地模块而不是网络模块
$
$ go mod edit -replace=github.com/gin-gonic/gin=github.com/zhyee/gin@latest
$
$ cat go.mod
module example.com/hellogo 1.20require golang.org/x/example/hello v0.0.0-20231013143937-1d6d2400d402require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/gin-gonic/gin v1.9.1 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.14.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.0.8 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.9.0 // indirectgolang.org/x/net v0.10.0 // indirectgolang.org/x/sys v0.8.0 // indirectgolang.org/x/text v0.9.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)replace github.com/gin-gonic/gin => github.com/zhyee/gin latest
添加replace指令之后再次执行 go get github.com/gin-gonic/gin
会发现又会重新拉下来一些东西:
$ go get github.com/gin-gonic/gin
go: upgraded github.com/go-playground/validator/v10 v10.14.0 => v10.16.0
go: upgraded github.com/pelletier/go-toml/v2 v2.0.8 => v2.1.1
go: upgraded golang.org/x/crypto v0.9.0 => v0.15.0
go: upgraded golang.org/x/net v0.10.0 => v0.18.0
go: upgraded golang.org/x/sys v0.8.0 => v0.14.0
go: upgraded golang.org/x/text v0.9.0 => v0.14.0
为了证明我们现在用的Gin已经替换为 github.com/zhyee/gin
而不是官方的github.com/gin-gonic/gin
,我们可以在 IDE里查看gin的相关源码,可以看到IDE已经为我们自动跳转到了替换后的项目:
那如果我们fork出来的项目随着时间的推移有了新版本了,该如何在go项目里升级 replace 的module呢,比如这里我给我fork出来的 github.com/zhyee/gin
打了一个新的tag v1.99.99
:
我现在想升级到该 v1.99.99
版本,你可以直接修改 replace指令,把老的版本号替换为新的版本号,比如把
replace github.com/gin-gonic/gin => github.com/zhyee/gin v0.0.0-20240119001857-857db39f82fb
替换为 replace github.com/gin-gonic/gin => github.com/zhyee/gin v1.99.99
,或者 replace github.com/gin-gonic/gin => github.com/zhyee/gin latest
,然后执行一下 go get github.com/gin-gonic/gin
或 go mod tidy
:
$ go mod edit -replace=github.com/gin-gonic/gin=github.com/zhyee/gin@latest
$
$ cat go.mod
module example.com/hellogo 1.20require github.com/gin-gonic/gin v1.9.1require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.16.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.1.1 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.15.0 // indirectgolang.org/x/net v0.18.0 // indirectgolang.org/x/sys v0.14.0 // indirectgolang.org/x/text v0.14.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)replace github.com/gin-gonic/gin => github.com/zhyee/gin latest
$
$ go get github.com/gin-gonic/gin
go: downloading github.com/zhyee/gin v1.99.99
$
$ cat go.mod
module example.com/hellogo 1.20require github.com/gin-gonic/gin v1.9.1require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.16.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.1.1 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.15.0 // indirectgolang.org/x/net v0.18.0 // indirectgolang.org/x/sys v0.14.0 // indirectgolang.org/x/text v0.14.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)replace github.com/gin-gonic/gin => github.com/zhyee/gin v1.99.99
$
如果使用了 go mod 代理,可能有时代理还没有缓存某个module的最新版本,这时建议暂时关闭代理或者使用具体的版本号而不是 latest
:
$ cat go.mod
module example.com/hellogo 1.20require github.com/gin-gonic/gin v1.9.1require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.16.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.1.1 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.15.0 // indirectgolang.org/x/net v0.18.0 // indirectgolang.org/x/sys v0.14.0 // indirectgolang.org/x/text v0.14.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)replace github.com/gin-gonic/gin => github.com/zhyee/gin v1.99.99
$
$ go mod edit -replace=github.com/gin-gonic/gin=github.com/zhyee/gin@v1.99.999
$
$ cat go.mod
module example.com/hellogo 1.20require github.com/gin-gonic/gin v1.9.1require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.16.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.1.1 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.15.0 // indirectgolang.org/x/net v0.18.0 // indirectgolang.org/x/sys v0.14.0 // indirectgolang.org/x/text v0.14.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)replace github.com/gin-gonic/gin => github.com/zhyee/gin v1.99.999
$
$
$ go get github.com/gin-gonic/gin
go: downloading github.com/zhyee/gin v1.99.999
$
$ cat go.mod
module example.com/hellogo 1.20require github.com/gin-gonic/gin v1.9.1require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.16.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.1.1 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.15.0 // indirectgolang.org/x/net v0.18.0 // indirectgolang.org/x/sys v0.14.0 // indirectgolang.org/x/text v0.14.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)replace github.com/gin-gonic/gin => github.com/zhyee/gin v1.99.999
$
$ go run .
my forked Gin framework v1.99.999
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.- using env: export GIN_MODE=release- using code: gin.SetMode(gin.ReleaseMode)
之前我们也说了,省略替换后模块 new[@v]
中的版本号,则应该指定一个本地的模块路径,所以我们也可以把module克隆到本地,然后用 replace 指令把某个模块替换为本地的模块,这种方式在开发阶段比较好用,不需要频繁把修改推到fork后的远端仓库就可以直接调试运行看到效果:
$ ls
example go.work hello
$
$ git clone https://github.com/zhyee/gin.git
Cloning into 'gin'...
remote: Enumerating objects: 7298, done.
remote: Counting objects: 100% (1915/1915), done.
remote: Compressing objects: 100% (248/248), done.
remote: Total 7298 (delta 1772), reused 1670 (delta 1667), pack-reused 5383
Receiving objects: 100% (7298/7298), 3.08 MiB | 843.00 KiB/s, done.
Resolving deltas: 100% (4745/4745), done.
$
$ cd hello/
$
$ go mod edit -replace=github.com/gin-gonic/gin=../gin
$
$ cat go.mod
module example.com/hellogo 1.20require golang.org/x/example/hello v0.0.0-20231013143937-1d6d2400d402require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/gin-gonic/gin v1.9.1 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.16.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.1.1 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.15.0 // indirectgolang.org/x/net v0.18.0 // indirectgolang.org/x/sys v0.14.0 // indirectgolang.org/x/text v0.14.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)replace github.com/gin-gonic/gin => ../gin
然后对我们克隆下来的 Gin 做一点修改(这里添加了一个init方法,打印一句话,方便查看效果), 然后编译运行我们的 hello 项目
$ cd hello/
$ ls
go.mod go.sum hello.go
$
$ cat hello.go
package mainimport ("github.com/gin-gonic/gin"
)func main() {gin.New()}
$
$ go run .
my forked Gin framework
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
可以看到已经应用了我们本地的 Gin代码。
go workspace(工作区/ 工作空间)
Golang 1.18版本新增了一个 workspace 的概念,通常一个go项目就是一个 go 模块,但一个workspace 能包含多个 go模块,方便多个模块之间的相互调用,以及多个模块的同时开发,从而形成go的 package、module、workspace 三层代码组织级别。
在没有workspace 的情况下,想要在 module 目录外编译和运行 module 是比较困难的,比如:
$ ll foobar/
total 16
-rw-r--r-- 1 zy staff 35 1 26 14:02 go.mod
-rw-r--r-- 1 zy staff 73 1 26 14:02 main.go
$
$ go run foobar/
package foobar is not in GOROOT (/Users/zy/go1.20.10/src/foobar)
提示package在 $GOROOT/src目录下找不到,即使把module放到 $GOPATH/src 路径下也还是会报错:
$ go env GOPATH
/Users/zy/go
$
$ ll /Users/zy/go/src/foobar/
total 16
-rw-r--r-- 1 zy staff 23 1 26 14:28 go.mod
-rw-r--r-- 1 zy staff 73 1 26 13:25 main.go
$
$ go run /Users/zy/go/src/foobar/
go: go.mod file not found in current directory or any parent directory; see 'go help modules'
提示在当前目录或是父级目录找不到 go.mod 文件,workspace的出现解决了这个问题:
$ mkdir goworkspace
$ cd goworkspace/
$
$ go work init
$
$ ls
go.work
$ cat go.work
go 1.20
$
$ mv ../foobar/ ./
$ ls
foobar go.work
$ go work use foobar/
$
SpaceX:goworkspace zy$ cat go.work
go 1.20use ./foobar
$
$ go run foobar/
hello foobar
$
go work init
命令会把当前目录作为一个workspace并创建一个go.work
文件,类似于 go mod init
,go work use moddirs
会把指定的module加入到当前workspace的主模块当中。同一个workspace中的各个模块之间相互调用是非常方便的,比如:
$ ls
foobar go.work
SpaceX:goworkspace zy$
$ mkdir foolib
$ cd foolib/
$
$ go mod init "example.com/go/foolib"
go: creating new go.mod: module example.com/go/foolib
$
$ ls
go.mod
$ cat go.mod
module example.com/go/foolibgo 1.20
$
$ vi lib.go
$
SpaceX:foolib zy$ cat lib.go
package foolibfunc Add(a, b int) int {return a * b
}
$
$ cd ..
$ ls
foobar foolib go.work
$ cat go.work
go 1.20use ./foobar
$
$ go work use foolib/
$
$ cat go.work
go 1.20use (./foobar./foolib
)
$ go build ./foolib/
$
$ vi foobar/main.go
$
$ cat foobar/main.go
package mainimport "fmt"
import "example.com/go/foolib"func main() {fmt.Println("hello foobar")fmt.Println("foolib Add: ", foolib.Add(4, 5))
}
$
$ go run foobar/
hello foobar
foolib Add: 20
上述操作在 workspace 中创建了另一个module example.com/go/foolib
,然后使用 go work use
命令把它加入到workspace中,最后在原来的 foobar
模块中调用 example.com/go/foolib
中提供的方法,一切都是那么的简单,不需要replace指令,不需要关注module所在的路径是否与module 声明中的一致,不需要处理模块之间的相对路径,使用起来和 go get
一样的优雅。
当然,go.work 中也支持使用 replace 指令,且该指令对workspace下的所有 module 都生效:
$ ls
foobar foolib go.work
$
$ go work edit -replace=github.com/gin-gonic/gin=/Users/zy/project/gin
$
SpaceX:goworkspace zy$ cat go.work
go 1.20use (./foobar./foolib
)replace github.com/gin-gonic/gin => /Users/zy/project/gin
$
$ mkdir foo bar
$
$
$ cd foo && go mod init "foo"
go: creating new go.mod: module foo
$
$ cd ..
$ cd bar && go mod init "bar"
go: creating new go.mod: module bar
$
$
$ cd ..
$ ls
bar foo foobar foolib go.work
$
$ vi foo/main.go
$
$ cp foo/main.go bar/
$
$ cat foo/main.go
package mainimport "github.com/gin-gonic/gin"func main() {gin.New()
}$ cat bar/main.go
package mainimport "github.com/gin-gonic/gin"func main() {gin.New()
}$
$ go work use foo/ bar/
$
$ cat go.work
go 1.20use (./bar./foo./foobar./foolib
)replace github.com/gin-gonic/gin => /Users/zy/project/gin
$
$ cd foo
$ go get github.com/gin-gonic/gin
go: added github.com/bytedance/sonic v1.9.1
go: added github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
go: added github.com/gabriel-vasile/mimetype v1.4.2
go: added github.com/gin-contrib/sse v0.1.0
go: added github.com/gin-gonic/gin v1.9.1
go: added github.com/go-playground/locales v0.14.1
go: added github.com/go-playground/universal-translator v0.18.1
go: added github.com/go-playground/validator/v10 v10.14.0
go: added github.com/goccy/go-json v0.10.2
go: added github.com/json-iterator/go v1.1.12
go: added github.com/klauspost/cpuid/v2 v2.2.4
go: added github.com/leodido/go-urn v1.2.4
go: added github.com/mattn/go-isatty v0.0.19
go: added github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
go: added github.com/modern-go/reflect2 v1.0.2
go: added github.com/pelletier/go-toml/v2 v2.0.8
go: added github.com/twitchyliquid64/golang-asm v0.15.1
go: added github.com/ugorji/go/codec v1.2.11
go: added golang.org/x/arch v0.3.0
go: added golang.org/x/crypto v0.9.0
go: added golang.org/x/net v0.10.0
go: added golang.org/x/sys v0.8.0
go: added golang.org/x/text v0.9.0
go: added google.golang.org/protobuf v1.30.0
go: added gopkg.in/yaml.v3 v3.0.1
$
$ cat go.mod
module foogo 1.20require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/gin-gonic/gin v1.9.1 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.14.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.0.8 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.9.0 // indirectgolang.org/x/net v0.10.0 // indirectgolang.org/x/sys v0.8.0 // indirectgolang.org/x/text v0.9.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)
$ cd ..
$ go run ./foo
my forked Gin framework
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.- using env: export GIN_MODE=release- using code: gin.SetMode(gin.ReleaseMode)$ cd bar/
$
$ go get github.com/gin-gonic/gin
go: added github.com/bytedance/sonic v1.9.1
go: added github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
go: added github.com/gabriel-vasile/mimetype v1.4.2
go: added github.com/gin-contrib/sse v0.1.0
go: added github.com/gin-gonic/gin v1.9.1
go: added github.com/go-playground/locales v0.14.1
go: added github.com/go-playground/universal-translator v0.18.1
go: added github.com/go-playground/validator/v10 v10.14.0
go: added github.com/goccy/go-json v0.10.2
go: added github.com/json-iterator/go v1.1.12
go: added github.com/klauspost/cpuid/v2 v2.2.4
go: added github.com/leodido/go-urn v1.2.4
go: added github.com/mattn/go-isatty v0.0.19
go: added github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
go: added github.com/modern-go/reflect2 v1.0.2
go: added github.com/pelletier/go-toml/v2 v2.0.8
go: added github.com/twitchyliquid64/golang-asm v0.15.1
go: added github.com/ugorji/go/codec v1.2.11
go: added golang.org/x/arch v0.3.0
go: added golang.org/x/crypto v0.9.0
go: added golang.org/x/net v0.10.0
go: added golang.org/x/sys v0.8.0
go: added golang.org/x/text v0.9.0
go: added google.golang.org/protobuf v1.30.0
go: added gopkg.in/yaml.v3 v3.0.1
$
$ cat go.mod
module bargo 1.20require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/gin-gonic/gin v1.9.1 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.14.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.0.8 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.9.0 // indirectgolang.org/x/net v0.10.0 // indirectgolang.org/x/sys v0.8.0 // indirectgolang.org/x/text v0.9.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)
$ cd ..
$
$ go run ./bar/
my forked Gin framework
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.- using env: export GIN_MODE=release- using code: gin.SetMode(gin.ReleaseMode)
$
$
上述操作新建了两个module foo
和 bar
,并加入到了 workspace 中,两个 module 都调用了 Gin 框架,但在各自的 go.mod 中都没有使用 replace 指令替换 github.com/gin-gonic/gin
,但在 workspace中的 go.work 中把 github.com/gin-gonic/gin
替换为了本地修改过的 /Users/zy/project/gin
,可以看到go.work中的replace 指令最终对 foo
和 bar
两个模块都生效了,可见workspace可以方便的统一管理多模块。
上面花了大量的篇幅介绍go workspace,还没有说怎么在项目里使用 fork后的module,其实就是把fork后的项目克隆到workspace下,然后使用 go work use
指令把module加入到主模块中,然后就可以直接用这个模块了,效果和使用 replace
指定本地模块路径差不多:
$ cd ~/project/goworkspace/
$
$ ls
bar foo foobar foolib go.work go.work.sum
$
$ git clone https://github.com/zhyee/gin.git
Cloning into 'gin'...
remote: Enumerating objects: 7304, done.
remote: Counting objects: 100% (3106/3106), done.
remote: Compressing objects: 100% (416/416), done.
remote: Total 7304 (delta 2861), reused 2696 (delta 2690), pack-reused 4198
Receiving objects: 100% (7304/7304), 3.02 MiB | 6.00 MiB/s, done.
Resolving deltas: 100% (4813/4813), done.
$
$ ls
bar foo foobar foolib gin go.work go.work.sum
$
$ go work use gin
$
$ cat go.work
go 1.20use (./bar./foo./foobar./foolib./gin
)replace github.com/gin-gonic/gin => /Users/zy/project/gin
$
$ go work edit -dropreplace=github.com/gin-gonic/gin
$
$ cat go.work
go 1.20use (./bar./foo./foobar./foolib./gin
)
$
$ cat foo/go.mod
module foogo 1.20require (github.com/bytedance/sonic v1.9.1 // indirectgithub.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectgithub.com/gabriel-vasile/mimetype v1.4.2 // indirectgithub.com/gin-contrib/sse v0.1.0 // indirectgithub.com/gin-gonic/gin v1.9.1 // indirectgithub.com/go-playground/locales v0.14.1 // indirectgithub.com/go-playground/universal-translator v0.18.1 // indirectgithub.com/go-playground/validator/v10 v10.14.0 // indirectgithub.com/goccy/go-json v0.10.2 // indirectgithub.com/json-iterator/go v1.1.12 // indirectgithub.com/klauspost/cpuid/v2 v2.2.4 // indirectgithub.com/leodido/go-urn v1.2.4 // indirectgithub.com/mattn/go-isatty v0.0.19 // indirectgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirectgithub.com/modern-go/reflect2 v1.0.2 // indirectgithub.com/pelletier/go-toml/v2 v2.0.8 // indirectgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirectgithub.com/ugorji/go/codec v1.2.11 // indirectgolang.org/x/arch v0.3.0 // indirectgolang.org/x/crypto v0.9.0 // indirectgolang.org/x/net v0.10.0 // indirectgolang.org/x/sys v0.8.0 // indirectgolang.org/x/text v0.9.0 // indirectgoogle.golang.org/protobuf v1.30.0 // indirectgopkg.in/yaml.v3 v3.0.1 // indirect
)
$ cat foo/main.go
package mainimport "github.com/gin-gonic/gin"func main() {gin.New()
}$
$ go run foo/
my forked Gin framework v1.99.999
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.- using env: export GIN_MODE=release- using code: gin.SetMode(gin.ReleaseMode)
当然这种方式适合开发阶段,如果你fork别人的module已经稳定了,或者你引用fork模块的项目已经到了测试发布阶段了,还是要用 replace
指令。
完!