如何使用
$ git submodule add [url] [path]
# like
$ git submodule add https://gitee.com/good_man_yikang/DataStructure-and-Algorithm
Cloning into 'DataStructure-and-Algorithm'...
remote: Enumerating objects: 304, done.
remote: Counting objects: 100% (304/304), done.
remote: Compressing objects: 100% (287/287), done.
remote: Total 304 (delta 91), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (304/304), 507.40 KiB | 317.00 KiB/s, done.
Resolving deltas: 100% (91/91), done.
上述命令就是以子模块的形式添加远程仓库 DataStructure-and-Algorithm
到你的项目中。默认情况下,他会放到一个与仓库同名的目录中,你也可以指定 path
来放到其他路径。
运行 git status
可以看到:
$ git status
On branch add-crypto
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .gitmodules
new file: DataStructure-and-Algorithm
注意,他会创建一个 .gitmodules
文件,该配置文件保存了项目 URL 与已经拉取的本地目录之间的映射:
[submodule "DataStructure-and-Algorithm"]
path = DataStructure-and-Algorithm
url = https://gitee.com/good_man_yikang/DataStructure-and-Algorithm
可以根据 git diff
查看差异,传递 --submodule
可以看到更漂亮的差异输出。设置 diff.submodule
为 log
来使,git diff –submodule
为默认选项
git config diff.submodule log
提交可以看到下面输出.
$ git commit -am 'add data module'
[add-crypto a379132] add data module
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 DataStructure-and-Algorithm
这里的 mode 160000
是 Git 的一种特殊模式,本质上意味着你是将一次提交记作一项目录记录的,而非将它记录成一个子目录或者一个文件。
克隆含有子模块的项目
直接克隆即可,会包含子模块目录,但是没有任何文件。
可以使用 git submodule init
来初始化本地配置文件, git submodule update
从子模块对应的远程仓库中拉取代码。
如果想一步完成,git clone
的时候,加上 --recurse-submodules
选项即可。
上述 git submodule init
和 git submodule update
两个命令可以由 it submodule update --init
组成,如果想要更新嵌套的子模块,使用 git submodule update --init --recursive
。
更新远程代码
git submodule update --remote [submodule]
来从远程拉取代码,不加 submodule
会克隆所有子模块,包括嵌套的子模块。
上述默认是更新并检出子模块仓库的 master
分支,如果你想要其他分支,可以在 .gitmodules
文件中设置:
$ git config -f .gitmodules submodule.DataStructure-and-Algorithm.branch stable
上述代码就将远程库中的 stable
分支与子模块进行了关联,就可以通过 git submodule update --remote
来拉取对应的代码了。
如果不加 config -f
选项,那就是在 本地的 .git/config
文件中修改,与 .gitmodules
文件不同的是,.git/config
不会追踪到版本控制中,也就是别人拉取不到该代码
拉取到新的代码,可以通过 git status
来查看状态:
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
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: gaokao (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
可以配置 status.submodulessummary
来详细查看更新
$ git config status.submodulesummary 1
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
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: gaokao (new commits)
Submodules changed but not updated:
* gaokao 525f5e8...1128ad0 (1):
> delete public/index annotate
no changes added to commit (use "git add" and/or "git commit -a")
当然,我们也可以用 git pull
来拉取更新,但是 git pull
只会拉取最新的代码,不会 更新 子模块,这时,你可以通过运行 git submodule update --init --recursive
来更新代码。
如果你想自动化此过程,可以为 git pull
添加 –recurse-submodules
的选项,他会在拉取代码之后,自动运行 git submodule update
。也可以修改配置选项 submodule.recurse
设置为 true
,会让你所有支持 –recurse-submodules
的命令使用该选项(出 clone
外)
在你拉取的提交中, 可能 .gitmodules
文件中记录的子模块的 URL 发生了改变。此时,若父级项目引用的子模块提交不在仓库中本地配置的子模块远端上,那么执行 git pull --recurse-submodules
或 git submodule update
就会失败。此时可以使用 git submodule sync
:
# 将新的 URL 复制到本地配置中
$ git submodule sync --recursive
在子模块上工作
当我们运行 git submodule update
从子模块仓库中抓取修改时, Git 将会获得这些改动并更新子目录中的文件,但是会将子仓库留在一个称作 游离的 HEAD 的状态。为了将子模块设置得更容易进入并修改,你需要做两件事。 首先,进入每个子模块并检出其相应的工作分支。 接着,若你做了更改就需要告诉 Git 它该做什么,然后运行 git submodule update --remote
来从上游拉取新工作。 你可以选择将它们合并到你的本地工作中,也可以尝试将你的工作变基到新的更改上。
直接在 update
中添加 --merge
即可拉取代码,并自动合并
$ git submodule update --remote --merge
同理,添加 --rebase
即可拉取代码,并自动变基
发送改动
再推送的同时,可以指定 --recurse-submodules
参数中的 check
选项,来检查是否可以直接推送
$ git push --recurse-submodules=check
可以设置 git config push.recurseSubmodules check
让它成为默认行为。
另一个选项是 on-demand
他会先推送子模块中的提前代码,最后推送本地代码。同样可以设置 git config push.recurseSubmodules on-demand
让它成为默认行为。
合并子模块的改动
如果你和其他人同时改动了一个子模块的引用,并造成了分叉。这时就会比较麻烦。Git 不会尝试去进行一次简单的合并。 如果子模块提交已经分叉且需要合并,那你会得到类似下面的信息:
$ git pull
remote: Counting objects: 2, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 2 (delta 1), reused 2 (delta 1)
Unpacking objects: 100% (2/2), done.
From https://github.com/chaconinc/MainProject
9a377d1..eb974f8 master -> origin/master
Fetching submodule DbConnector
warning: Failed to merge submodule DbConnector (merge following commits not found)
Auto-merging DbConnector
CONFLICT (submodule): Merge conflict in DbConnector
Automatic merge failed; fix conflicts and then commit the result.
可以看到 ,他指出了 merge following commits not found(未找到接下来需要合并的提交)。
可以使用 git diff
来查看 试图合并 的两个分支中记录的提交 SHA-1 值
$ git diff
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
在上述中,eb41d76
是我们自己的提交,c771610
是上游的提交。可以执行检出这两个提交,进行手动合并,在提交该合并即可。
子模块的技巧
遍历
有一个 foreach
子模块命令,它能在每一个子模块中运行任意命令。如 git submodule foreach 'git stash'
他会在每个子模块中执行 git status
并显示出来。
也可以通过该命令来主项目与所有子项目的改动的差异:git diff; git submodule foreach 'git diff'
。
切换分支
如果直接切换分支,会直接将本地的文件直接切换过去,会造成麻烦,如果加上 --recurse-submodules
的话,git checkout
不仅会切换分支,并会让该分支的子模块处于正确状态上。
如果觉得每次写 --recurse-submodules
麻烦,可以通过 git config submodule.recurse true
来概述 Git 总是使用它。