Git 子模块具有一定的应用场景。
比如在一个项目 P 中,该项目被分为两部分,由 A 和 B 两个团队并行开发。而 B 团队开发的内容属于公共支持类的项目,不止会被该项目使用,其他项目也可能会使用。因此 B 团队开发的内容不可能和团队 A 共用一个仓库。因为这样的话别人拉取该公共内容时会连带拉取不需要的内容。而 A 团队项目的开发又依赖于 B 团队所做的内容,并希望能随时获得团队 B 得最新成果。
此时使用 Git 的子模块功能能较好的解决该问题。
Git 得子模块功能允许把一个仓库作为另一个仓库得子目录。
概述
子模块常用得命令很简单,该文章完全学习于 Git 的官方文档《Git 工具 - 子模块》。本文对官方文档进行了提炼。
常用命令
添加一个子模块
1 |
|
此时会在根目录下创建 .gitmoudules
文件,该文件保存了子模块的相关配置。如下:
1 |
|
添加完毕后,即拥有了一个子模块。可以在子模块进行代码的修改和提交和拉取等操作。
需要注意的是,当在上层仓库执行 commit
操作时,该仓库会记录子模块的提交记录,但也仅限于此。上层仓库并不会跟踪子模块里的文件。
所有的 git 命令,在两个仓库里独立工作。
克隆一个带子模块的项目
git clone <url>
将上层仓库克隆下来,子模块仅仅被拉取了一个文件夹,里面没有任何内容。接下来要执行两个命令:
git submodule init
初始化本地配置文件
git submodule update
拉取子目录所有数据,并检出上层仓库里所列的提交。
之后每次需要更新子模块仓库时都需运行 git submodules
update
。子模块并不会随上层目录的更新而一起更新。
需要注意的问题
上层仓库只保持对子模块的一个记录,而不会跟踪内部文件。
上层仓库(后简称 A 仓库)只会保留子模块(后简称 B 仓库)的一个提交记录(即 HEAD 指针值)。所以当在 A 仓库拉取更新时,B 仓库的内容并不会得到更新,而是只是更新了 B 仓库的 HEAD。当执行
git submodule update
命令时,B 仓库才会从远程仓库拉取所有数据,并根据 A 仓库中对 B 仓库的 HEAD 记录检出需要的文件内容。子模块拉取失败的原因
有上文可知,A 仓库仅仅记录一个 HEAD 值。所以当一个开发者在本地的 B 仓库里做了代码的修改,并提交,但未推送到远程仓库。他随后在 A 仓库中进行了提交和推送,此时 A 仓库会记录 B 仓库在该开发者本地的一个 HEAD。当其他开发者尝试通过
git submodule update
更新 B 仓库时,会发生错误,因为此时 B 仓库的 HEAD 所指向的提交只存在于第一个开发者的本地。此时,你只能查看是谁提交了本次更改,然后联系他,教育他一下。
在子模块里工作要格外小心
因为在上层仓库里进行
git submodule update
时会将子模块检出至一个游离的 HEAD,这可能导致你在子目录里提交但未在上层仓库提交的工作丢失。这种情况仍可以使用git reflog
命令去查找出那个游离的 HEAD。但仍要避免这种失误出现。