replace永远优先于require。只要go.mod中存在匹配的replace规则,Go工具链就会跳过require声明的路径和版本,直接使用replace指定的目标,且exclude优先级高于二者,用于彻底剔除某版本。

require 和 replace 谁先起作用
replace 永远优先于 require。只要 go.mod 中存在匹配的 replace 规则,Go 工具链就会跳过 require 声明的路径和版本,直接使用 replace 指定的目标——哪怕那个目标是 ./local/pkg 这样的本地路径。
常见误操作:在开发时加了 replace github.com/xxx/y => ../y,测试通过后忘了删,CI 构建直接失败,报错 cannot find module providing package(因为 CI 环境没有 ../y)。
-
replace不会传递给下游模块;只有当前模块的go build、go test受影响 - 执行
go list -m all可验证实际加载路径,确认是否被replace覆盖 - 用
go mod edit -dropreplace=github.com/xxx/y可临时移除,适合 CI 流水线中清理
exclude 会彻底屏蔽某个版本
exclude 的优先级高于 replace 和 require:它不是“替换”,而是“剔除”。一旦某模块版本被 exclude,Go 就当它根本不存在,后续 MVS(最小版本选择)过程完全不考虑它。
典型场景:上游依赖引入了一个有严重安全漏洞的 v1.5.0 版本,但你又不能立刻升级到 v2.x(因 API 不兼容),这时可写:
立即学习“go语言免费学习笔记(深入)”;
exclude github.com/bad/pkg v1.5.0
注意:exclude 只对当前模块生效,且必须配合 go mod tidy 才真正生效;漏掉这步,go build 仍可能拉取被排除的版本。
间接依赖(// indirect)如何影响版本选择
间接依赖本身不参与 MVS 决策权重,除非它被显式提升为直接依赖。例如:
你的代码 import "github.com/A",而 A 依赖 github.com/B v1.2.0;同时另一个依赖 C 要求 github.com/B v1.4.0。此时 Go 选 v1.4.0 —— 因为它是满足所有直接依赖的最小可行版本。
但如果你手动运行 go get github.com/B@v1.3.0,go.mod 里会出现:
require github.com/B v1.3.0 // indirect
这个 // indirect 注释意味着:你没直接 import B,但它被锁定了。MVS 仍以 v1.4.0 为准,除非你删掉注释,让它变成显式 require。
- 删掉
// indirect后再go mod tidy,B 的版本会被提升为决策依据之一 - 多个
// indirect行共存时,Go 不保证谁胜出;最终版本由 MVS 统一计算,不是按行序 -
go mod graph | grep B能看清谁在拉哪个版本,比猜更可靠
GO111MODULE=on 是一切的前提
如果 GO111MODULE 是 off 或未设置(尤其在老项目或子目录中),Go 会退回到 GOPATH 模式:replace 失效、go.sum 被忽略、go get 行为异常——所有依赖优先级规则都不起作用。
检查方式:go env GO111MODULE 必须输出 on;若不是,加环境变量或在命令前显式设置:
GO111MODULE=on go mod tidy
顺带一提:go version 建议 ≥ 1.19,1.16+ 虽支持模块,但在私有模块认证、go work 多模块协作上稳定性明显不足——低版本下 replace 对私有域名可能静默失效。


















