Git中的供应商分支

35
一个Git项目中包含独立开发的第二个项目。子模块不能用于这个小项目,因为即使用户尝试克隆或下载“父”项目时,子项目也必须被包括在内。
子树合并也无法使用,因为子项目正在积极开发中,而子树合并会使将这些更新合并回原始项目变得非常困难。
我被告知,在SVN世界中该解决方案被称为“供应商分支”,而在Git中如此简单,以至于不需要进行解释。但是,网络上存在许多粗略的教程,我似乎无法使其正常工作。
请问是否能够解释如何创建这样一个结构:一个项目嵌套在另一个项目中,两个项目都可以从同一个工作目录进行开发和更新。当客户尝试下载“父”项目时,最好(或者说相当重要的是)能够自动获得子项目的最新版本。
请勿向我解释如何使用子模块、子树合并甚至SVN:Externals。本文旨在理解如何进行供应商分支,并且如果那里有所遗漏,请在此处发布,谢谢!

1
Ruby?Git 与 Ruby 没有关系,Git 是为 Linux 内核设计的。Git bash 在 Win32 系统上使用起来一直很好。 - singpolyma
1
这是关于编程的话题...你一直争论git与ruby有关。但事实并非如此。Rails不等同于Ruby。 - singpolyma
@SamGoody: 如果GIT还没有准备好面向生产,那么为什么它被这么多项目使用?我也发现GIT教程非常直观。 - EFraim
1
你所描述的不是供应商分支。供应商分支是指您维护第三方产品的自定义版本,而第三方仅提供源代码,而不提供其存储库的访问权限。当您从第三方接收更新时,您将其提交到供应商分支,然后将其更改合并到主分支中。 - rjmunro
1
感谢您提出这个问题。您成功地用言语表达了一个困扰我数周的问题。我正在尝试解决如何管理Drupal核心、自定义模块和贡献模块等拼接的git代码库,而这个问题与此密切相关。 - JW.
显示剩余7条评论
4个回答

42
我认为子模块是处理"供应商分支"的方法。
以下是如何使用子模块... 哦,开个玩笑。

仅仅是一种想法; 您想要:

  • 在同一目录中开发主项目和子项目(称为"系统方法":您开发、标记和合并整个系统)
  • 或将您的子项目视为"供应商分支"(这是一个分支,允许您访问供应商外部组件的明确定义版本 - 或"文件集" - ,并且仅更新每个该外部组件发布的新版本: 这称为"组件方法",整个系统被视为单独开发其自身的不同组件的集合)

这两种方法是不兼容的:

  • 第一种策略与子树合并兼容:您正在同时处理项目和子项目。
  • 第二种方法使用子模块,但子模块用于定义配置(您需要工作的标记列表):每个Git子模块与svn:externals不同,都固定到特定的提交ID,这就是允许您定义配置(如SCM中的"软件配置管理")

我喜欢第二种方法,因为大多数情况下,当您有一个项目和一个子项目时,它们的生命周期是不同的(它们不以相同的节奏开发,不同时打标记,也没有相同的名称)。

你提出的那种“基于组件”的方法受到限制的原因在于“两者可以从同一工作目录中开发和更新”的部分。
我真的建议您重新考虑该要求,因为大多数IDE都可以处理多个“源”目录,并且子项目开发可以在其专用环境中完成。


samgoody补充道:

想象一下Joomla和ModX的eMap插件。插件和Joomla特定代码(属于Joomla而不是eMap的部分)在插件位于Joomla时同时开发。所有路径都是相对的,结构是固定的,它们必须一起分发 - 即使每个项目都有自己的生命周期。

如果我理解正确,您处于这样一种配置中:开发环境(您正在处理的文件集)与发布环境(同一组文件复制到发布平台上)非常相似。

这完全归结为粒度问题:

  • 如果两组文件不能互相存在,则应将它们视为一个大项目(并进行子树合并),但这会强制它们被标记并作为一个整体合并。 -如果一个依赖于另一个(可以单独开发),则它们应该在自己的Git存储库和项目中,第一个项目依赖于第二个项目的特定提交作为子模块:如果在第一个组件的正确子树中定义了子模块,则所有相对路径都将得到尊重。

samgoody 补充道:

原帖列出了与子模块相关的问题 - 主要是 GitHub 的下载不包括它们(对我来说至关重要),以及它们会卡在特定提交上。

我不确定 GitHub 的下载最近是否有问题: "Guides: Developing with Submodules" 文章确实提到:

最重要的是:克隆您的 my-awesome-framework 分支的人将没有问题下载您的 my-fantastic-plugin 子模块,因为您已经注册了该子模块的公共克隆 URL。 命令如下:

$ gh submodule init
$ gh submodule update

将子模块拉入当前仓库。至于“它们卡在特定提交上”的问题:这就是子模块的全部意义,允许您使用配置(组件的标记版本列表)而不是最新的可能不稳定的文件集来工作。您的要求是完全合法的,我不想评判其正当性:我的先前答案只是为了提供更大的背景并尝试说明通常使用通用SCM工具可用的选项。子树合并应该是这里的答案,但需要合并仅针对主项目文件进行的提交,而不是针对子项目进行的提交。如果您可以管理那种部分合并,我认为这是正确的路径。然而,我没有看到一个原生的Git方法来做你想做的事情,而不使用子树合并或子模块。我希望一个真正的Git guru会在这里发布一个更适当的答案。

想象一下,Joomla和ModX都有一个eMap插件。插件和Joomla特定的代码(这是Joomla的一部分,而不是eMap的一部分)在插件内部开发。所有路径都是相对的,结构是固定的,它们必须一起分发 - 即使每个项目都有自己的生命周期。我不了解IDE;我们的办公室去年从Eclipse切换到Notepad++用于HTM/PHP/JS,从Flex切换到FD用于AS [提高了生产力]。我错过了什么重要的东西吗? - SamGoody
如果项目相互依赖,使用子树合并。否则使用子模块。 我需要避免使用子树和子模块(请参见问题),而且我宁愿在不太争论是否合理的情况下解决这个需求。 在我的情况下,较大的项目必须包含子项目,但反过来则不行。原始线程列出了有关子模块的问题 - 主要是GitHub的下载不包括它们(对我至关重要)以及它们会卡在特定提交上。 有什么想法吗? - SamGoody
1
这是一个非常有趣的回答。我以前从未听说过关于 scm 中 componentsystem 方法的比较。这个概念来自哪里?即是否有一些开创性的学术论文/博客文章或书籍推广了这些想法?如果有的话,您能提供参考资料吗? - JW.
2
@JW。我在过去使用ClearCase的经验中首次遇到了这些概念:https://dev59.com/sErSa4cB1Zd3GeqPWGda#1766662 - VonC
感谢VonC提供的参考。我很快会详细研究一下。从初步了解来看,这些想法似乎源于IBM的统一变更管理(Unified Change Management)。该链接为http://www.redbooks.ibm.com/redbooks/SG246399/wwhelp/wwhimpl/common/html/wwhelp.htm?context=SG246399&file=20-15.htm,我已经将其添加到我的阅读列表中。 - JW.
@JW,是的,UCM。我一直对UCM的实现持批评态度,因为有寄生基线,但概念是正确的。 - VonC

7
我终于在回到山区之前有几个小时的网络时间。我们将看看我是否有什么能够为您的情况提供帮助的见解。
我的(可能过于简化的)理解是,您的(内部)团队正在使用外部框架开发主要项目代码,而(外部)供应商正在开发插件来为您的项目增加功能。供应商不会更改您的代码,也可能不需要最新的开发版本,但需要您的稳定代码来开发和测试他们的工作。您的团队不会更改框架,但有时会对插件进行更改。
  1. 像VonC那样(他通常会仔细思考事情),我不认为Git完全符合您的要求。像他一样,我认为使用子树合并模式是最接近的匹配。我不是一个Git大师,但我已经成功地将Git应用于各种需求。也许Git不符合您的需求:

    • SVN允许您在一个库中拥有多个存储库,这对您很重要。我认为这意味着使用外部或供应商分支模式来接近您想要的内容。

    • Mercurial有一个扩展程序Forest,用于使用嵌套存储库,这似乎更符合您的思维模型。15个月前,我选择了Git而不是Mercurial,但HG很稳定,在许多用途上,我认为它与Git相当。我不知道该扩展程序的稳定性如何。

  2. 如果我处于您的情况下,我会使用两个Git存储库——一个用于插件,另一个用于主项目。供应商将在插件存储库中进行开发,并具有一个发布分支,他们将当前版本的插件拉入其中,而不包括其他开发环境。该分支将作为供应商分支被拉入到MainProject存储库中,然后合并到您的主开发分支中。当您的团队对插件进行更改时,他们会在主开发分支的功能分支中进行开发,并将其作为补丁提交给供应商存储库。这为您提供了一个非常清晰的工作流程,相对容易设置和学习,同时保持开发历史记录分离。

    我不是在争论,只是想说这是我理解您情况下Git的最佳匹配。最简单的设置方法是使用子树合并,但是这不会在两个方向上运行更改,这是我使用该模式的反对意见。

  3. 如果您的团队真的积极参与插件开发,或者您真的希望将两个项目和插件的开发历史记录集成到一个Git存储库中,那么只需使用一个Git存储库即可。您可以像这里所述,不时地提取插件及其历史记录以供供应商记录。这可能会给您比预期更少的封装,但是Git不是为封装而设计的——Git的数据结构基于跟踪一个整个项目中的更改。

也许我误解了您的情况,这些都不适用。如果是这样,我很抱歉。感谢您和VonC共同解决的细节,这些细节填补了我在试图理解您的问题时原本存在的许多漏洞。


4
个人来说——你说“我不是 Git 大师”,但我注意到你的回答几乎成为了该网站上所有 Git 相关问题的被接受答案。非常感谢你的帮助。 "VonC(通常会仔细地思考问题)" —— 我想应该是这样的吧。在一个有来自世界专家贡献的网站中,有人如何获得 25,000 多分的积分呢?我很敬佩!我也很感激他的帮助。你们太棒了。现在,如果我能让这个东西工作起来就好了... :) - SamGoody
"..我会使用两个Git仓库" - 我正在尝试这个方法,但是(作为新手的我)很难理解。从这篇文章中,我了解到要更新供应商项目,我应该将其最新提交的子树合并到父级,创建一个名为“vendor”的父级分支,并进行更改。然后他们从我的“vendor”分支中拉取这些更改到他们的项目中。我没有意识到这是可行的,并且不明白将其作为一个分支的意义所在。另外,另一篇文章没有避免子树合并吗? - SamGoody
请不要生气,但是告诉我是否有地方可以看到这个实际操作的完整逐行工作流程(一个理论上的例子)[1. 克隆 git://project.git 2. 合并分支 origin 3. 提交 origin master 等等] 非常感谢。 - SamGoody
在《Pro Git(2009)》一书的第9章“子树合并”一节中,Scott Chacon对使用子树合并涉及的工作流程进行了相当不错的解释。也许这会有所帮助。 - JW.

1
如果您只看原问题的标题: 使用git的供应商分支模式的好模板在哪里可以找到?

https://www.roe.ch/Git_Reference

供应商分支模式部分


这应该是被接受的答案... OP 请求解决方案,而人们提供的替代方案并不真正符合要求。我为OP感到难过,因为这是一个明确而简单的解决方案。 - axd

0
由于子项目正在积极开发中,因此无法使用子树合并,而子树合并使得将这些更新合并回原始项目变得非常困难。
原始问题(来自2009年4月20日)早于git subtree的宣布10天。很难确定OP当时在寻找什么,但是git subtree可能是正确的答案。
请注意,git subtree是一个命令行工具。现在它已经包含在git中。它使用子树合并,但不是同一件事。它有一个git subtree push命令,专门用于将本地更改合并回上游子项目。
我已被告知,在SVN世界中,该解决方案被称为“供应商分支”,在Git中如此简单,以至于甚至不需要解释。半吊子教程在网络上随处可见。
我写了https://david.rothlis.net/vendor-branch/来解释git中的供应商分支以及它们与git subtree的关系。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接