如何使用

  1. $ git submodule add [url] [path]
  2. # like
  3. $ git submodule add https://gitee.com/good_man_yikang/DataStructure-and-Algorithm
  4. Cloning into 'DataStructure-and-Algorithm'...
  5. remote: Enumerating objects: 304, done.
  6. remote: Counting objects: 100% (304/304), done.
  7. remote: Compressing objects: 100% (287/287), done.
  8. remote: Total 304 (delta 91), reused 0 (delta 0), pack-reused 0
  9. Receiving objects: 100% (304/304), 507.40 KiB | 317.00 KiB/s, done.
  10. Resolving deltas: 100% (91/91), done.

上述命令就是以子模块的形式添加远程仓库 DataStructure-and-Algorithm 到你的项目中。默认情况下,他会放到一个与仓库同名的目录中,你也可以指定 path 来放到其他路径。
运行 git status 可以看到:

  1. $ git status
  2. On branch add-crypto
  3. Changes to be committed:
  4. (use "git restore --staged <file>..." to unstage)
  5. new file: .gitmodules
  6. new file: DataStructure-and-Algorithm

注意,他会创建一个 .gitmodules 文件,该配置文件保存了项目 URL 与已经拉取的本地目录之间的映射:

  1. [submodule "DataStructure-and-Algorithm"]
  2. path = DataStructure-and-Algorithm
  3. url = https://gitee.com/good_man_yikang/DataStructure-and-Algorithm

可以根据 git diff 查看差异,传递 --submodule 可以看到更漂亮的差异输出。设置 diff.submodulelog 来使,git diff –submodule 为默认选项

  1. git config diff.submodule log

提交可以看到下面输出.

  1. $ git commit -am 'add data module'
  2. [add-crypto a379132] add data module
  3. 2 files changed, 4 insertions(+)
  4. create mode 100644 .gitmodules
  5. create mode 160000 DataStructure-and-Algorithm

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

克隆含有子模块的项目

直接克隆即可,会包含子模块目录,但是没有任何文件。
可以使用 git submodule init 来初始化本地配置文件, git submodule update 从子模块对应的远程仓库中拉取代码。
如果想一步完成,git clone 的时候,加上 --recurse-submodules 选项即可。
上述 git submodule initgit submodule update 两个命令可以由 it submodule update --init 组成,如果想要更新嵌套的子模块,使用 git submodule update --init --recursive

更新远程代码

git submodule update --remote [submodule] 来从远程拉取代码,不加 submodule 会克隆所有子模块,包括嵌套的子模块。
上述默认是更新并检出子模块仓库的 master 分支,如果你想要其他分支,可以在 .gitmodules 文件中设置:

  1. $ git config -f .gitmodules submodule.DataStructure-and-Algorithm.branch stable

上述代码就将远程库中的 stable 分支与子模块进行了关联,就可以通过 git submodule update --remote 来拉取对应的代码了。
如果不加 config -f 选项,那就是在 本地的 .git/config 文件中修改,与 .gitmodules 文件不同的是,.git/config 不会追踪到版本控制中,也就是别人拉取不到该代码
拉取到新的代码,可以通过 git status 来查看状态:

  1. $ git status
  2. On branch master
  3. Your branch is ahead of 'origin/master' by 1 commit.
  4. (use "git push" to publish your local commits)
  5. Changes not staged for commit:
  6. (use "git add <file>..." to update what will be committed)
  7. (use "git restore <file>..." to discard changes in working directory)
  8. modified: gaokao (new commits)
  9. no changes added to commit (use "git add" and/or "git commit -a")

可以配置 status.submodulessummary 来详细查看更新

  1. $ git config status.submodulesummary 1
  2. $ git status
  3. On branch master
  4. Your branch is ahead of 'origin/master' by 1 commit.
  5. (use "git push" to publish your local commits)
  6. Changes not staged for commit:
  7. (use "git add <file>..." to update what will be committed)
  8. (use "git restore <file>..." to discard changes in working directory)
  9. modified: gaokao (new commits)
  10. Submodules changed but not updated:
  11. * gaokao 525f5e8...1128ad0 (1):
  12. > delete public/index annotate
  13. 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-submodulesgit submodule update 就会失败。此时可以使用 git submodule sync

  1. # 将新的 URL 复制到本地配置中
  2. $ git submodule sync --recursive

在子模块上工作

当我们运行 git submodule update 从子模块仓库中抓取修改时, Git 将会获得这些改动并更新子目录中的文件,但是会将子仓库留在一个称作 游离的 HEAD 的状态。为了将子模块设置得更容易进入并修改,你需要做两件事。 首先,进入每个子模块并检出其相应的工作分支。 接着,若你做了更改就需要告诉 Git 它该做什么,然后运行 git submodule update --remote 来从上游拉取新工作。 你可以选择将它们合并到你的本地工作中,也可以尝试将你的工作变基到新的更改上。
直接在 update 中添加 --merge 即可拉取代码,并自动合并

  1. $ git submodule update --remote --merge

同理,添加 --rebase 即可拉取代码,并自动变基

发送改动

再推送的同时,可以指定 --recurse-submodules 参数中的 check 选项,来检查是否可以直接推送

  1. $ git push --recurse-submodules=check

可以设置 git config push.recurseSubmodules check 让它成为默认行为。
另一个选项是 on-demand 他会先推送子模块中的提前代码,最后推送本地代码。同样可以设置 git config push.recurseSubmodules on-demand 让它成为默认行为。

合并子模块的改动

如果你和其他人同时改动了一个子模块的引用,并造成了分叉。这时就会比较麻烦。Git 不会尝试去进行一次简单的合并。 如果子模块提交已经分叉且需要合并,那你会得到类似下面的信息:

  1. $ git pull
  2. remote: Counting objects: 2, done.
  3. remote: Compressing objects: 100% (1/1), done.
  4. remote: Total 2 (delta 1), reused 2 (delta 1)
  5. Unpacking objects: 100% (2/2), done.
  6. From https://github.com/chaconinc/MainProject
  7. 9a377d1..eb974f8 master -> origin/master
  8. Fetching submodule DbConnector
  9. warning: Failed to merge submodule DbConnector (merge following commits not found)
  10. Auto-merging DbConnector
  11. CONFLICT (submodule): Merge conflict in DbConnector
  12. Automatic merge failed; fix conflicts and then commit the result.

可以看到 ,他指出了 merge following commits not found(未找到接下来需要合并的提交)。
可以使用 git diff 来查看 试图合并 的两个分支中记录的提交 SHA-1 值

  1. $ git diff
  2. diff --cc DbConnector
  3. index eb41d76,c771610..0000000
  4. --- a/DbConnector
  5. +++ 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 总是使用它。