子模块
有时一个项目需要引用另一个独立的项目,比如一个公共组件库、一个第三方库。你希望它们保持独立的版本历史,又能在主项目中使用。Git 的**子模块(submodule)**就是为此设计的。
添加子模块
git submodule add https://github.com/example/ui-kit.git libs/ui-kit执行后会发生两件事:
- 子项目被克隆到
libs/ui-kit目录; - 生成一个
.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:把相关项目放在同一个仓库中统一管理。