go list -m -f '{{.Path}} {{.Version}}' -u 显示 go.mod 中显式 require 的模块及其解析版本,精准反映主动选择的依赖清单,不包含间接依赖。

直接看 go list -m -f '{{.Path}} {{.Version}}' -u,它只显示你 go.mod 里显式 require 的模块及其实际解析版本,不掺水、不带间接依赖——这才是你“主动选的”依赖清单。
为什么 go list -m all 不适合查“我写了哪些依赖”
它会把整个依赖树(含所有间接依赖)全列出来,动辄上百行,真正你 require 的那十几行被淹没。比如 github.com/gin-gonic/gin 下面拉进来的 golang.org/x/net、golang.org/x/sys 全都平铺在列表里,看不出谁是根、谁是叶。更关键的是:go list -m all 不体现层级、不标来源、不区分 replace 是否生效,只是一张静态快照。
- 它不会告诉你
gopkg.in/yaml.v2是被spf13/cobra还是urfave/cli拉进来的 - 如果某模块被
replace到本地路径,go list -m all仍显示远程路径 +(devel),容易误判真实加载源 - 输出中混着伪版本(如
v0.0.0-20230101000000-abcdef123456),和语义化版本并存,人工比对成本高
go mod graph 是依赖分析的底层事实源
它输出的是构建时实际解析出的有向边,格式为 A B@v1.2.3,表示模块 A 依赖模块 B 的指定版本。这个输出不受 replace 或 exclude 影响,反映的是原始声明关系,不是运行时映射结果。
- 查某个包是不是你的直接依赖:先
go list -m拿主模块名,再go mod graph | grep "^$(go list -m) " | grep 'target/pkg';没输出就说明不是你require的 - 找谁在用某个旧版模块:比如发现
golang.org/x/net@v0.7.0存在冲突,直接go mod graph | grep 'golang.org/x/net@v0.7.0'就能定位上游模块 - 反向查“谁引入了我”:用
go mod graph | awk '{if ($2 == "github.com/foo/bar@v1.2.3") print $1}',不能靠go mod why -m,后者只给一条最短路径
gomodtree 是最接近“依赖树”的命令行体验
它把 go mod graph 的边关系组织成缩进树形,视觉上接近 npm ls,但要注意它依赖本地模块缓存——CI 环境刚拉代码没跑过 go mod download,可能漏掉部分间接依赖。
立即学习“go语言免费学习笔记(深入)”;
- 安装:
go install github.com/icholy/gomodtree@latest(别装 loic-lopez 版本,已归档) - 运行:
gomodtree默认从当前 module 展开;加参数如gomodtree github.com/your/app可指定目标模块 - 它会标出
(indirect)和(replace),但注意:若replace指向本地目录且该目录未go mod init,gomodtree可能跳过该分支
真正容易被忽略的是:Go 的依赖图不是静态的。go build、go test 甚至 go run 都可能触发隐式模块加载,导致 go list -m all 输出随命令不同而变化;而 go mod graph 始终基于 go.mod 当前状态,更稳定可靠。


















