GitSubmodule详解

摘要

有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目。 也许是第三方库,或者你独立开发的,用于多个父项目的库。 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使用另一个。

常用命令

1
2
3
4
5
$ git clone <repository> --recursive #递归的方式克隆整个项目
$ git submodule add <repository> <path> #添加子模块
$ git submodule init #初始化子模块
$ git submodule update #更新子模块
$ git submodule foreach git pull #拉取所有子模块

用法

引入Git

1
2
3
#现有项目引入Git
$ git init
Initialized empty Git repository in GitSub/.git/

添加子模块

1
2
3
4
5
6
7
8
#通过url添加子模块,默认情况下,子模块会将子项目放到一个与仓库同名的目录中
$ git submodule add <url> [path]
Cloning into '<path>'...
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.

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

1
2
3
4
5
6
7
8
9
10
$ 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: <dir>
#.gitmodules保存了仙姑url与已经拉取的本地目录<dir>之间的关系

子模块操作

在子模块更新的内容并不需要 push到该模块的维护仓库,而是需要为它单独建立一个仓库进行维护。实际上你在子模块中的提交是提交到子模块的目标仓库,而在主项目的提交时记录的你在子模块提交的commit id。你所要做的就是在子模块下commit你的内容,并push后,到项目根目录下再进行commit。如果你能理解Git维护项目的秘诀在于记录更新的操作而不是具体什么文件,就能更好的理解这些操作。

当你不在子模块目录中时,Git 并不会跟踪它的内容, 而是将它看作子模块仓库中的某个具体的提交。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#子模块内容更新后
$ cd <module dir>
$ git commit -am <msg>

#转到根目录提交,可以看到子模块的提交时作为new commits更新
$ cd ..
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: aiss-spider (new commits)
$ git commit -am "update submodule"
[master (root-commit) 89b9dc9] u

#同时确保我们有子模块远程仓库的权限,如果我们使用的是他人的项目作为子模块,
#那么我们就需要进行fork该仓库,以取得权限。
$ git remote set-url origin <our submodule repo url>
#同时也可以保留原项目仓库地址,一边拉他人更新取子模块的内容
$ git remote add <shortname> <origin repo url>

子模块的错误操作示范

1
2
3
4
5
6
#在子模块更新内容后在子模块目录下进行commit
$ cd <module dir>
$ git commit -am <msg>
[master ceba38b] update submodule
1 file changed, 29 deletions(-)
delete mode 100644 README.md

commit子模块更新时

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
# 若是未在子模块提交更新的话,在项目根目录进行status就会显示子模块有更新
$ cd ..
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
(commit or discard the untracked or modified content in submodules)
modified: <module dir> (modified content)
no changes added to commit (use "git add" and/or "git commit -a")

#在模块目录进行status就才能看到具体更新内容
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: README.MD

#这个时候你在项目根目录提交就不会成功,因为你子项目内容未提交
$ git commit -am "update module"
On branch master
Changes not staged for commit:
modified: pod-library (modified content)

no changes added to commit

子模块推送错误

内容提交后,如果你还按以往的操作在子模块目录下推送,这时如果你进行推送的话,更新内容是推送到子模块的维护仓库,如果没有权限就会报错。这种情况我们就需要将子模块推送到我们自己的仓库。

1
2
3
4
#在子模块目录下未推送更新远程仓库地址情况下进行推送的情形
$ git push
remote: Permission to x-spiders/aiss-spider.git denied to <username>.
fatal: unable to access '<remote-url>': The requested URL returned error: 403

克隆含有子模块的项目

一般操作

当你在克隆这样的项目时,默认会包含该子模块目录,但其中还没有任何文件:

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
$

这这是需要执行下面两个命令,

1
2
3
4
5
6
7
#初始化本地submodule配置文件
$ git submodule init
Submodule '<module name>' <url> registered for path <module dir>
#从该项目中抓取所有数据并检出父项目中列出的合适的提交
$ git submodule update
Cloning into '<module dir>'...
Submodule path '<module name>': checked out '0d8ba0ebc9b8532e7aebeb3ebc8f0e0efa3328ae'

简单操作

1
2
#直接递归clone模块
$ git clone --recursive <url> [dir]

参考

[1] Git官方文档https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97

[2] Boblim的博客https://www.cnblogs.com/fnlingnzb-learner/p/10396283.html