damoluomu
发布于 2026-09-02 / 4 阅读
0

一个管「聚」,一个管「引」:单仓和 Submodule 到底差在哪

前几天一个朋友问我:我们团队仓库拆了一地,前端一个、后端一个、组件库又一个,要不要学大厂把所有代码都塞进一个仓库?

我说你先别急着学大厂。单仓(Monorepo)和 Git Submodule 这两个东西经常被摆在一起比,很多人以为是一个问题的两种解法,其实它俩根本不在同一层:单仓是把所有代码收进同一个 Git 仓库,Submodule 是把别人家的独立仓库挂到你家仓库里用。差别在「代码最终归哪个仓库管」,不在代码本身长什么样。

先弄清它俩分别是啥

单仓(Monorepo):所有业务模块、公共组件、工具库、配置全放在同一个 Git 仓库里,用目录层级划分模块。全仓共享一套 .git 元数据、一套提交历史、分支和标签。一次提交可以同时覆盖多个模块的改动,前后端联动改完一个 commit 走人,所有代码始终处在同一个版本基线上。

Git Submodule:在主仓库里嵌入其他独立的 Git 仓库。主仓库只记录两样东西:子模块的远程地址,和一个固定的 commit 哈希(gitlink 引用)。子模块本身是完整的 Git 仓库,有自己的提交历史、分支、标签,完全可以独立演进。

一张小表看清主要差别

单仓

Submodule

本质

所有代码同属一个仓库

主仓挂一串独立子仓

日常更新

改完直接 add/commit,一步

子模块里提交推送,再回主仓更新引用,两步

克隆

一次 clone 全部到手

还得再拉子模块,否则子目录是空的

权限

粗,仓库级,管不到目录

细,子模块按仓库各自设权限

体积

越用越大,克隆变慢

主仓很小,但全部代码拉下来并不小,只是分散了

表格里没列全,还有两个细节:单仓里跨模块改动能做到原子提交(一次 commit 包含所有关联变更),submodule 做不到,跨仓库改动得在各子模块分别提交;另外单仓依赖天然一致,submodule 得手动同步,容易遇到「子模块版本滞后」。

什么时候用哪个

我自己现在的习惯是:自己团队天天一起改的东西,收进单仓。前后端联调、跨模块重构,一次 commit 带走,CI 一起跑,不会出现「代码是对的但版本没对齐」。前端组件库和业务项目放一起、微前端这类要多包协同的场景,也是单仓的主场。

代价也要认:仓库越来越胖,全量克隆慢;权限只能粗到仓库级别;全量构建耗时长,得配 pnpm workspaces、Turborepo 这些工具做增量构建。别误会一点:单仓里代码复用靠目录直接引用,省了「发内部包再安装」那一步,但不是说就不用包管理工具了,模块多了反而要靠 workspaces 理清依赖。

要挂外部独立维护的仓库,才用 submodule。比如项目里嵌一个开源基础库、或者某个组件是别的团队/供应商在维护,权责清晰、不需要跟着你频繁同步,submodule 合适——仓库物理隔离,又嵌在主项目里一起构建发布。

submodule 的坑我也都踩过:clone 完主仓发现子目录空空如也;切了个分支,子模块还停在老 commit 上不动;两个分支各自更新了子模块引用,合并时直接冲突。换来的是操作繁琐、容易忘同步,跨模块联动改动做起来很别扭。

最容易弄混的两处

第一,「单仓里的目录」和「submodule 的目录」看起来都是目录,本质完全不同。单仓里的目录就是普通文件夹,没有独立 Git 历史;submodule 的目录拉取之后是完整的独立仓库,主仓里存的只是指向它的引用(地址 + commit),本身不包含子模块的代码和历史。

第二,submodule 不是单仓的替代品。它本质是多仓(Multirepo)模式下的一种集成手段,整体架构仍然是多仓。单仓管「聚」,submodule 管「引」,两个赛道,谈不上谁取代谁。

所以下次再有人问「单仓和 submodule 哪个好」,不用急着二选一,先反问一句:这些代码是你自己团队在管,还是要挂别人管的仓库?前者往一个仓库里收,省事;后者才用 submodule。一个管聚,一个管引,各干各的活。


参考来源