如果您从事开源开发,您可能已经使用 Git 来管理源代码。您可能遇到过具有众多依赖项和/或子项目的项目。您如何管理它们呢?
对于开源组织而言,为社区和产品实现单一来源的文档和依赖项管理可能很棘手。文档和项目经常最终变得碎片化和冗余,这使得它们难以维护。
需求
假设您想在一个存储库中使用单个项目作为子项目。传统方法只是将项目复制到父存储库。但是,如果您想在多个父存储库中使用同一个子项目怎么办?将子项目复制到每个父项目中并在每次更新时都必须在所有父项目中进行更改是不可行的。这将会在父存储库中造成冗余和不一致,并使子项目的更新和维护变得困难。
Git 子模块和子树
如果您可以使用单个命令将一个项目放入另一个项目中会怎样?如果您可以像添加子项目一样将项目添加到任意数量的项目中,并在您想要时随时推送更改,又会怎样?Git 为此提供了解决方案:Git 子模块和 Git 子树。这些工具的创建是为了在更模块化的层面上支持代码共享开发工作流程,旨在弥合 Git 存储库的源代码管理 (SCM) 和其中的子存储库之间的差距。

樱桃树生长在桑树上
这是本文将详细介绍的概念的真实场景。如果您已经熟悉树,以下是此模型的样貌

CC BY-SA opensource.com
什么是 Git 子模块?
Git 在其默认软件包中提供子模块,使 Git 存储库可以嵌套在其他存储库中。准确地说,Git 子模块指向子存储库上的特定提交。以下是 Git 子模块在我的 Docs-test GitHub 存储库中的样子

folder@commitId 格式表示该存储库是一个子模块,您可以直接单击该文件夹以转到子存储库。名为 .gitmodules 的配置文件包含所有子模块存储库详细信息。我的存储库的 .gitmodules 文件如下所示

您可以使用以下命令在您的存储库中使用 Git 子模块。
克隆存储库并加载子模块
要克隆包含子模块的存储库
$ git clone --recursive <URL to Git repo>
如果您已经克隆了存储库并想要加载其子模块
$ git submodule update --init
如果存在嵌套子模块
$ git submodule update --init --recursive
下载子模块
顺序下载子模块可能是一项繁琐的任务,因此 clone 和 submodule update 将支持 --jobs 或 -j 参数。
例如,要一次下载八个子模块,请使用
$ git submodule update --init --recursive -j 8
$ git clone --recursive --jobs 8 <URL to Git repo>
拉取子模块
在运行或构建父存储库之前,您必须确保子依赖项是最新的。
要拉取子模块中的所有更改
$ git submodule update --remote
创建带有子模块的存储库
要将子存储库添加到父存储库
$ git submodule add <URL to Git repo>
要初始化现有的 Git 子模块
$ git submodule init
您还可以通过将 --update 添加到 submodule update 命令中,在子模块中创建分支和跟踪提交
$ git submodule update --remote
更新子模块提交
如上所述,子模块是指向子存储库中特定提交的链接。如果您想更新子模块的提交,请不要担心。您无需显式指定最新的提交。您只需使用通用的 submodule update 命令即可
$ git submodule update
只需像往常一样添加和提交,即可创建父存储库并将其推送到 GitHub。
从父存储库中删除子模块
仅仅手动删除子项目文件夹不会从父存储库中删除子项目。要删除名为 childmodule 的子模块,请使用
$ git rm -f childmodule
尽管 Git 子模块可能看起来易于使用,但初学者可能会发现很难上手。
什么是 Git 子树?
Git 子树是在 Git 1.7.11 中引入的,它允许您将任何存储库的副本作为另一个存储库的子目录插入。它是 Git 项目可以注入和管理项目依赖项的几种方式之一。它将外部依赖项存储在常规提交中。Git 子树提供干净的集成点,因此它们更容易回滚。
如果您使用 GitHub 提供的 子树教程 来使用子树,您将看不到本地的 .gittrees 配置文件。这使得很难识别子树,因为子树看起来像普通文件夹,但它们是子存储库的副本。带有 .gittrees 配置文件的 Git 子树版本在默认 Git 软件包中不可用,因此要获取带有 .gittrees 配置文件的 git-subtree,您必须从 Git 源代码存储库的 /contrib/subtree 文件夹 下载 git-subtree。
您可以像克隆任何其他通用存储库一样克隆任何包含子树的存储库,但这可能需要更长的时间,因为子存储库的完整副本驻留在父存储库中。
您可以使用以下命令在您的存储库中使用 Git 子树。
将子树添加到父存储库
要将新的子树添加到父存储库,您首先需要 remote add 它,然后运行 subtree add 命令,例如
$ git remote add remote-name <URL to Git repo>
$ git subtree add --prefix=folder/ remote-name <URL to Git repo> subtree-branchname
这会将整个子项目的提交历史合并到父存储库。
推送和拉取子树的更改
$ git subtree push-all
或
$ git subtree pull-all
您应该使用哪个?
每种工具都有优点和缺点。以下是一些功能,可以帮助您决定哪种最适合您的用例。
- Git 子模块的存储库大小较小,因为它们只是指向子项目中特定提交的链接,而 Git 子树则包含整个子项目及其历史记录。
- Git 子模块需要在服务器中可访问,但子树是去中心化的。
- Git 子模块主要用于基于组件的开发,而 Git 子树用于基于系统的开发。
Git 子树不是 Git 子模块的直接替代品。有一些注意事项指导着每个子模块的适用场景。如果存在您拥有并且可能将代码推回的外部存储库,请使用 Git 子模块,因为它更容易推送。如果您有不太可能推送的第三方代码,请使用 Git 子树,因为它更容易拉取。
试用 Git 子树和子模块,并在评论中告诉我您的使用体验。
评论已关闭。