Git中文社区

子模块

有时一个项目需要引用另一个独立的项目,比如一个公共组件库、一个第三方库。你希望它们保持独立的版本历史,又能在主项目中使用。Git 的**子模块(submodule)**就是为此设计的。

添加子模块

git submodule add https://github.com/example/ui-kit.git libs/ui-kit

执行后会发生两件事:

  1. 子项目被克隆到 libs/ui-kit 目录;
  2. 生成一个 .gitmodules 文件,记录子模块的路径和地址:
[submodule "libs/ui-kit"]
	path = libs/ui-kit
	url = https://github.com/example/ui-kit.git

提交这些变更:

git commit -m "添加 ui-kit 子模块"

注意

主项目并不保存子模块的文件内容,只记录子模块当前指向的提交。这意味着子模块是被"锁定"在某个特定版本上的。

克隆含有子模块的项目

默认克隆时,子模块目录是空的。有两种方式获取子模块内容:

# 方式一:克隆时一并拉取
git clone --recurse-submodules https://github.com/example/main-project.git

# 方式二:克隆后再初始化
git submodule update --init --recursive

更新子模块

拉取子模块的最新版本

# 把所有子模块更新到其远程分支的最新提交
git submodule update --remote

# 只更新某一个
git submodule update --remote libs/ui-kit

更新后,主项目会看到子模块指向了新的提交,需要提交这一变化:

git add libs/ui-kit
git commit -m "升级 ui-kit"

同步其他人的子模块更新

当同事更新了子模块的版本并推送后,你拉取主项目后还需要执行:

git pull
git submodule update --init --recursive

可以设置让 git pull 自动处理子模块:

git config --global submodule.recurse true

在子模块中修改代码

子模块本身就是一个完整的 Git 仓库,可以进入目录进行开发:

cd libs/ui-kit
git switch main
# ... 修改、提交 ...
git push

cd ../..
git add libs/ui-kit
git commit -m "更新 ui-kit 引用"

警告

子模块默认处于分离头指针状态。在子模块中开发前,先切换到一个分支,否则提交可能"丢失"。

批量操作

# 查看所有子模块状态
git submodule status

# 在每个子模块中执行命令
git submodule foreach 'git switch main && git pull'

删除子模块

git submodule deinit -f libs/ui-kit
git rm -f libs/ui-kit
rm -rf .git/modules/libs/ui-kit
git commit -m "移除 ui-kit 子模块"

子模块的替代方案

子模块概念较多,容易出错。根据场景,也可以考虑:

  • 包管理器(npm、Maven、pip 等):对于第三方依赖,这通常是最佳选择;
  • git subtree:把另一个仓库的内容直接合并到子目录中,使用者无需额外操作;
  • Monorepo:把相关项目放在同一个仓库中统一管理。