go 引用fork后的模块的两种方式(replace和工作区)

很久没更新了,一是工作琐碎,二是处在舒适区,但最近看着身边的同事一个个离开,危机感骤然而生,不得不重拾书本,毕竟生活还得继续,不卷是不可能的,谁让我们生在这个卷中卷的国度,只能活到老卷到老…

说完题外话,说说正题,笔者使用 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/[email protected]: parsing go.mod:
	module declares its path as: github.com/gin-gonic/gin
	        but 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官方当然也考虑到了这种常见的使用场景。

go 引用fork后的模块的两种方式(replace和工作区)_第1张图片

使用 replace 指令

比较通用的做法是使用 replace 指令,比如

$ cat go.mod
module example.com/hello

go 1.20

require golang.org/x/example/hello v0.0.0-20231013143937-1d6d2400d402

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.9.1 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.14.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.9.0 // indirect
	golang.org/x/net v0.10.0 // indirect
	golang.org/x/sys v0.8.0 // indirect
	golang.org/x/text v0.9.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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/hello

go 1.20

require golang.org/x/example/hello v0.0.0-20231013143937-1d6d2400d402

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.9.1 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.14.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.9.0 // indirect
	golang.org/x/net v0.10.0 // indirect
	golang.org/x/sys v0.8.0 // indirect
	golang.org/x/text v0.9.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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已经为我们自动跳转到了替换后的项目:
go 引用fork后的模块的两种方式(replace和工作区)_第2张图片

那如果我们fork出来的项目随着时间的推移有了新版本了,该如何在go项目里升级 replace 的module呢,比如这里我给我fork出来的 github.com/zhyee/gin 打了一个新的tag v1.99.99

go 引用fork后的模块的两种方式(replace和工作区)_第3张图片
我现在想升级到该 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/gingo mod tidy

$ go mod edit -replace=github.com/gin-gonic/gin=github.com/zhyee/gin@latest
$
$ cat go.mod
module example.com/hello

go 1.20

require github.com/gin-gonic/gin v1.9.1

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.16.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.1.1 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.15.0 // indirect
	golang.org/x/net v0.18.0 // indirect
	golang.org/x/sys v0.14.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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/hello

go 1.20

require github.com/gin-gonic/gin v1.9.1

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.16.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.1.1 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.15.0 // indirect
	golang.org/x/net v0.18.0 // indirect
	golang.org/x/sys v0.14.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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/hello

go 1.20

require github.com/gin-gonic/gin v1.9.1

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.16.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.1.1 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.15.0 // indirect
	golang.org/x/net v0.18.0 // indirect
	golang.org/x/sys v0.14.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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/[email protected]
$
$ cat go.mod
module example.com/hello

go 1.20

require github.com/gin-gonic/gin v1.9.1

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.16.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.1.1 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.15.0 // indirect
	golang.org/x/net v0.18.0 // indirect
	golang.org/x/sys v0.14.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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/hello

go 1.20

require github.com/gin-gonic/gin v1.9.1

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.16.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.1.1 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.15.0 // indirect
	golang.org/x/net v0.18.0 // indirect
	golang.org/x/sys v0.14.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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/hello

go 1.20

require golang.org/x/example/hello v0.0.0-20231013143937-1d6d2400d402

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.9.1 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.16.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.1.1 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.15.0 // indirect
	golang.org/x/net v0.18.0 // indirect
	golang.org/x/sys v0.14.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/gin-gonic/gin => ../gin

然后对我们克隆下来的 Gin 做一点修改(这里添加了一个init方法,打印一句话,方便查看效果), 然后编译运行我们的 hello 项目

go 引用fork后的模块的两种方式(replace和工作区)_第4张图片

$ cd hello/
$ ls
go.mod   go.sum   hello.go
$
$ cat hello.go
package main

import (
   "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.20

use ./foobar
$
$ go run foobar/
hello foobar
$

go work init 命令会把当前目录作为一个workspace并创建一个go.work 文件,类似于 go mod initgo 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/foolib

go 1.20
$
$ vi lib.go
$
SpaceX:foolib zy$ cat lib.go
package foolib

func Add(a, b int) int {
	return a * b
}
$
$ cd ..
$ ls
foobar  foolib  go.work
$ cat go.work
go 1.20

use ./foobar
$
$ go work use foolib/
$
$ cat go.work
go 1.20

use (
	./foobar
	./foolib
)
$ go build ./foolib/
$
$ vi foobar/main.go
$
$ cat foobar/main.go
package main

import "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.20

use (
	./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 main

import "github.com/gin-gonic/gin"

func main() {
	gin.New()
}

$ cat bar/main.go
package main

import "github.com/gin-gonic/gin"

func main() {
	gin.New()
}

$
$ go work use foo/ bar/
$
$ cat go.work
go 1.20

use (
	./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 foo

go 1.20

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.9.1 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.14.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.9.0 // indirect
	golang.org/x/net v0.10.0 // indirect
	golang.org/x/sys v0.8.0 // indirect
	golang.org/x/text v0.9.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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 bar

go 1.20

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.9.1 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.14.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.9.0 // indirect
	golang.org/x/net v0.10.0 // indirect
	golang.org/x/sys v0.8.0 // indirect
	golang.org/x/text v0.9.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.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 foobar,并加入到了 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 指令最终对 foobar 两个模块都生效了,可见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.20

use (
	./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.20

use (
	./bar
	./foo
	./foobar
	./foolib
	./gin
)
$
$ cat foo/go.mod
module foo

go 1.20

require (
	github.com/bytedance/sonic v1.9.1 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/gin-gonic/gin v1.9.1 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.14.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
	github.com/leodido/go-urn v1.2.4 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.11 // indirect
	golang.org/x/arch v0.3.0 // indirect
	golang.org/x/crypto v0.9.0 // indirect
	golang.org/x/net v0.10.0 // indirect
	golang.org/x/sys v0.8.0 // indirect
	golang.org/x/text v0.9.0 // indirect
	google.golang.org/protobuf v1.30.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)
$ cat foo/main.go
package main

import "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 指令。

完!

你可能感兴趣的:(go,mod,go,replace,go,workspace)