何时使用 git 子树?

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

我听说是 用于存储库分离。但是为什么我不创建两个独立的存储库,而是将两个不相关的存储库放在一个存储库中呢?

本 GitHub 教程解释 如何执行 Git 子树合并

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

52660 次浏览

首先: 我相信你的问题往往会得到非常固执己见的回答,在这里可能会被认为是跑题了。然而,我不喜欢这样的政策,并会推动边界上的话题有点外向,所以我喜欢回答而不是,希望其他人也这样做。

在你指出的 GitHub 教程中,有一个到 如何使用子树合并策略的链接,提供了一个关于优缺点的观点:

比较子树合并和子模块

使用 子树合并的好处是它是存储库的 减轻用户的管理负担 older (before Git v1.5.2) 客户 and you have the code right after 克隆人。

However if you use 子模组 then you can 选择不传输子模块对象. This may be a problem with the subtree merge.

另外,如果您对其他项目进行了更改,那么如果您只使用子模块,那么它就是 更容易提交更改

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

我经常与那些不是 Git 常规用户的提交者一起工作,他们中的一些人仍然(并将永远)在版本控制方面挣扎。教他们如何使用子模块合并策略基本上是不可能的。它涉及到额外远程的概念,关于合并、分支,然后将它们混合到一个工作流中。从上游抽水和向上游推水是一个两阶段的过程。因为树枝对它们来说很难理解,所以这一切都是没有希望的。

对于子模块来说,它还是太复杂了(叹气) ,但是它更容易理解: 它只是一个回购中的回购(他们熟悉层次结构) ,你可以像往常一样进行推拉操作。

Providing simple wrapper scripts is easier imho for the submodule workflow.

对于具有多个子回购的大型超级回购,选择不克隆某些子回购数据的点是子模块的一个重要优点。我们可以根据工作需求和磁盘空间使用情况对此进行限制。

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

就我个人而言,我不知道该用什么。所以我分享你的困惑: o ]

当你在 git的上下文中使用术语 子树时,你应该注意明确地注意你在谈论什么,因为这里实际上有两个独立但相关的主题:

Git-subtree Git 子树合并策略

TL; DR

这两个子树相关的概念有效地允许您在一个子树中管理多个存储库。与 Git 子模块不同,在 Git 子模块中,只有元数据以 。 gitmodule的形式存储在根存储库中,您必须分别管理外部存储库。

更多细节

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>]

在子树这个主题上,我遇到了相当多的资源,因为我正打算写一篇自己的博客文章。如果我这样做了,我会更新这篇文章,但现在这里有一些相关信息的问题:

Much of what you are seeking can be found on 这个 Atlassian 博客 by Nicola Paolucci the relevant section below:

为什么使用子树而不是子模块?

有几个原因 你可能会发现使用 subtree更好:

  • 管理一个简单的工作流很容易。
  • 支持旧版本的 git(甚至在 v1.5.2之前)。
  • 子项目的代码是可用的后,超级项目的 clone完成。
  • 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不像 submodules那样添加新的元数据文件(即。 .gitmodule).
  • 可以修改模块的内容,而不需要 其他地方的依赖项的独立存储库副本。

在我看来,这些缺点是可以接受的:

  • 您必须了解新的合并策略(即 subtree)。
  • 为子项目提供返回 upstream的代码稍微复杂一些。
  • The responsibility of not mixing super and sub-project code in commits lies with you.

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

您可能已经注意到,他还写了一个后续的 给你,其中他提到了一个重要的细节,这个方法被遗漏了..。

git-subtree目前无法包含远程!

这种短视可能是由于人们在处理子树时经常手动添加一个远程控制器,但是这也不是存储在 git 中的。作者详细介绍了他编写的一个补丁,该补丁将这个元数据添加到 git-subtree已经生成的提交中。在此之前,您可以通过修改提交消息或将其存储在另一个提交中来完成类似的工作。

我还发现 这篇博文也非常有用。作者在这个混合中添加了第三个子树方法,他调用 git-stree。这篇文章值得一读,因为他在比较这三种方法方面做得很好。他对自己喜欢什么和不喜欢什么给出了自己的观点,并解释了为什么他创造了第三种方法。

临时演员

结束语

本主题展示了 git的强大功能,以及当一个特征错过标记时可能发生的分割。

我个人不喜欢 git-submodule,因为我发现它对于贡献者来说更难理解。我还喜欢在项目中管理我的依赖关系的 全部,以促进易于重现的环境,而不需要管理多个存储库。然而,git-submodule目前更为人所知,所以很明显,意识到这一点是件好事,这取决于你的听众,他们可能会影响你的决定。

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

The main product of our company is high modular and developed in several projects in separate repositories. All modules have their separate roadmap. Whole product is composed with all modules of concrete versions.

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

为了为定制产品提供一个独立的产品生命周期(维护、功能分支) ,我们引入了 git 子树。我们有一个用于所有定制模块的 git-subtree 存储库。我们的定制是每天‘ git 子树推’回到所有原始存储库的定制分支。

像这样,我们避免管理许多回购和许多分支。 git-subtree 增加了我们的生产力几倍!

更新

More details about solution that was posted to comments:

我们建立了一个全新的仓库。然后,我们将每个具有客户分支的项目作为子树添加到新的回购中。我们有一个 jenkins 工作,定期将对原始存储库的主更改推回到客户分支。 我们只使用带有特性和维护分支的典型 git 流来处理“客户回购”。

Our 'client' repo had also building scripts that we also adapted for this particular client.

然而,目前的解决方案存在一个缺陷。

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

基本上,Git 子树是 Git 子模块方法的替代方法: There are many drawbacks or rather I would say, you need to be very careful while using git-submodules. e.g when you have "one" repo and inside "one" you have added another repo called "two" using submodules. Things you need to take care:

  • 当你在“ two”中更改某些内容时,你需要在“ two”中提交和推送,如果你在顶级目录(即在“ one”中) ,你的更改不会被突出显示。

  • 当一个未知的用户试图克隆您的“一个”回购,克隆“一个”后,该用户需要更新子模块,以获得“两个”回购

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

  • 为了克服这些问题,人们发明了子树方法

  • 我发现与子模块相比,子树方法更加可靠和实用:)(我是非常初学者说这些事情)

干杯!

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

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

因此,如果一个代码库是高度模块化的,那么这些代码加起来就会非常快。

然而,考虑到存储价格总是在下降,这可能不是一个重要因素。