git submodule
是git工具提供的一种特性,实现对项目依赖的其它代码仓库的管理。该特性主要是将相关的仓库建立一定的逻辑关系,并在顶级仓库中维护各个子仓库的相关信息,达到更好的管理依赖的代码库的目的。有了该特性就可以对项目代码仓库进行合理的划分,建立耦合度高的代码仓库,改善多人使用同一仓库开发提交代码时容易产生冲突的问题。同时可以更好的管理第三方代码仓库。git submodule是git工具提供的一种特性,实现对项目依赖的其它代码仓库的管理。该特性主要是将相关的仓库建立一定的逻辑关系,并在顶级仓库中维护各个子仓库的相关信息,达到更好的管理依赖的代码库的目的。有了该特性就可以对项目代码仓库进行合理的划分,建立耦合度高的代码仓库,改善多人使用同一仓库开发提交代码时容易产生冲突的问题。同时可以更好的管理第三方代码仓库。
一、相关概念与原理
顶级仓库:也可以叫根仓库,用于管理git代码的最上一层仓库,用于代码的发布;
父仓库:子仓库的父仓库,源于管理多个子仓库,顶级仓库就是其子仓库的父仓库;
子仓库:每一个被添加到父仓库的仓库都被称为父仓库的子仓库;
绑定关系:父仓库与其子仓库(版本号)的关系。一旦绑定关系确定,在不修改绑定关系的前提下,检出父仓库并归位(更新)子仓库,子仓库的版本号是固定的。下面详细描述绑定关系的原理。
1、绑定关系
大多应用与模块间的引用。例如,平台组发布D驱动仓库,其中D1、D2是相继两个版本。项目组S需要使用D仓库与release仓库R进行绑定。当前Release仓库含有R1、R2两个版本。在R2的版本上,R加入子仓库D,并R与D2进行绑定。紧接着,平台组在D仓库D2基础上发布了D3版本。R仓库每次检出的时候,更新后的子仓库都是D2版本,而不是D3,因为绑定关系是R&D2,和D3没有关系。也可以重新进行绑定,即R与D3进行绑定。以后R仓库每次检出的时候,更新后的子仓库都是D3版本。
顶层仓库可以绑定多个子仓库,绑定关系类似一棵树,除了树根以外,每个节点都是一个版本号。绑定关系确定了,这个树的各节点的值就确定了。
2、父子仓库
父仓库中可以有子仓库,可以有多个子仓库。子仓库中也可以有子仓库。
二、首次将父仓库与子仓库进行绑定
git submodule
提供了一系列用于管理子仓库的命令,通过这一系列命令并结合git相关命令完成子仓库的管理工作。
1、添加子仓库
指令:
git submodule add Git_URL child_dir
- 1
解释:
- 指令含义:将子仓库的信息添加到父仓库的
.gitmodules
文件,并在父仓库的工作副本创建用于管理子仓库工作副本的目录。 .gitmodules
:在父仓库的根目录下,用于管理子仓库的路径。Git_URL
: 指定子仓库的地址,比如http://172.0.20.254/gerrit/platform/web/uniweb
child_dir
: 在父仓库工作副本中用于管理子仓库工作副本的目录,必须是父仓库中不存在的目录。
特别注意:
child_dir目录千万不能手动创建,只能通过上述指令(自动)创建。如果执行指令前路径child_dir已存在,该指令会执行失败。需要手动删除后再试。
示例:
/* 以在web路径下添加uniweb子仓库为例 */ git submodule add
http://172.0.20.254/gerrit/platform/web/uniweb web/uniweb
2、下载并初始化子仓库
初始化子仓库,并下载代码。init和update后面,可直接跟子仓库的目录名。
git submodule init
git submodule update
- 1
- 2
这两行命令也可以合并为一行:
git submodule update --init
- 1
3、受控特定版本子仓库(绑定)
可能最新代码或默认分支(master)不是我们要绑定的版本,需要对子仓库进行切换分支和版本。
cd web/uniweb
git checkout BRANCH_1
git reset --hard Commit-ID_1
cd -
git add web/uniweb
git commit
- 1
- 2
- 3
- 4
- 5
- 6
三、日常使用
1、子仓库有变动
注意:母仓库是M,子仓库是A。A包含在M工程中以submodule形式存在。在如gerrit开发方式中,直接在M中的A目录内修改是无法push到远程仓库。正确做法是:单独clone出A仓库,修改,提交,push,合并到远端仓库。再在M中更新A这个submodule。
如果子仓库本地有变动,那么在父仓库下,执行git status可以看到如下几种情况:
# modified: child_dir (new commits)
- 1
上面的结果表示,子仓库与父仓库不在当前绑定关系中。子仓库的版本,要么是更旧了,要么是更新了。
# modified: child_dir (untracked content)
- 1
上面的结果表示子仓库中,有未受控的内容(Untracked)。
# modified: api_1 (modified content)
- 1
上面的结果表示子仓库中,有受控的文件被修改。
2、重新绑定
父仓库和子仓库的绑定关系需要重新确定,即,子仓库有更新,父仓库以后都要使用新版的子仓库代码。
在子仓库的目录中,切换到目标分支,拉下最新代码(如果有必要)。切换到目标版本(不一定是更新的版本)。
cd web/uniweb
git checkout BRANCH
git pull
git reset --hard Target_Commit_ID
cd -
git add web/uniweb
git commit
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3、绑定关系查看
git submodule status
git submodule status --recursive #包含上面的情况,也显示子仓库中子仓库的绑定情况
- 1
- 2
版本号之前的符号,“-”表示仓库检出的时候,还没有执行git submodule update;“+”表示子仓库版本与父仓库绑定关系不符。“ ”表示正常。
4、绑定关系归位
当前可能的状况是:子仓库使用了更新/更旧的版本,或者父仓库更新后,子仓库尚未同步。
git submodule update
- 1
四、注意事项
- 1.子仓库中无法将代码
push
到gerrit
。(因为子仓库是submodule update
下来的,不是clone
下来的。clone
下来带有Gerrit
认证) - 2.
git submodule update
后,所有的子仓库都处于(no branch
)分支,不要害怕,只要git log
的版本号没问题就行。 - 3.子仓库与父仓库的绑定关系中,子仓库的版本不一定是最新版,也不一定是
master
。 - 4.当父仓库有新提交时,如绑定关系未发生变化,父仓库新版本下,子仓库内容(
git submodule update后)
不变。
五、删除子仓库
当顶级仓库不在需要子仓库代码时,应该及时解除顶级仓库和子仓库的关系。
四个步骤:
(1)删除.gitmodules文件中相关的子模块条目
(2)删除项目中子模块目录
(3)删除.git/modules中子模块文件夹
(4)删除.git/config文件中子模块相关条目
执行过程为:
一、删除子模块
git rm --cache(会保留本地文件,只会删除.gitsubmodel文件中的子模块)
rm -rf 子模块
或者
git rm -rf 子模块(删除掉子模块-是将.gitsubmodule文件中模块删除,并且不会保留本地文件)
二、删除git中对应模块内容
(1)删除配置
vi .git/config (删除对应模块的内容)
(2)删除git中缓存
cd .git/module/
rm 对应模块
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
或者
(1)git submodule deinit 子模块(手动实验了一下,执行这个命令之后.gitmodules文件并不会改变, 只是清空了子模块文件夹,子模块文件夹本身并不会被删除,并且会清空 .git/config文件中子模块相关条目)
(2)删除项目中子模块目录
(3)删除.git/modules中子模块文件夹