解决 Go 依赖冲突
如下图所示:
其中,go.timd.cn/c 从 v0.0.1 升级到 v0.0.2 时,存在破坏性更新,导致 go.timd.cn/a@v0.0.1 无法与 go.timd.cn/b@v0.0.2 一起工作。
go.timd.cn/c 的项目结构如下:
.
├── c.go
├── go.mod
└── pkg
└── print.go
v0.0.1 版本 go.mod 的内容如下:
module go.timd.cn/c
go 1.22.6
v0.0.1 版本 c.go 的内容如下:
package c
func C() {
println("[go.timd.cn/c] C called")
}
v0.0.1 版本 pkg/print.go 的内容如下:
package pkg
func Print() {
println("[go.timd.cn/c/pkg] Print called")
}
v0.0.2 版本删除 pkg
包。
go.timd.cn/b 的项目结构如下:
.
├── b.go
├── go.mod
└── go.sum
v0.0.1 版本 go.mod 的内容如下:
module go.timd.cn/b
go 1.22.6
require go.timd.cn/c v0.0.1
v0.0.1 版本 b.go 的内容如下:
package b
import (
"go.timd.cn/c"
"go.timd.cn/c/pkg"
)
func B() {
println("go.timd.cn/b started")
c.C()
pkg.Print()
println("go.timd.cn/b ended")
}
v0.0.2 版本 go.mod 的内容如下:
module go.timd.cn/b
go 1.22.6
require go.timd.cn/c v0.0.2
v0.0.2 版本 b.go 的内容如下:
package b
import (
"go.timd.cn/c"
)
func B() {
println("go.timd.cn/b started")
c.C()
println("go.timd.cn/b ended")
}
go.timd.cn/a 的项目结构如下:
.
├── a.go
├── go.mod
└── go.sum
v0.0.1 版本 go.mod 的内容如下:
module go.timd.cn/a
go 1.22.6
require go.timd.cn/b v0.0.1
require go.timd.cn/c v0.0.1
v0.0.1 版本 a.go 的内容如下:
package a
import (
"go.timd.cn/b"
"go.timd.cn/c"
"go.timd.cn/c/pkg"
)
func A() {
println("go.timd.cn/a started")
b.B()
c.C()
pkg.Print()
println("go.timd.cn/a ended")
}
mkdir test
cd test/
go mod init test
go.mod 的内容如下:
module test
go 1.22.6
require go.timd.cn/a v0.0.1
require go.timd.cn/b v0.0.2
require go.timd.cn/c v0.0.2 // indirect
test.go 的内容如下:
package main
import (
"go.timd.cn/a"
"go.timd.cn/b"
"log"
)
func main() {
log.Println("test started")
a.A()
b.B()
log.Println("test ended")
}
执行 go mod tidy
,得到类似下面的错误:
go: finding module for package go.timd.cn/c/pkg
go: test imports
go.timd.cn/a imports
go.timd.cn/c/pkg: module go.timd.cn/c@latest found (v0.0.2), but does not contain package go.timd.cn/c/pkg
go mod graph
Graph 以文本形式打印应用 replace
后的模块依赖图。在输出中,每行有两个空白分隔的字段:模块及其一个依赖。除 main 模块没有 @version 外,每个模块被标识为 path@version 形式的字符串。
可以使用如下脚本将依赖图转换成图片格式:
需要安装 GraphViz:
apt install -y graphviz
#!/bin/bash
go_mod_graph=$(go mod graph)
# 将输出转换为 Graphviz 格式
echo "digraph G {" > graphviz_input.dot
echo " rankdir=LR;" >> graphviz_input.dot
echo " node [shape=box];" >> graphviz_input.dot
# 遍历每一行,添加节点和边
while read -r line; do
# 移除行尾的换行符
line=$(echo "$line" | tr -d '\r')
# 将依赖关系添加到文件中
echo " \"$line\";" >> graphviz_input.dot
done <<< "$go_mod_graph"
echo "}" >> graphviz_input.dot
dot -Tpng graphviz_input.dot -o go_mod_graph.png
rm graphviz_input.dot
列出具名包:
go list -json -m [all | 模块名]
对于第 1 节的示例,执行 go list -json -m go.timd.cn/c
输出:
{
"Path": "go.timd.cn/c",
"Version": "v0.0.2",
"Time": "2024-09-11T06:31:54Z",
"Indirect": true,
"Dir": "/root/go/pkg/mod/go.timd.cn/c@v0.0.2",
"GoMod": "/root/go/pkg/mod/cache/download/go.timd.cn/c/@v/v0.0.2.mod",
"GoVersion": "1.22.6"
}
可见 MVS 算法最终选择的是 go.timd.cn/c@v0.0.2,而 go.timd.cn/a@v0.0.1 使用 go.timd.cn/c@v0.0.2 模块中已经移除的包 go.timd.cn/c/pkg 导致 go mod tidy
报错。
建议。
建议。
不建议。
如果想在 test 模块中,同时使用 go.timd.cn/c 的 v0.0.1 和 v0.0.2 版本,并且不改变 go.timd.cn/a,那么可以 FROK 不向前兼容的模块:go.timd.cn/b@v0.0.2、go.timd.cn/c@v0.0.2。
# 为避免干扰,清理缓存
go clean -cache
go clean -modcache
# 创建 b-fork 和 c-fork 存储库
git init --bare ~git/git-repository/b-fork.git
git init --bare ~git/git-repository/c-fork.git
sudo chown -R git:git ~git/git-repository/
# 创建 Workspace
mkdir workspace
cd workspace/
git clone git@127.0.0.1:/home/git/git-repository/b.git/ b-fork
git clone git@127.0.0.1:/home/git/git-repository/c.git/ c-fork
进入 c-fork 目录:
git remote remove origin
git remote add origin git@127.0.0.1:/home/git/git-repository/c-fork.git/
git checkout -b fork-v0.0.2 v0.0.2
git branch
将 go.mod 修改为:
module go.timd.cn/c-fork
go 1.22.6
提交:
git commit -a -m "v0.0.2"
git push -u origin fork-v0.0.2
git tag -d v0.0.2
git tag v0.0.2
git push origin v0.0.2
进入 b-fork 目录:
git remote remove origin
git remote add origin git@127.0.0.1:/home/git/git-repository/b-fork.git/
git checkout -b fork-v0.0.2 v0.0.2
git branch
将 b.go 修改为:
package b
import (
c "go.timd.cn/c-fork"
)
func B() {
println("go.timd.cn/b started")
c.C()
println("go.timd.cn/b ended")
}
将 go.mod 修改为:
module go.timd.cn/b-fork
go 1.22.6
require go.timd.cn/c-fork v0.0.2
提交:
go mod tidy
go mod download
git commit -a -m "v0.0.2"
git push -u origin fork-v0.0.2
git tag -d v0.0.2
git tag v0.0.2
git push origin v0.0.2
回到 workspace 目录,初始化 test:
mkdir test
cd test/
go mod init test
go get go.timd.cn/a@v0.0.1
go get go.timd.cn/b-fork@v0.0.2
test.go 的内容如下:
package main
import (
"go.timd.cn/a"
b "go.timd.cn/b-fork"
"log"
)
func main() {
log.Println("test started")
a.A()
b.B()
log.Println("test ended")
}
运行:
go mod tidy
go mod download
回到 workspace 目录:
go work init b-fork/ c-fork/ test/
go run ./test
逐渐修改 b-fork、c-fork 使其可以正常工作,然后提交、打标签。具体流程,可参考 http://timd.cn/go-workspace/ 中的 1.4.1 和 1.4.2 小节。