git子模块

1 简介

为什么需要子模块?:某个工作中的项目需要包含并使用另一个项目。 也许是第三方库,或者你独立开发的,用于多个父项目的库。 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使用另一个。

Git 子模块是一个独立的 Git 仓库,被嵌套在另一个 Git 仓库中。它允许在一个存储库中引用另一个存储库,并且可以使项目更加模块化、易于维护。子模块允许你将一个Git 仓库作为另一个 Git 仓库的子目录。 它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。

2 开始使用子模块

2.1 添加子模块

我们首先将一个已存在的 Git 仓库添加为正在工作的仓库的子模块,你可以通过在git submodule add后面加上想要跟踪的项目的相对或绝对URL来添加新的子模块。在本例中,我们将会添加一个名为DbConnector 的库。

1
2
3
4
5
6
7
$git submodule add https: //github.com/chaconinc/DbConnector 
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.

默认情况下,子模块会将子项目放到一个与仓库同名的目录中,本例中是 DbConnector。 如果你想要放到其他地方,那么可以在命令结尾添加一个不同的路径。

如果这时运行git status,你会注意到几件事。

1
2
3
4
5
6
7
$git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
  new file: .gitmodules
  new file: DbConnector

首先应当注意到新的.gitmodules 文件。 该配置文件保存了项目 URL 与已经拉取的本地目录之间的映射:

1
2
3
[submodule "DbConnector"]
  path = DbConnector
  url = https://github.com/chaconinc/DbConnector

如果有多个子模块,该文件中就会有多条记录。 要重点注意的是,该文件也像.gitignore 文件一样受到(通过)版本控制。 它会和该项目的其他部分一同被拉取推送。 这就是克隆该项目的人知道去哪获得子模块的原因。

你也可以根据自己的需要,通过在本地执行 git config submodule.DbConnector.url <私有URL> 来覆盖这个选项的值。

在 git status 输出中列出的另一个是项目文件夹记录。 如果你运行git diff,会看到类似下面的信息

虽然 DbConnector 是工作目录中的一个子目录,但 Git 还是会将它视作一个子模块。当你不在那个目录中时,Git 并不会跟踪它的内容, 而是将它看作子模块仓库中的某个具体的提交。
如果你想看到更漂亮的差异输出,可以给 git diff 传递 —submodule 选项。

当你提交时,会看到类似下面的信息:

1
2
3
4
5
$ git commit -am 'added DbConnector module'
[master fb9093c] added DbConnector module
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 DbConnector

注意 DbConnector 记录的 160000 模式。 这是 Git 中的一种特殊模式,它本质上意味着你是将一次提交记作一项目录记录的,而非将它记录成一个子目录或者一个文件。

最后,推送这些更改:

1
$ git push origin master

2.2 克隆含有子模块的项目

接下来我们将会克隆一个含有子模块的项目。 当你在克隆这样的项目时,默认会包含该子模块目录,但其中还没有任何文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ git clone https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
$ cd MainProject
$ ls -la
total 16
drwxr-xr-x 9 schacon staff 306 Sep 17 15:21 .
drwxr-xr-x 7 schacon staff 238 Sep 17 15:21 ..
drwxr-xr-x 13 schacon staff 442 Sep 17 15:21 .git
-rw-r--r-- 1 schacon staff 92 Sep 17 15:21 .gitmodules
drwxr-xr-x 2 schacon staff 68 Sep 17 15:21 DbConnector
-rw-r--r-- 1 schacon staff 756 Sep 17 15:21 Makefile
drwxr-xr-x 3 schacon staff 102 Sep 17 15:21 includes
drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 scripts
drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 src
$ cd DbConnector/
$ ls
$

其中有 DbConnector 目录,不过是空的。 你必须运行两个命令:git submodule init 用来初始化本地配置文件,而git submodule update则从该项目中抓取所有数据并检出父项目中列出的合适的提交。

1
2
3
4
5
6
7
8
9
10
11
12
$ git submodule init
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector)
registered for path 'DbConnector'
$ git submodule update
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out
'c3f01dc8862123d317dd46284b05b6892c7b29bc'

现在 DbConnector 子目录是处在和之前提交时相同的状态了。

不过还有更简单一点的方式。 如果给 git clone 命令传递 —recurse-submodules 选项,它就会自动初始化并更新仓库中的每一个子模块, 包括可能存在的嵌套子模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git clone --recurse-submodules https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector)
registered for path 'DbConnector'
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out
'c3f01dc8862123d317dd46284b05b6892c7b29bc'

如果你已经克隆了项目但忘记了--recurse-submodules,那么可以运行git submodule update --initgit submodule initgit submodule update 合并成一步。如果还要初始化、抓取并检出
任何嵌套的子模块, 请使用简明的git submodule update --init --recursive

2.3 在包含子模块的项目上工作