Close

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

Nicola Paolucci 头像
Nicola Paolucci

开发人员推广人员


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

数据库
相关资料

如何移动完整的 Git 存储库

Bitbucket 徽标
查看解决方案

了解 Bitbucket Cloud 的 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] 部分)。

Stack Overflow 引用

如何删除子模块?


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

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

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

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

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

Stack Overflow 引用

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


或者,换句话说,如何取消 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 之前提交所有变更,就好像有变更会被覆盖一样!

总结


有了这些笔记,您应该能够处理使用子模块时出现的许多常见的重复工作流程。在以后的文章中,我将介绍 git submodule 的替代方案。

关注我 @durdn 和很棒的 @Bitbucket 团队,了解更多 DVCS 优势。

Nicola Paolucci

Nicola is an all-round hacker who loves exploring and teaching bleeding edge technologies. He writes and talks about Git, development workflows, code collaboration and more recently about Docker. Prior to his current role as Developer Instigator at Atlassian he led software teams, built crowd sourcing applications for geo-spacial data, worked on huge e-commerce deployments. Little known facts about Nicola: he gesticulates a lot while speaking (being Italian), lives in Amsterdam and rides a Ducati.


分享此文章
下一主题

推荐阅读

将这些资源加入书签,以了解 DevOps 团队的类型,或获取 Atlassian 关于 DevOps 的持续更新。

人们通过满是工具的墙进行协作

Bitbucket 博客

Devops 示意图

DevOps 学习路径

与 Atlassian 专家一起进行 Den 功能演示

Bitbucket Cloud 与 Atlassian Open DevOps 如何协同工作

注册以获取我们的 DevOps 新闻资讯

Thank you for signing up