# 添加子模块 [1]

命令格式:

git submodule add <仓库地址> <本地路径>

以我博客主题 shokaFork 版本为例

仓库地址 可以是 https 或者 ssh 链接:

https://github.com/llxlr/hexo-theme-shoka.git
git@github.com:llxlr/hexo-theme-shoka.git

我实际用到的命令:

blog
git submodule add -b dev --name shoka git@github.com:llxlr/hexo-theme-shoka.git themes/shoka
Cloning into 'C:/Users/i/Desktop/blog/themes/shoka'...
remote: Enumerating objects: 2278, done.
remote: Counting objects: 100% (239/239), done.
remote: Compressing objects: 100% (160/160), done.
remote: Total 2278 (delta 100), reused 153 (delta 68), pack-reused 2039Receiving objects:  96% (2187/2278), 3.Receiving objects:  98% (2233/2278), 3.60 MiB | 2.17 MiB/s
Receiving objects: 100% (2278/2278), 3.66 MiB | 2.19 MiB/s, done.
Resolving deltas: 100% (1232/1232), done.
warning: LF will be replaced by CRLF in .gitmodules.
The file will have its original line endings in your working directory

子模块数据包含在以下目录结构:

blog
tree .
.
├── .git
│    ├── config
│    └── modules
│         └── shoka
├── .gitmodules
└── themes
     └── shoka

以下为子模块信息( .git/config 文件亦包含 .gitmodules 同样的内容)

.gitmodules
[submodule "shoka"]
	path = themes/shoka
	url = git@github.com:llxlr/hexo-theme-shoka.git
	branch = dev

通过 -b 选项指定了 dev 分支(复制于 master 分支), --name 选项指定了子模块名称为 shoka

也可以使用以下命令指定分支:

git config -f .gitmodules submodule.shoka.branch dev
git submodule update --remote

#检出(checkout)

克隆一个包含子模块的仓库,并不会 clone 子模块的文件,只是会克隆 .gitmodule 描述文件,需要进一步克隆子模块文件。

git submodule update --init --recursive # 能同时拉取子模块 / 嵌套的子模块
git submodule init # 初始化本地配置文件
git submodule update # 检出父仓库列出的 commit

# 提交子模块

提交变更信息:

blog
git commit -am 'add themes/shoka module'
[master (root-commit) e56f4c4] add themes/shoka module
 2 files changed, 5 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 themes/shoka

推送到远端:

blog
git push origin master

# 更新子模块

如果远端有子模块的新提交,需要本地更新数据:

blog
cd themes/shoka
git pull
cd ../../

# 删除子模块

  • 删除 .gitsubmodule 文件里相关部分
  • 删除 .git/config 文件里相关部分
  • 删除子模块目录
    blog
    git rm --cached <本地路径>

若未按照上述步骤删除子模块,在 .git/modudles 文件夹内可能有残留。

# 变更子模块平台 [2]

当子模块项目改变了它的托管平台,比如从 GitHub 迁移到公司 GitLab/Gitea/Gogs 服务器。

git submodule sync --recursive # 将新的 URL 复制到本地配置中
git submodule update --init --recursive # 从新 URL 更新子模块

# 在子模块中工作

比如从本地子模块检出一个分支 stable

blog
cd themes/shoka
git checkout stable
Switched to branch 'stable'

尝试用 --merge 选项来更新子模块。这时将会看到服务器上的这个子模块有一个改动并且它被合并了进来。

blog
git submodule update --remote --merge

当我们对子模块做一些本地的改动而同时其他人推送另外一个修改到上游时,在此时更新子模块,就会看到我们在本地做了更改时上游也有一个改动,需要将它并入本地。

blog
git submodule update --remote --rebase

若忘记 --rebase--merge ,Git 会将子模块更新为服务器上的状态,并且会将项目重置为一个游离的 HEAD 状态。

git submodule update --remote

# 子模块技巧

# 遍历

若开始开发一项新功能或者修复一些错误,并且需要在几个子模块内工作。

  • 我们可以轻松地保存所有子模块的工作进度。

    git submodule foreach 'git stash'
  • 能够生成一个主项目与所有子项目的改动的统一差异

    git diff; git submodule foreach 'git diff'

# 别名

为命令设置别名,因为它们非常长而又不能设置选项作为它们的默认选项。

若计划大量使用子模块,这样当你想要更新子模块时可以简单地运行 git supdate ,或 git spush 检查子模块依赖后推送。

git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
git config alias.spush 'push --recurse-submodules=on-demand'
git config alias.supdate 'submodule update --remote --merge'

# 在其他电脑上维护博客

此时当前博客仓库内包含着 shoka 子模块,需要指定 --recurse-submodules 选项,以便在克隆项目的同时递归拉取子模块:

git clone --recurse-submodules git@github.com:llxlr/blog.git

若未指定 --recurse-submodules 选项,参考 检出

# 参考资料


  1. Git submodule 实战 ↩︎

  2. Git 工具 - 子模块 ↩︎