2.1 常用命令
git clone <repository> --recursive 递归的方式克隆整个项目
git submodule add <repository> <path> 添加子模块
git submodule init 初始化子模块
git submodule update 更新子模块
git submodule foreach git pull 拉取所有子模块
项目包含由多个子模块,每个子模块是一个独立的 Git 仓库,子模块还允许继续嵌套包含子模块。 例如,主工程依赖 common、framework、react_native 等多个子模块,而 react_native 子模块又依赖 node_modules、HFCommon、HFModules 等多个嵌套子模块。
3.2 git submodule添加子模块
Git 提供了 submodule 来支持子模块的需求,使用它可以很方便的将多个独立仓库包含到同一个主工程中:
$ git init
$ git submodule add http://xxx.xxx/common.git
$ git submodule add http://xxx.xxx/framework.git
Git submodule 还支持嵌套添加子模块:
$ git submodule add http://xxx.xxx/react_native.git
$ cd react_native
$ git submodule add http://xxx.xxx/HFCommon.git
$ git submodule add http://xxx.xxx/HFModules.git
$ git submodule add http://xxx.xxx/node_modules.git
3.3 Git submodule 使用风险
通过子模块,这些子模块既可以各自独立的修改和提交代码,又可以将改动作用到依赖它的父工程。这听起来是个很棒的特性,然而 Git submodule 也存在着一些让人抓狂的坑。
首先,主工程并不直接跟踪子模块的代码,而仅仅只跟踪子模块的 commit id 的改动。在执行 git submodule update 更新子模块代码时,Git 就是根据主工程所维护的 commit id 来更新子模块到指定状态的。
bash-3.2$ git diff react_native
diff --git a/react_native b/react_native
index 3a9c5b1..ad68a28 160000
--- a/react_native
+++ b/react_native
@@ -1 +1 @@
-Subproject commit 3a9c5b14c45b199e2e6863d2b6da22dabc2a54f5
+Subproject commit ad68a28c13d4196df531c7df8523d07358288297
(END)
因此,如果你只在子模块中修改并提交了代码,而没有到主工程上面再把子模块的 commit id 提交一下,其他人拉取工程代码的时候会发现子模块的代码依然停留在老的 commit id 所指向的状态。对于嵌套子模块,这种工作尤为繁琐,提交代码后要逐层往上提交 commit id ,否则其他人无法正确更新代码。
其次,如前面所说,使用 git submodule update 更新子模块后,子模块将被切换到一个指向父工程维护的 commit id 所指定的游离状态:
bash-3.2$ git submodule update react_native
bash-3.2$ cd react_native
bash-3.2$ git branch
* (detached from 3a9c5b1)
master
jilin
TaiShan
一旦代码处于游离分支,你就要时刻警惕在游离分支上的提交有没有即时合并到非游离分支上。如果你直接在游离分支上开发并提交了代码,之后在父工程里再次 git submodule update ,你所有未合并的提交都会丢失!
最后还有一个非常麻烦,但也极容易出现的问题:如果团队里有人只提交了主工程该子模块的 commit id ,却忘了进入该模块提交模块真正的代码,那么当推送到中央仓库之后,其他人就会因为找不到与该 commit id 对应的代码而无法正确更新代码:
bash-3.2$ git submodule update
error: pathspec 'ad68a28c13d4196df531c7df8523d07358288297' did not match any file(s) known to git.
Did you forget to 'git add'?
Unable to checkout 'ad68a28c13d4196df531c7df8523d07358288297' in submodule path 'react_native'
对于熟练的用户,这些坑自然可以轻松越过。但考虑到团队里大都是 Git 新手,我们发现子模块的引入对他们造成了很大的负担,频繁出现子模块代码没有更新到最新状态,或者更新出错的情况。
3.4 子模块的更新
子模块的维护者提交了更新后,使用子模块的项目必须手动更新才能包含最新的提交。
在项目中,进入到子模块目录下,执行 git pull更新,查看git log查看相应提交。
完成后返回到项目目录,可以看到子模块有待提交的更新,使用git add,提交即可。
3.5 删除子模块
有时子模块的项目维护地址发生了变化,或者需要替换子模块,就需要删除原有的子模块。
删除子模块较复杂,步骤如下:
rm -rf 子模块目录 删除子模块目录及源码
vi .gitmodules 删除项目目录下.gitmodules文件中子模块相关条目
vi .git/config 删除配置项中子模块相关条目
rm .git/module/* 删除模块下的子模块目录,每个子模块对应一个目录,注意只删除对应的子模块目录即可
执行完成后,再执行添加子模块命令即可,如果仍然报错,执行如下:
git rm --cached 子模块名称
4.2 忽略 Git submodule
首先 .gitmodules 文件只是一个初始化配置。当你执行 git submodule init 时,git 会从 .gitmodules 读取信息来操作 .git/ 目录下的文件,比如修改 .git/config 以及修改 .git/modules/ 目录等操作。
存在一种使用场景,一个很大的项目下有些 submodule 项目不能让某些开发者访问。那么执行 git submodule update 会报错导致中断。
你可以忽略那些没有访问权限的子项目。假设有个名叫 xxx 的 submodule。
那么在 git submodule init 之后,修改 .git/config 文件,修改 active=false 和 ignore=all 即可。即:
[submodule "xxx"]
url = git@github.com:zzz/xxx.git
active = false
ignore = all
之后的 git submodule 操作都会忽略掉 xxx 项目了。
这些配置可以通过 git help config 来找到,比如搜索 submodule..ignore。