什么时候使用 git 子树?

When to use git subtree?

git subtree解决什么问题? 我应该何时以及为何使用该功能?

我读到它是 used for repository separation。但为什么我不创建两个独立的存储库,而不是将两个不相关的存储库合并为一个?

本 GitHub 教程解释了 how to perform Git subtree merges

我有点知道如何使用它,但不知道何时(用例)和为什么,以及它与 git submodule 的关系。当我依赖于另一个项目或库时,我会使用子模块。

首先:我相信你的问题往往会得到强烈的自以为是的答案,在这里可能被认为是题外话。但是我不喜欢那个 SO 政策,并且会把话题的边界向外推一点,所以我喜欢回答,并希望其他人也这样做。

在您指向的 GitHub 教程中,有一个 link 到 How to use the subtree merge strategy 给出了 advantages/disadvantages 的观点:

Comparing subtree merge with submodules

The benefit of using subtree merge is that it requires less administrative burden from the users of your repository. It works with older (before Git v1.5.2) clients and you have the code right after clone.

However if you use submodules then you can choose not to transfer the submodule objects. This may be a problem with the subtree merge.

Also, in case you make changes to the other project, it is easier to submit changes if you just use submodules.

基于以上观点,我的观点如下:

我经常与非 git 用户的人(=提交者)一起工作,有些人仍然(并将永远)与版本控制作斗争。教育他们如何使用子模块合并策略基本上是不可能的。它涉及附加遥控器的概念,关于合并、分支,然后将它们全部混合到一个工作流中。从上游拉动和向上游推动是一个两个阶段的过程。因为branches对他们来说很难理解,所以这都是无望的。

有了子模块,对他们来说还是太复杂了(叹气)但是更容易理解:它只是一个repo中的一个repo(他们熟悉层次结构),你可以照常推拉。

恕我直言,提供简单的包装器脚本对于子模块工作流来说更容易。

对于具有许多子存储库的大型超级存储库,选择不克隆某些子存储库的数据是子模块的一个重要优势。我们可以根据工作要求和磁盘 space 使用情况限制此设置。

访问控制可能不同。还没有遇到这个问题,但是如果不同的回购需要不同的访问控制,有效地禁止某些用户访问某些子回购,我想知道使用子模块方法是否更容易实现。

就我个人而言,我还没有决定自己使用什么。所以我分享你的困惑 :o]

当您在 git 的上下文中使用术语 'subtree' 时,您应该注意清楚地指出您在说什么,因为实际上这里有两个独立但相关的主题:

git-subtree and git subtree merge strategy.

TL;DR

这两个与子树相关的概念都有效地允许您将多个存储库合二为一。与 git-submodule where only metadata is stored in the root repository, in the form of .gitmodules 相反,您必须单独管理外部存储库。

更多详情

git 子树合并策略 基本上是使用您引用的命令的更手动的方法。

git-subtree 是一个包装器 shell 脚本,以促进更自然的语法。这实际上仍然是 contrib 的一部分,并没有完全集成到 git 通常的手册页中。 documentation 与脚本一起存储。

这是使用信息:

NAME
----
git-subtree - Merge subtrees together and split repository into subtrees


SYNOPSIS
--------
[verse]
'git subtree' add   -P <prefix> <commit>
'git subtree' add   -P <prefix> <repository> <ref>
'git subtree' pull  -P <prefix> <repository> <ref>
'git subtree' push  -P <prefix> <repository> <ref>
'git subtree' merge -P <prefix> <commit>
'git subtree' split -P <prefix> [OPTIONS] [<commit>]

我在计划写自己的博客 post 时遇到了很多关于子树主题的资源。如果我这样做,我会更新这个 post,但现在这里有一些与手头问题相关的信息:

您正在寻找的大部分内容都可以在下面的 this Atlassian blog by Nicola Paolucci 相关部分找到:

Why use subtree instead of submodule?

There are several reasons why you might find subtree better to use:

  • Management of a simple workflow is easy.
  • Older version of git are supported (even before v1.5.2).
  • The sub-project’s code is available right after the clone of the super project is done.
  • subtree does not require users of your repository to learn anything new, they can ignore the fact that you are using subtree to manage dependencies.
  • subtree does not add new metadata files like submodules does (i.e. .gitmodule).
  • Contents of the module can be modified without having a separate repository copy of the dependency somewhere else.

In my opinion the drawbacks are acceptable:

  • You must learn about a new merge strategy (i.e. subtree).
  • Contributing code back upstream for the sub-projects is slightly more complicated.
  • The responsibility of not mixing super and sub-project code in commits lies with you.

我也同意其中的大部分内容。我建议您查看这篇文章,因为它介绍了一些常见用法。

您可能已经注意到他还写了一篇跟进文章 here,其中他提到了一个重要的细节,但这种方法遗漏了...

git-subtree 目前无法包含遥控器!

这种短视可能是因为人们在处理子树时经常手动添加一个远程,但这也没有存储在 git 中。作者详细介绍了他编写的一个补丁,用于将此元数据添加到 git-subtree 已经生成的提交中。在它进入官方 git 主线之前,您可以通过修改提交消息或将其存储在另一个提交中来做类似的事情。

我还发现 this blog post 也非常有用。作者将他称为 git-stree 的第三个子树方法添加到组合中。这篇文章值得一读,因为他很好地比较了这三种方法。他对自己喜欢什么和不喜欢什么给出了个人意见,并解释了他创建第三种方法的原因。

额外内容

  • listing git-subtrees
  • Detach Subdirectory

结束语

本主题既展示了 git 的强大功能,又展示了当某个特征未达到目标时可能发生的分割。

我个人对 git-submodule 产生了反感,因为我发现贡献者更难以理解。我也更喜欢在我的项目中管理我的 ALL 依赖项,以促进易于重现的环境,而无需尝试管理多个存储库。 git-submodule,但是,目前更为人所知,因此了解它显然是件好事,并且取决于您的听众可能会影响您的决定。

我们有一个真实的用例,其中 git 子树是一种救赎:

我们公司的主要产品是高度模块化的,在不同的存储库中的多个项目中开发。所有模块都有各自的路线图。 整个产品由具体版本的所有模块组成。

与此同时,整个产品的具体版本是为我们的每个客户定制的——每个模块都有单独的分支。有时必须同时在多个项目中进行定制 (cross-module customization)。

为了让定制产品具有单独的产品生命周期(维护、功能分支),我们引入了 git 子树。我们有一个 git-subtree 存储库,用于所有自定义模块。我们的定制每天 'git subtree push' 返回所有原始存储库到定制分支。

像这样,我们避免管理许多 repos 和许多分支。 git-subtree 使我们的生产力提高了好几倍!

更新

有关已发布到评论的解决方案的更多详细信息:

我们创建了一个全新的存储库。然后我们将每个具有客户端分支的项目作为子树添加到新的回购协议中。我们有一项詹金斯工作,定期将对原始存储库的主更改推送回客户端分支。 我们只与 "client repo" 合作,使用典型的 git 流程与功能和维护分支。

我们的 'client' 回购也有我们也为这个特定客户改编的构建脚本。

然而,所提出的解决方案存在缺陷。

随着我们离产品的主要核心开发越来越远,针对特定客户的可能升级变得越来越困难。在我们的例子中,子树已经远离主路径之前的项目状态是可以的,所以子树至少引入顺序和引入默认 git 流程的可能性。

基本上 Git-subtree 是 Git-submodule 方法的替代方案: 有很多缺点或者我想说的是,在使用 git-submodules 时需要非常小心。例如,当你有 "one" 存储库并且在 "one" 内部时,你已经使用子模块添加了另一个名为 "two" 的存储库。您需要注意的事项:

  • 当您在 "two" 中更改某些内容时,如果您位于顶级目录(即在 "one" 中,则需要提交并推送到 "two" 中) 您的更改不会突出显示。

  • 当未知用户尝试克隆您的 "one" 存储库时,在克隆 "one" 之后,该用户需要更新子模块以获取 "two" 存储库

这些是一些要点,为了更好地理解我建议您观看此视频:https://www.youtube.com/watch?v=UQvXst5I41I

  • 为了克服这些问题,发明了子树方法。要获得有关 git-subtree 的基础知识,请查看以下内容:https://www.youtube.com/watch?v=t3Qhon7burE

  • 我发现子树方法比子模块更可靠和实用:)(我是初学者说这​​些东西)

干杯!

为了补充上述答案,使用子树的另一个缺点是与子模块相比的回购大小。

我没有任何现实世界的指标,但考虑到每次对模块进行推送时,使用该模块的任何地方都会在父模块上获得相同更改的副本(随后更新那些回购协议)。

因此,如果代码库高度模块化,那么加起来会很快。

但是,鉴于存储价格总是在下降,这可能不是一个重要因素。