gomodtree 是最接近拓扑树直觉的命令行工具,输出带缩进和分支线的依赖树,支持标注 (indirect) 和 (replace),但依赖本地 go.sum 和模块缓存,CI 环境未执行 go mod download 时可能漏掉间接依赖。

用 gomodtree 看缩进式依赖树
gomodtree 是目前最接近“拓扑树”直觉的命令行工具,输出带缩进和分支线,类似 tree 命令。它能标出 (indirect) 和 (replace),但依赖本地 go.sum 和模块缓存——CI 环境刚拉代码没执行过 go mod download,可能漏掉部分间接依赖。
安装命令是:go install github.com/icholy/gomodtree@latest(注意不是 loic-lopez 版本,后者已归档)。
运行时默认从当前 module 展开;指定模块可用:gomodtree github.com/your/app。
常见误操作:在未 go mod download 的干净环境里直接跑,结果树不全;或者误装旧版导致命令不存在或输出异常。
用 go mod graph 提取原始依赖边再人工/脚本还原层级
go mod graph 输出的是扁平有向边,每行形如 github.com/user/project github.com/sirupsen/logrus@v1.9.0,表示前者直接依赖后者。它不处理 replace 和 exclude,即使你本地用了 replace github.com/foo/bar => ./local,图中仍显示原始路径。
想只看主模块的直接依赖,可过滤:go mod graph | grep "^$(go list -m) " | cut -d' ' -f2。
若需反向查“谁引入了某模块”,go mod graph 本身不支持,得配合 awk 或用 go mod why -m 查单条路径。
输出行数常达上千,不加过滤基本无法肉眼阅读;也别指望靠它直接看出“哪一层用了哪个版本”,它只记录边,不维护层级上下文。
用 go mod why -m 定位特定模块的引入路径
当你在 go list -m all 里看到一个陌生模块(比如 gopkg.in/yaml.v2),又不确定谁把它拉进来的,go mod why -m gopkg.in/yaml.v2 会给出一条最短路径,例如:main → github.com/spf13/cobra → github.com/spf13/pflag → gopkg.in/yaml.v2。
这个命令只返回第一条可达路径,不是全路径;如果某模块被多个路径引入不同版本,它不会告诉你其他路径,得手动结合 go mod graph | grep 比对。
对 replace 后的本地路径无效——go mod why -m ./local-fix 会报错,必须查原始模块名。
依赖树不是静态快照,构建时才真正确定
go list -m all、go mod graph、gomodtree 都基于 go.mod 和本地缓存生成视图,但实际编译时(go build、go test)可能触发隐式加载,导致最终使用的模块与这些命令输出不一致。
真正要确认某个二进制用了什么版本,得看 go version -m your-binary;而 go mod graph 输出的节点带完整版本号(如 golang.org/x/net@v0.14.0),文字过长影响可读性,可视化前建议先用 sed 's/@[^ ]*//g' 清洗。

















