应用场景
比如工程中需要以源码级别来使用某个第三方依赖库,这个依赖库和宿主工程都是同一个人维护的。这个时候既想把它们当作两个独立的工程,又想合在一起使用,那就可以使用git submodule指令来满足这个需求。
使用方式
在宿主工程中引入第三方依赖
- main-module 宿主工程
- sub-module 子模块依赖
git submodule add [options] https://github.com/xxx/sub-module.git
文件结构变化:
- package.json
- sub-module
- xxx
- aaa
- .gitmodules
新增了一个sub-module同名文件夹,和一个隐藏文件.gitmodules。
.gitmodules内容
[submodule "sub-module"]
path = sub-module
url = https://github.com/xxx/sub-module.git
宿主工程将本地更新推送到远端:
- sub-module@版本号
- .gitmodules
- xxxx
点击sub-module会跳到sub-module的代码仓库。
克隆含有子模块的宿主工程
直接克隆: git clone xxxx/main.git
- sub-module
- .gitmodules
- xxx
或者加 --recurse-submodules 参数,直接拉取所有子模块
此时克隆的子模块,只是一个空的文件夹,没有任何子模块的代码。
拉取子模块代码
// 1. 初始化本地配置文件
git submodule init
// 2. 根据主模块记录的版本信息,拉取子模块内容
git submodule update
// 初始化+拉取子模块内容+拉取嵌套子模块内容
git submodule update --init --recursive
子模块产生了更新内容
- 当前子模块内部产生了未跟踪的内容变动->比如在宿主工程中直接改了子模块代码。
这里我改了子模块sub-module-2的内容,在main-module宿主工程中使用git status
➜ main-module git:(master) git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
(commit or discard the untracked or modified content in submodules)
modified: sub-module-2 (modified content)
no changes added to commit (use "git add" and/or "git commit -a")
可以看到main-module可以捕捉到子模块的文件变动,但是使用git add/commit指令无法对变更内容产生影响。
如何将变更内容推送到sub-module-2的远端?
进入sub-module-2目录,再执行git add/commit推送到远端。
- 子模块远端产生了更新内容
这种情况通常是主模块和子模块分开维护,且二者更新频率不一致导致的(比较常见)。
这时由于主模块中.git/module记录的版本同子模块的版本保持一致,主模块就无法主动探测到子模块是否发生了变化。
如何去主动的更新子模块内容?
方案一:
进入子模块
cd sub-module-2
git pull
方案二:
暴力更新所有子模块内容,前提是子模块分支统一
// main-module
git submodule foreach 'git pull'
删除子模块
// 删除子模块文件内容
git submodule deinit sub-module
// 1. 删除sub-module文件夹
// 2. 删除.gitmodules文件中关于sub-module的配置
git rm --cached sub-module
其他常用的指令
status [--cached] [--recursive] [--] [<path>...]:查看子模块状态summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]:插件子模块简要(模块与版本的对应情况)sync [--recursive] [--] [<path>...]:根据给出的url,修改所有/指定模块的git url参数
可能遇到的问题
由有子模块的分支,切换到没有子模块的分支,发现有未关联的子模块文件夹。
// 切换分支时添加--recurse-submodules
git checkout --recurse-submodules master
之前拉取过子模块,再次拉取时报A git directory for 'sub-module' is found locally with remote(s):
方案一:走submodule删除机制
方案二:进入.git/module删除对应子模块。
总结
从功能上看:git submodule主要解决了代码分仓的问题,将一个工程拆分成多个工程各自独立维护。
从使用上看:git submodule的仓库很有manorepo风格,使用上感觉和lerna很类似,只不过lerna作为一个工具功能上要比git submodule强大很多,当然二者解决的问题不是一致的,不要混淆。