Git Submodule 使用教程

创建测试项目

准备环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建测试目录
mkdir git_submodule
cd git_submodule
# 创建本地裸仓库
mkdir repos
cd repos
git init --bare lib1.git
git init --bare lib2.git
git init --bare project1.git
git init --bare project2.git
# 创建工作区目录
cd ..
mkdir ws
cd ws

初始化测试项目 project1 和 project2

1
2
3
4
5
6
7
8
9
10
11
12
13
# 初始化 project1
git clone ../repos/project1.git
cd project1
echo "project1" > project_infos.txt
git add .
git ci -m "init project1"
# 初始化 project2
cd ..
git clone ../repos/project2.git
cd project2
echo "project2" > project_infos.txt
git add .
git ci -m "init project2"

初始化公共类库 lib1 和 lib2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 初始化公共类库 lib1
cd ..
git clone ../repos/lib1.git
cd lib1
echo "This is lib1" > lib1_features
git add .
git ci -m "init lib1"
git push origin master
# 初始化公共类库 lib2
cd ..
git clone ../repos/lib2.git
cd lib2
echo "This is lib2" > lib2_features
git add .
git ci -m "init lib2"
git push origin master

为 project1 添加 submodules ( lib1 和 lib2 )

1
2
3
cd ../project1
git submodule add ../../repos/lib1.git libs/lib1
git submodule add ../../repos/lib2.git libs/lib2

此时,执行ls命令,会发现多了个libs目录
upload successful

接着,查看状态,会发现lib1lib2和另外一个新文件.gitmodules都已经被加入了暂存区
upload successful

先看一下lib1lib2里面的文件的内容,跟公共类库内容一致
upload successful

接着看一下.gitmodules的内容,会发现 .gitmodules记录了每个 Submodules 的引用信息(在当前项目的位置以及引用仓库的URL)
upload successful

最后,执行如下命令,将更改提交并推送到远程仓库

1
2
git ci -a -m "add submodules[lib1,lib2] to project1"
git push origin master

模拟开发人员 B 对 submodules 的修改

开发人员 B 克隆测试项目 project1

1
2
3
cd ..
git clone ../repos/project1.git project1-b
cd project1-b

执行git submodule命令,会发现列出了所有子模块(Submodules),但是,hash 码前面有一个-号,表明该子模块还没有检出
upload successful

接下来,检出 project1-b 的所有子模块,执行git submodule init命令
upload successful

上一步,仅完成了子模块在当前项目的注册,想检出子模块的内容,还需要执行git submodule update命令
upload successful

开发人员 B 修改子模块 lib1

首先,进入lib1所在的目录,并查看状态
upload successful

要修改lib1的文件,首先需要切换到相应的分支
upload successful

修改、提交、推送

1
2
3
4
echo 'added by developer B' >> lib1_features
git add .
git ci -m "update lib1_features by developer B"
git push origin master

开发人员 B 在项目(project1-b)目录中,提交更新并推送

回到project1-b所在的目录,会发现project1-b的状态发生了变化,很好理解,因为我们更改了子模块里面的文件,子模块也属于项目project1-b本身
upload successful

最后一步,在project1-b项目中提交更新,并推送

1
2
3
git add .
git ci -m "update libs/lib1 by developer B"
git push origin master

在 project1 中同步开发人员 B 对 submodules 做的修改

1
2
cd ../project1
git pull origin master

查看状态,会发现libs/lib1处于modified
upload successful

很好理解,我们需要手动更新 submodules 的更新
upload successful

至此,我们就在 project1 中成功同步了开发人员 B 对 submodules 所做的修改了

为 project2 添加 submodules ( lib1 和 lib2 )

1
2
3
4
5
6
cd ../project2
git submodule add ../../repos/lib1.git libs/lib1
git submodule add ../../repos/lib2.git libs/lib2
git add .
git ci -m "add submodules[lib1,lib2] to project2"
git push origin master

在 project2 中修改 lib1 和 lib2 并同步到 project1 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 在 lib1 中添加一个文件 README
cd libs/lib1
echo "This is lib1 readme contents" > README
git add .
git commit -m "add README for lib1"
git push origin master
cd ..
git add .
git commit -m "update lib1 - add README"

# 修改 lib2 中的 lib2-features 文件
cd libs/lib2
echo "some new feature" >> lib2_features
git add .
git ci -m "add some new feature into lib2_features"
git push origin master
cd ../../
git add .
git commit -m "update lib2 - add some new features"

# 推送 project2 的修改
git push origin master

# 在 project1 中更新 lib1 和 lib2 的最新修改,并推送
cd ../project1
git pull origin master
cd libs/lib1
git co master
git pull origin master
cd ../lib2
git pull
cd ../../
git ci -a -m "update lib1 and lib2 to new version"
git push

更新 project1-b 项目的子模块

根据以上的操作,我们总结出来了修改并更新子模块的基本流程

  • 切换到子模块的某个分支
  • 修改子模块
  • 提交子模块的修改
  • 推送子模块的修改
  • 在项目目录提交子模块的修改
  • 在项目目录推送子模块的修改
  • 在其他项目的项目目录新(pull)子模块的修改
  • 在其他项目的子模块目录中依次更新(pull)
  • 在其他项目的项目目录中提交并推送修改

事实上,修改子模块、提交和推送的操作确实需要一步步细致地进行,不过在其他项目中,更新对子模块的修改,步骤太繁琐。幸运的是我们可以使用git submodule foreach git pull命令,一步到位完成各子模块的同步更新。

最后,在项目目录中提交并推送

1
2
3
4
5
cd ../project1-b
git pull
git submodule foreach git pull
git ci -a -m "update libs1 and libs2 to new version"
git push origin master

新成员加入团队,一次性克隆项目和 submodules

根据已有知识,我们可以这样操作

1
2
3
git clone /path/to/repos/foo.git
git submodule init
git submodule update

幸运的是,我们可以一行命令搞定

1
git clone --recursive ../repos/foo.git 

–recursive参数的含义:可以在克隆项目时同时克隆关联的submodules

移除 submodules

1
2
3
4
git submodule deinit libs/lib1
git rm libs/lib1
git ci -m "remove libs/lib1"
git push origin master