只在自己机器上改一个依赖模块,马上要在另一个模块里验证,这种来回很常见。若把 replace 写进各自的 go.mod,同事拉下来的构建也会跟着变。
官方的 多模块工作区教程 给出的做法是:在多个模块的共同上级目录里执行 go work init,把要一起参与构建的模块路径写进新生成的 go.work。之后再用 go work use 追加目录。教程里的示例文件包含一行 go 指令和若干 use 指令,语法和 go.mod 相近。加入工作区之后,Go 命令会把这些模块都当作主模块来解析,因此可以在一个模块里改代码,立刻被同一工作区里的另一个模块用到,而不必先把 replace 写进各模块自己的 go.mod。教程也写明,工作区可以用来代替在模块之间添加 replace 指令,方便同时修改。go work use 还可以带 -r,递归检查子目录并把其中的模块一并写进去。
工作区并不会取代模块本身。每个模块仍然要有自己的 go.mod,发布时还是按模块打版本。教程的后续步骤是给被依赖的模块做一次正式发布,再在依赖方用 go get 打到具体版本,这样在工作区之外也能正确解析。模块参考里关于工作区的部分 把工作区定义为一组磁盘上的模块,在最小版本选择时作为主模块集合使用。没有 go.work 时,工作区就只包含当前目录所属的那一个模块。use 指令的参数是模块根目录(里面有 go.mod)的相对路径,不加 -r 时不会自动把子目录里的其他模块加进来,那些目录需要各自写一条 use。参考还提到,go.work 里也可以写 replace,并且会覆盖工作区模块 go.mod 里对同一模块的替换。若多个主模块之间的 replace 互相冲突,必须在 go.work 里删掉或改写清楚。多数与模块相关的子命令会按当前工作区来运作,但 go mod tidy、go get、go mod vendor 这类命令始终只作用于单个主模块,不会因为开了工作区就把几个模块的依赖表一次性收拾完。
要不要把 go.work 提交进版本库,参考的态度偏谨慎:签入的文件可能盖住开发者在父目录里自己的工作区,也可能让持续集成选到错误的依赖版本并测错行为。该页同时承认,若仓库里这组模块只在彼此之间共同开发、几乎不会和外部模块拼出另一套组合,提交一份 go.work 可以说得通,但各个模块仍应被单独测试和发布。不确定当前启用的是哪一份工作区文件时,可以用 go env GOWORK 查看。临时关掉则把 GOWORK 设成 off,也可以让它指向另一个以 .work 结尾的文件。命令方面,除了 init 和 use,还有 go work edit 做低级编辑,以及 go work sync 把工作区构建列表里的依赖同步回各模块。工作区还会维护一份 go.work.sum,用来记录各模块自己的 go.sum 里尚未收录、但工作区用到的校验和。
这和在 go.mod 里用 ignore 指令排除目录、调整 vet 范围不是同一件事,后者可以看站内这篇 Go 1.25 与 go.mod ignore。工作区也不是 Git 的 worktree:工作区管的是一次构建里同时启用哪些本地模块,不是同一仓库的多份工作副本。
你现在是几个服务模块要一起改,还是一个主模块加这份主模块所依赖的本地库?