当一个项目很大很复杂时,可以将项目分为几个模块分别进行管理;或者,当一个项目引用第三方开源代码,可以将这些第三方开源代码单独进行管理,这样做是为了代码隔离,方便项目维护。这时可以使用git的submodule功能,git submodule允许你将一个 git 仓库作为另一个 git 仓库的子目录。
git submodule用于多模块(仓库)管理,其父项目与子项目提交是分开的,父项目提交只包含子项目的信息,而不会包括子项目的代码,子项目使用独立的commit、push、pull操作。
简单来说:父项目git仓库管理父项目内容和子项目信息;子项目git仓库管理子项目内容。
常用命令:
git submodule add # 添加子模块
git submodule init # 初始化子模块
git submodule update # 更新子模块:
git clone --recursive # 递归的方式克隆整个项目:
git submodule foreach git pull # 拉取所有子模块
git submodule foreach git checkout master # 所有子模块切到master分支
1 添加、同步子模块
这里通过一个示例来展示git submodule的使用。
1.1 添加一个模块
环境准备:
首先需要准备一个父项目与一个子项目。详细步骤见参考3《如何将本地代码推送至github》
- 在github上新建一个父项目 submodule_main;并在本地新建工程,与github库绑定
mkdir submodule_main
cd submodule_main
echo "## submodule_main" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin [email protected]:sz-ok/submodule_main.git
git push -u origin main
- 同样,在github上新建一个子项目submodule_subdir;并在本地新建工程,与github库绑定
mkdir submodule_subdir
cd submodule_subdir
echo "## submodule_subdir" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin [email protected]:sz-ok/submodule_subdir.git
git push -u origin main
这样,我们已经有了父项目submodule_main与子项目submodule_subdir,然后我们可以进行添加子模块的操作。
添加子模块:
git clone [email protected]:sz-ok/submodule_main.git # clone 父项目
cd submodule_main/ # 进入父项目目录
git submodule add [email protected]:sz-ok/submodule_subdir.git #在主项目中添加子项目
这时项目中会多出submodule_subdir文件夹,是独立的子项目。整个项目包括了多个.git仓,且主项目目录下会多一个.gitmodules文件。
然后,在主项目目录下查看状态:
submodule_main$ git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .gitmodules
new file: submodule_subdir
可见在添加子模块后,主项目下需要将子项目信息提交一次,提交后,在主项目仓库中,会显示出子模块文件夹,并附带其所在仓库的版本号,如:submodule_subdir @ abcd1234。
git add -u
git commit -m "add submodule"
git push # 因为在建库时用了git push -u 所以后面push时可以直接git push
最终形成的目录结构如下(以下展示局部):
submodule_main$ tree -a -L 3
submodule_main
├── .git # 父项目仓库
│ ├── branches
│ ├── config
│ ├── description
│ ├── HEAD
│ ├── hooks
│ ├── index
│ ├── info
│ ├── logs
│ ├── modules # 管理子项目的modules
│ │ └── submodule_subdir
│ ├── objects
│ ├── packed-refs
│ └── refs
├── .gitmodules # 管理子项目的modules
├── README.md
└── submodule_subdir
├── .git # 子项目仓库
└── README.md
其中.gitmodules用来管理子模块,内容如下:
submodule_main$ cat .gitmodules
[submodule "submodule_subdir"]
path = submodule_subdir
url = [email protected]:sz-ok/submodule_subdir.git
1.2 clone已经存在的模块
如果我们从库上clone一个多仓库的项目,继续使用上述例子做实验,将上述例子的submodule_main文件夹删掉,然后进行下面操作。
方法1:
git clone [email protected]:sz-ok/submodule_main.git # 单独使用 git clone 命令是不会拉取到子项目的代码的
cd submodule_main/
git submodule init # 只用执行一次,后续同步只需执行git submodule update
git submodule update # 更新子模块
这样就可以得到如上节一样的工程,示例中仅有一个模块,不能充分体现git submodule的优势,如果子模块非常多也是这样同步操作,这样不必一个个pull然后组织在一起。
方法2:
组合命令:
git clone [email protected]:sz-ok/submodule_main.git # 单独使用 git clone 命令是不会拉取到子项目的代码的
cd submodule_main/
git submodule update --init --recursive
方法3:
git clone时带上--recursive参数。
git clone [email protected]:sz-ok/submodule_main.git --recursive # 递归的方式克隆整个项目
2 子模块库修改及提交
由于:父项目git仓库管理父项目内容和子项目信息;子项目git仓库管理子项目内容。
所以:
当父项目中的内容有改动,还是正常的git add, git commit, git push操作
子项目中内容有改动时,同样需要git add, git commit, git push操作,然后父项目也要提交一次改动(因为子项目信息有改动)
父项目通过git diff并不能直接看到子项目的修改,如果需要查看,可在父目录下执行
git diff --submodule
3 模块的删除
删除子模块稍微比较复杂,还是以上述例子为例。
git rm --cached submodule_subdir
rm -rf submodule_subdir
rm .gitmodules
vim .git/config
进行上述操作后,需要在父目录下进行一次提交。
4 模块的分支管理
当clone包换submodule的项目时,主项目获取到的是子项目的commit id,所以clone之后子项目不在任何分支上。主项目执行 git submodule update 也不会更新,此时需要从主项目主动进入子项目执行 git pull 主动拉取新版代码,然后提交,更新子项目的commit id。如果要在主项目开发子模块,建议将子项目切到master分支进行提交。当主项目 clone 后,也可以使用 foreach 命令批量切换到 master 分支进行更新。
若一个项目中有多个子模块需要执行相同的操作,每次切换到对应的目录挨个执行效率太低,此时可以使用 git submodule foreach
类似:
git submodule foreach git checkout master
git submodule foreach git pull
参考:
- 使用Jenkins + git submodule 实现自动化编译,解决代码安全性问题
- Git Submodule管理项目子模块
- 如何将本地代码推送至github
- 公共模块管理之 Git Submodule 使用总结