Git Submodule详解

1、Git Submodule介绍

软件开发过程中,经常在一个项目中需要引用另外一个项目。可能是需要引用一个第三方库;或者是独立开发的一个项目被几个项目引用;或者是项目分为几个单独的子模块分别开发;

我们在开发WEB应用中, 前端团队和后端团队分开开发,各负其责,所以把资源文件单独生成一个项目由前端团队负责,主项目中引用此资源项目,后端人员负责,后端团队不能修改资源文件。

Git Submodule可以解决这个问题。

  1.1、Git submodule命令

  • git submodule add :创建子模块,将子模块加入主工程。

  • git submodule status :  查看子模块的状态。

  • git submodule init :初始化子模块, 把记录在模块索引文件(.gitmodules)中的子模块名复制到 .git/config.  模块索引文件被提交到git仓库。

  • git submodule deinit  :  把子模块从 .git/config中删掉, 模块索引文件中还存在。

  • git submodule update :此命令用于更新子模块的更新。

  • git submodule summary:  现实HEAD和工作区的区别。

  • git submodule foreach: 在所有的子模块中执行一个shell命令。

  • git submodule sync : 同步子模块的upstream到模块索引文件(.gitmodules)。

下面简单实战一下主要命令用法,每个命令可以添加options来完成不同的操作,请参考帮助,git submodule --help.

2、实战演练

2.1、准备环境

在CSDN CODE中创建三个项目,每个项目中随便添加几个文本文件,作为测试使用如下图所示:

进入 submodule_master 为主项目添加子项目:

查看了当前的状态发现添加了一个新文件(.gitmodules)和两个文件夹(submodule_a,submodule_b):

查看一下模块索引文件.gitmodules文件内容,记录了每个submodule的引用信息,包括在当前项目的位置以及仓库的url。

把更改提交到仓库,再看code上文件的变化,  可以看到,code里面多了2个目录,目录后面是引用地址。

2.2、Clone带有Submodule的仓库

如果一个项目里面含有submodule, 那么Clone此项目和其他项目有何不同呢?我们操作一下:

看到submodules的状态是hash码和文件目录,但是注意前面有一个减号:-,含义是该子模块还没有检出。git submodule init先初始化所有的子模块,然后git submodule update检出所有注册的模块。

可以看到子项目里的文件已全部clone到了本地:

  除了上述方法,还一个命令可以完成该操作:

  git clone --recursive git@code.csdn.net:zu-test/submodule_master.git

  --recursive参数的含义:可以在clone项目时同时clone关联的submodules。

特别要说的是,如果你有多个子模块,一个一个clone比较慢, git 2.8 提供 了一种方法可以并行clone子模块的方式:

git clone --recursive --jobs=4 git@code.csdn.net:zu-test/submodule_master.git 

--job=4参数允许一次同时读取4个submodules,这个功能CODE暂不支持。

2.3、修改Submodule

一个项目中如果引入了submodule, 如何获取此Submodule中的最新内容呢? 下面我们操作一下。 先看一下Submodule里面的状态:

为什么是HEAD detached at呢?不是应该默认在master分支吗?Git对于Submodule有特殊的处理方式,在一个主项目中引入了Submodule其实Git做了3件事情:

  • 记录引用的仓库
  • 记录主项目中Submodules的目录位置
  • 记录引用Submodule的commit id

在submodule_master中push之后其实就是更新了引用的commit id,然后submodule-a 和submodule-b在clone的时候获取到了submodule的commit id,然后当执行git submodule update的时候git就根据gitlink获取submodule的commit id,最后获取submodule的文件,所以clone之后不在任何分支上;

如果修改submodule的git仓需要修改或者有新的提交,则需要先检出master分支, 更新仓库或者修改并提交新的提交。 然后在主项目中提交引用的commit id,就回更新到master分支的最新提交。操作如下:

检出master分支并,修改并提交:

先看主项目中的变化, 可以看到submodulea有变化,变化的内容是引用的commit id 不一样了。由原来的697a70d01e102aafc5641d9ab00aa21877631c33更改为be8d73b2cf7653d18c7c1c8df47c4ca94cc9ecf2

最后把更新的commit id提交到仓库:

一旦主项目中的引用被提交,别的人只需要更新主项目,在执行 git submodule update命令即可得到submodule的更新。

2.4、移除子模块

要注销一个子模块,可以用git submodule deinit命令。但此命令仅仅是把 .git/config中的子模块信息删掉,把子模块目录里面的内容删掉。子模块索引文件和子模块对应的目录都没有删除。 如果想要彻底删除子模块,需要自己动手:

  1. 删除git rm --cache物理文件夹
  2. 删除.gitmodules的内容(或者整个文件)。如果所有的子模块被删除了, 直接删除文件。如果仅仅删除某一个submodule那么打开.gitmodules文件编辑,删除对应submodule配置即可。
  3. 删除.git/config的submodule配置 源文件:

删除后

2.5、使用第三方工具

对于submodule的重度使用者,有几个工具可作推荐:

1.Repo Google用于管理Android项目的工具。

2.Gitslave

3.Git Subtree

3、总结

submoudle解决了git不能提供目录级别的权限控制问题。 如果在开发过程中,项目被分为几个子模块分团队开发,并且要严格区分各团队的权限,则使用submodule是个很好的选择。 使用submodule时, 当子模块有新的更新后,引用的项目需要先切换到master分支, 更新最新的子模块代码,然后提交引用项目中的子模块引用的commit id。 否则,使用 git submodule update 总是把子模块更新到上次引用项目中纪录的commit id. 

总之,submodule在分模块管理权限的时候,还是非常有用的。