如何使用
$ git submodule add [url] [path]# like$ git submodule add https://gitee.com/good_man_yikang/DataStructure-and-AlgorithmCloning 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 0Receiving 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 statusOn branch add-cryptoChanges to be committed:(use "git restore --staged <file>..." to unstage)new file: .gitmodulesnew file: DataStructure-and-Algorithm
注意,他会创建一个 .gitmodules 文件,该配置文件保存了项目 URL 与已经拉取的本地目录之间的映射:
[submodule "DataStructure-and-Algorithm"]path = DataStructure-and-Algorithmurl = 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 module2 files changed, 4 insertions(+)create mode 100644 .gitmodulescreate 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 statusOn branch masterYour 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 statusOn branch masterYour 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 annotateno 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 pullremote: 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/MainProject9a377d1..eb974f8 master -> origin/masterFetching submodule DbConnectorwarning: Failed to merge submodule DbConnector (merge following commits not found)Auto-merging DbConnectorCONFLICT (submodule): Merge conflict in DbConnectorAutomatic merge failed; fix conflicts and then commit the result.
可以看到 ,他指出了 merge following commits not found(未找到接下来需要合并的提交)。
可以使用 git diff 来查看 试图合并 的两个分支中记录的提交 SHA-1 值
$ git diffdiff --cc DbConnectorindex 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 总是使用它。
