子模块:核心概念、工作流程和提示

在 Git 开发中包含子模块允许您在代码库中包含其他项目,从而将它们的历史记录分开,同时与您的历史记录同步。这是解决供应商库和依赖关系问题的便捷方法。像往常一样,所有的 git 方法都以意见为基础,鼓励大家在熟练使用之前先进行一些研究。已经有关于子模块详细信息,所以我不会重提。我在这里要做的是分享一些有趣的事情,这些内容可以帮助您充分利用这个功能。

目录

核心概念

首先,我先简要解释一下子模块的核心概念,这将使它们更易于使用。

子模块由父项目中指定的确切提交进行跟踪,而不是分支、引用或任何其他符号引用。

更新子模块指定的存储库时,它们永远不会自动更新,只有在父项目本身更新时才会自动更新。前面提到的 Pro Git 章节中非常清楚地表达了:

当您在 [submodule] 子目录中进行变更并提交时,超级项目会注意到那里的 HEAD 已变更,并记录了您当前正在处理的确切提交,这样,当其他人克隆这个项目时,他们就可以准确地重新创建环境。

或者换句话说

[...] git 子模块 [...] 是静态的。非常静态。您正在使用 git 子模块跟踪特定的提交——不是分支,不是引用,而是单个提交。如果您向子模块添加提交,父项目不会知道。如果您有一堆模块的拷贝,那么 git 子模块就无所谓了。您有一个远程存储库,您指向一个提交,在更新父项目之前,没有任何变更。

可能的工作流程

通过记住这个核心概念并对其进行反思,您可以理解 submodule 很好地支持某些工作流程,而对其他工作流程的支持则不太理想。至少在三种情况下,子模块是合理的选择:

  • 当组件或子项目变更过快或即将发生的变更将破坏 API 时,为了您自己的安全,您可以将代码锁定到特定提交。

  • 当您有一个不经常更新的组件,而您想将其作为供应商依赖关系进行跟踪时。例如,我为我的 vim 插件这样做。

  • 当您将项目的一部分委托给第三方,并且想要在特定时间或发布时整合他们的工作时。同样,这在更新不太频繁的情况下有效。

详细解释的场景要归功Finch

有用的提示即将奉上

子模块基础架构功能强大,允许对代码库进行有用的分离和集成。但是,有些简单的操作没有简化的程序或强大的命令行用户界面支持。

如果您在项目中使用 git 子模块,您要么已经遇到了这些子模块,要么即将遇到这些子模块。发生这种情况时,您将需要查找解决方案。一次又一次。我来为您节省研究时间:InstapaperEvernote 或采用老式方法将这个页面加入书签 (:D:D),然后您就能安心一段时间了。

这是我为您准备的:

如何用自己的拷贝交换 git 子模块

这是一个非常常见的工作流程:您开始使用别人的项目作为子模块,但过了一会儿您发现需要对其进行自定义并自己进行调整,所以您需要拷贝项目,用自己的拷贝替换子模块。那是怎么做的?

子模块存储在 .gitmodules 中:

$ cat .gitmodules [submodule "ext/google-maps"] path = ext/google-maps url = git://git.naquadah.org/google-maps.git

您可以用文本编辑器编辑网址,然后运行以下命令:

$ git submodule sync

这会更新 .git/config,其中包含这个子模块列表的副本(您也可以手动编辑 .git/config 的相关 [submodule] 部分)。

如何删除子模块?

这是一个相当常见的需求,但程序有些复杂。要删除子模块,您需要:

1. 从 .gitmodules 文件中删除相关行。

2. 从 .git/config 中删除相关部分。

3. 运行 git rm --cached path_to_submodule(结尾处没有斜杠)。

4. 提交并删除现在未被跟踪的子模块文件。

如何将子模块重新集成到我的项目中?

或者,换句话说,如何取消 git 子模块的子模块?如果您只想把您的子模块代码放到主存储库中,只需删除子模块并将文件重新添加到主代码存储库即可:

1. 从索引中删除对子模块的引用,但保留文件:

git rm --cached submodule_path (no trailing slash)

2. 删除 .gitmodules 文件,或者如果您有多个子模块,请编辑此文件,从列表中移除子模块:

git rm .gitmodules

3. 移除 .git 元数据文件夹(确保您有此文件夹的备份):

rm -rf submodule_path/.git

4. 将 submodule 添加到主存储库索引中:

git add submodule_path git commit -m "remove submodule"

注意:上面概述的程序会破坏子模块的历史记录,如果您想保留子模块的一致历史记录,则必须进行花哨的“合并”。欲了解更多详情,请参阅这份非常完整的 Stack Overflow 引用

如何忽略子模块的变更

有时,您的 submodules 可能会自己变为 dirty。例如,如果您使用 Git submodules 来跟踪您的 vim 插件,它们可能会生成或修改 helptags 等本地文件。遗憾的是,git status 会开始让您对这些变更感到烦恼,虽然您对它们根本不感兴趣,也无意提交它们。

解决方案非常简单,打开存储库根目录下的 .gitmodules 文件,对于要忽略的每个子模块,添加 ignore = dirty,如下例所示:

[submodule ".vim/bundle/msanders-snipmate"] path = .vim/bundle/msanders-snipmate url = git://github.com/msanders/snipmate.vim.git ignore = dirty

危险地带!与远程存储库互动的陷阱

正如 kernel.org 上的 Git 子模块教程提醒的那样,在与远程存储库交互时,需要注意一些重要事项。

第一种是始终发布子模块变更,然后再将变更发布到引用它的超级项目。这很关键,因为它可能会阻碍其他人克隆存储库。

第二是始终记住在运行git submodule update 之前提交所有变更,因为如果存在未提交的变更,这些变更将会被覆盖!

总结

有了这些笔记,您应该能够处理使用子模块时出现的许多常见的重复工作流程。

为您推荐

Bitbucket 博客

DevOps 学习路径

了解有关 Git 的更多信息

在此中心查找更多 Git 指南和资源。