Git子模块

Git 子模块(Git submodules)允许你将 git repo 保留为另一个 git repo 的子目录。Git 子模块只是在特定时间快照上对另一个 repo 的引用。Git 子模块使 Git repo 能够合并和跟踪外部代码的版本历史。

添加子模块

通过下列命令,可以将在一个已存在的 Git 仓库添加为当前工作的仓库的子模块:

1
$ git submodule add <repo url> [submodule path]

例如,在一个 Hexo 博客仓库下添加一个主题仓库 NexT:

1
$ git submodule add https://github.com/wylu/hexo-theme-next themes/next

默认情况下,如果没有指定子模块存放路径,子模块将会放到一个与仓库同名的目录中。如果你想要放到其他地方,那么可以在命令结尾添加一个不同的路径,本例中子模块将会 clone 到 "themes/next" 目录下。

命令执行完成后,会在当前工作仓库根目录下生成 .gitmodules 文件,内容如下:

1
2
3
[submodule "themes/next"]
path = themes/next
url = https://github.com/wylu/hexo-theme-next

该文件保存了项目 URL 与已经拉取的本地目录之间的映射,如果有多个子模块,该文件中就会有多条记录。要重点注意的是,该文件应像 .gitignore 文件一样受到(通过)版本控制,和该项目的其他部分一同被拉取推送。有了映射关系,克隆该项目的人就知道去哪获得子模块了。

添加子模块完成后,当在父仓库时,Git 仍然不会跟踪 submodule 的文件, 而是将它看作该仓库中的一个特殊提交。

推送到远程仓库后,远程仓库中 submodule 会和指定的 commit 关联起来。如果需要指定分支,可以在 ".gitmodules" 文件中加上 branch 配置,如 branch = develop

克隆含有子模块的项目

接下来我们将会克隆(clone)一个含有子模块的项目。 当你在克隆这样的项目时,默认会包含该子模块目录,但其中还没有任何文件,你需要执行两个命令以拉取子模块:

1
2
$ git submodule init
$ git submodule update

git submodule init 用来初始化本地配置文件,而 git submodule update 则从子项目中抓取所有数据并检出父项目中列出的合适的提交。

或者:

1
git clone --recursive <parent repo url>

删除子模块

  1. 把子模块从版本控制系统中移除

    1
    $ git rm --cached <submodule path>
  2. 删除子模块目录

    1
    $ rm -rf <submodule path>
  3. 编辑 ".gitmodules",移除相应 submodule 节点内容

  4. 编辑 ".git/config",移除相应 submodule 配置

  5. 如果有 ".git/modules" 目录,还应删除其下的相应子模块的目录

以上面的 NexT 子模块为例:

1
2
$ git rm --cached themes/next
$ rm -rf themes/next

然后删除 ".gitmodules" 中如下内容:

1
2
3
[submodule "themes/next"]
path = themes/next
url = https://github.com/wylu/hexo-theme-next

最后删除 ".git/config" 中如下内容:

1
2
3
[submodule "themes/next"]
url = https://github.com/wylu/hexo-theme-next
active = true

要把此次修改同步到远程库,还需要 push 一下。

References

https://git-scm.com/book/en/v2/Git-Tools-Submodules