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/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 指令,比如

$ 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已经为我们自动跳转到了替换后的项目:
在这里插入图片描述

那如果我们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

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

$ 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 项目

在这里插入图片描述

$ 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 指令。

完!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/350217.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3d gaussian splatting介绍整理

3D 高斯分布是用于实时辐射场渲染的 3D 高斯分布中描述的一种光栅化技术,它允许实时渲染从小图像样本中学习到的逼真场景。 paper github 本文翻译整理自: blog: Introduction to 3D Gaussian Splatting DDPMs - Part 2 给出一些2D图片,用…

「阿里云」幻兽帕鲁个人服务器已上线,3分钟快速搭建

基于阿里云搭建幻兽帕鲁服务器方法,1到2分钟部署完成,稳定运行无卡顿,阿里云服务器网aliyunfuwuqi.com分享保姆级手把手教程,基于阿里云计算巢、云服务器或无影云桌面都可以: 基于阿里云幻兽帕鲁服务器创建教程 基于…

WLAN

前言 今天给大家讲一个不一样的实验,生活息息相关,特别有意思的,顺便让大家放松放松 实验 一.引入 实验拓扑图: 明眼人已经知道我没要干嘛了,WIFI无线路由器 所有的PC设备都换成WIMP300N模块无线接收 成功后你们的拓扑图就会和我的一样 二、配置Linksys WRT300N   配置pc3…

循环测试之旅——深度解析Pytest插件 pytest-repeat

在软件开发中,测试的重要性不言而喻。而为了提高测试的鲁棒性和可靠性,Pytest插件 pytest-repeat 应运而生。这个插件可以帮助你轻松实现测试用例的循环运行,以更全面地评估代码的稳定性。本文将深入介绍 pytest-repeat 插件的基本用法和实际案例,助你更好地利用循环测试,…

独占指针:unique_ptr 与 函数调用 笔记

推荐B站视频: 2.unique_ptr_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p2&vd_sourcea934d7fc6f47698a29dac90a922ba5a3 3.unique_ptr与函数调用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p3&vd_sourcea934d…

MIT_线性代数笔记:第 29 讲 奇异值分解

目录 如何实现用矩阵数学语言描述这一过程举例 本讲介绍奇异值分解(Singular value decomposition),简称 SVD。这是矩阵最终也是最好的分解,任意矩阵可分解为 A U Σ V T AUΣV^T AUΣVT,分解结果为正交矩阵 U&#x…

OpenAI API 的最新动态:新一代的嵌入模型,更新 GPT-4 Turbo,更新 GPT-3.5 Turbo 以及降低 API 价格

文章目录 一、前言二、主要内容三、总结 🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、前言 OpenAI 正在推出新一代嵌入模型、新的 GPT-4 Turbo 和审查模型、新的 API 使用管理工具,而且很快就会降低 GPT-3.5 Turbo 的价格。 OpenAI…

【MAC】Multi-Level Monte Carlo Actor-Critic阅读笔记

基本思想: 利用多层次蒙特卡洛方法(Multi-Level Monte Carlo,MLMC)和Actor-Critic算法,解决平均奖励强化学习中的快速混合问题。 快速混合? 在强化学习中,当我们说一个策略"混合得快"…

3D视觉技术快讯

SparseGS主要解决了3D GS(Gaussian Splatting)与NeRF类似的稀疏视角问题,即当训练输入视角很稀疏时,GS会在训练中过拟合,从而在新视角上的测试结果较差。本论文则是提出使用原有的深度先验以及显式的约束来提升GS在稀疏视角下的表现&#xff…

以太网与PON网络的巅峰对决

在这网络的江湖中,各路江湖豪侠都神色匆忙地往同一个地方赶,豪侠们脸上都充满期待和焦虑,生怕错过了什么。这个地方就是传说中的园区网,因为在那里万众期待已久的以太网与PON网络的巅峰对决“将在今天上演。 一方是以太网大侠&am…

Hive 行列转换

行列转换 列转行 使用 lateral view explode(array|map) 或 lateral view inline(array_struct) 可以将列转换为行。 单列转多行,降维(单列数组或键值对) 示例1:explode(array(…)) select ..., A from T lateral view exp…

Java-List接口常用方法和遍历方法

List的继承结构 其中,红色为接口,蓝色为实现类 List的四大方法 List的基本操作void add(int index,E e)boolean remove(Object o)E remove(int index)E set(int index,E e)E get(int index)其中注意删除方法有两种,执行的时候主要选择实参…

作者推荐 | 【深入浅出MySQL】「底层原理」探秘缓冲池的核心奥秘,揭示终极洞察

探秘缓冲池的核心奥秘,揭示终极洞察 缓存池BufferPool机制MySQL缓冲池缓冲池缓冲池的问题 缓冲池的原理数据预读程序的局部性原则(集中读写原理)时间局部性空间局部性 innodb的数据页查询InnoDB的数据页InnoDB缓冲池缓存数据页InnoDB缓存数据…

可解释性AI

方向一:可解释性AI的定义与重要性 1. 什么是可解释人工智能 可解释人工智能(Explainable Artificial Intelligence, XAI)是指智能体以一种可解释、可理解、人机互动的方式,与人工智能系统的使用者、受影响者、决策者、开发者等&…

牛客网---------[USACO 2016 Jan S]Angry Cows

题目描述 Bessie the cow has designed what she thinks will be the next big hit video game: "Angry Cows". The premise, which she believes is completely original, is that the player shoots cows with a slingshot into a one-dimensional scene consistin…

中仕教育:事业单位考试考什么?

事业单位考试分为两个阶段,分别是笔试和面试,考试科目包括公共科目和专业科目两部分。 公共科目内容是公共基础知识、职业能力测试或申论。一种形式为:公共基础知识职业能力测试或职业能力测试申论。另一种形式为:公共基础申论。…

图像字幕中一些广泛使用的技术

文章目录 R-CNNsRNNsLSTMs and GRUsResNet R-CNNs 在图像识别领域,卷积神经网络(CNN)不仅可以识别出图像中的物体,还能检测出这些物体的边界框。如果我们使用传统的CNN进行对象检测,一种方法是在图像上覆盖一层栅格&a…

理想架构的Doherty功率放大器理论与仿真

Doherty理论—理想架构的Doherty功率放大器理论与仿真 参考: 三路Doherty设计 01 射频基础知识–基础概念 ADS仿真工程文件链接:理想架构的Doherty功率放大器理论与仿真 目录 Doherty理论---理想架构的Doherty功率放大器理论与仿真0、Doherty架构的作用…

试卷扫描转化word的功能有吗?分享4款工具!

试卷扫描转化word的功能有吗?分享4款工具! 随着科技的飞速发展,将试卷扫描并转化为Word文档已经成为我们日常学习和工作的常规需求。但是,市面上的扫描工具众多,如何选择一个既方便又准确的工具呢?本文将为…

JDWP原理分析与漏洞利用

JDWP(Java DEbugger Wire Protocol):即Java调试线协议,是一个为Java调试而设计的通讯交互协议,它定义了调试器和被调试程序之间传递的信息的格式。说白了就是JVM或者类JVM的虚拟机都支持一种协议,通过该协议,Debugger 端可以和 target VM 通信,可以获取目标 VM的包括类…