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

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

说完题外话,说说正题,笔者使用 Golang 也有两三年了,基本还停留在会用就行,没有深挖细节,不符合我刨根问底的学习习惯,接下来一段时间,准备系统的看看 go 官方文档,毕竟这是最新最权威的资料,然后顺手写点东西,就当是加深映像吧。

今天要说的是我们怎么在项目里使用fork的别人的模块,因为fork别人的项目之后,项目的路径和 go.mod 里声明的路径就不一样了,直接 go get 是引入不了的,比如这里引入我 fork 后的 github.com/zhyee/gin 框架会报错:

bash 复制代码
$ 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/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官方当然也考虑到了这种常见的使用场景。

使用 replace 指令

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

bash 复制代码
$ 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] 模块中的版本号如果被省略,则用于替换的模块应该是一个本地模块而不是网络模块

bash 复制代码
$
$ 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 会发现又会重新拉下来一些东西:

bash 复制代码
$ 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/gingo mod tidy

bash 复制代码
$ 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

bash 复制代码
$ 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/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 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后的远端仓库就可以直接调试运行看到效果:

bash 复制代码
$ 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 项目

bash 复制代码
$ 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 是比较困难的,比如:

bash 复制代码
$ 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 路径下也还是会报错:

bash 复制代码
$ 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的出现解决了这个问题:

bash 复制代码
$ 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中的各个模块之间相互调用是非常方便的,比如:

bash 复制代码
$ 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 都生效:

bash 复制代码
$ 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 指定本地模块路径差不多:

bash 复制代码
$ 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 指令。

完!

相关推荐
魔法小匠2 个月前
Go 模块化开发入门(go mod 深度解析)
服务器·golang·go·模块化·go mod
景天科技苑5 个月前
【Golang】Go语言中如何进行包管理
开发语言·后端·golang·go mod·go语言包管理·go包管理·go sum
月屯1 年前
go mod
go·go mod
takujo2 年前
go mod 添加私有库GOPRIVATE
golang·go mod·goprivate