将子目录作为Go模块导入时在Source Hut遇到的问题
我之前也碰到过类似的Source Hut和Go模块子目录兼容的问题,咱们来一步步拆解解决:
问题核心梳理
你的场景可以总结为:
- 仓库结构:根目录无
go.mod,仅子目录bar有独立的go.mod(声明模块路径为git.sr.ht/~graywolf/foo/bar) - GitHub上导入
github.com/graywolf/foo/bar可正常运行,但Source Hut上导入同结构的git.sr.ht/~graywolf/foo/bar报错:build command-line-arguments: cannot load git.sr.ht/~graywolf/foo/bar: module git.sr.ht/~graywolf/foo@latest (v0.0.0-20191019153505-33a4721605aa) found, but does not contain package git.sr.ht/~graywolf/foo/bar
原因分析
问题根源在于两个平台返回的go-import元标签存在差异:
GitHub的元标签包含
.git后缀,明确告知Go这是仓库地址:<meta name="go-import" content="github.com/graywolf/foo git https://github.com/graywolf/foo.git">Go解析时会将
github.com/graywolf/foo对应到仓库https://github.com/graywolf/foo.git,克隆后自动在子目录bar中查找匹配模块路径github.com/graywolf/foo/bar的go.mod,因此能正常识别。Source Hut的元标签缺少
.git后缀:<meta name="go-import" content="git.sr.ht/~graywolf/foo git https://git.sr.ht/~graywolf/foo">Go会默认将
git.sr.ht/~graywolf/foo当作一个完整的模块路径,克隆仓库后优先检查根目录的go.mod(你这里没有),进而认为该模块不包含bar子包,忽略了子目录的独立模块。
解决方案
方案1:给仓库根目录添加空的go.mod
这是最简便且不改变现有结构的方法:在仓库根目录创建一个go.mod,内容如下:
module git.sr.ht/~graywolf/foo go 1.13
提交推送后,Go克隆仓库时会识别到根目录的模块,同时能正确关联子目录bar的模块路径git.sr.ht/~graywolf/foo/bar,此时再运行你的sr.go就能正常导入了。
方案2:将子目录改为独立仓库(可选)
如果允许拆分模块结构,可以把bar目录单独作为一个仓库git.sr.ht/~graywolf/bar,修改其go.mod为:
module git.sr.ht/~graywolf/bar go 1.13
之后导入路径改为git.sr.ht/~graywolf/bar即可正常使用,适合不需要关联父仓库的场景。
补充说明:为什么Go不直接检查子目录?
Go的模块解析逻辑是先匹配模块路径前缀对应的仓库,再在仓库内查找对应模块的go.mod。当Source Hut的元标签没有.git后缀时,Go会默认将前缀git.sr.ht/~graywolf/foo当作一个完整模块,而非仓库路径,因此只会检查根目录的go.mod,不会自动遍历子目录寻找独立模块。GitHub的.git后缀相当于给Go明确了这是仓库地址,而非模块路径,因此会遍历子目录匹配模块。
内容的提问来源于stack exchange,提问作者graywolf

