如何使用源代码管理管理同一项目的开源和商业版本?

时间:2011-10-07 10:46:04

标签: git version-control mercurial repository bitbucket

我们正在开发一个开源项目,我们正在使用Mercurial进行源代码管理控制。该项目的Mercurial存储库是公共的(我们使用的是Bitbucket)。

现在我们有一个客户,我们需要为他们定制我们的开源软件。这些自定义必须保持私有,因此我们可能需要为此客户端创建一个新的Hg存储库;这个新的存储库将是私有的。

但问题是我们需要[不时]将开放存储库中的更改(例如新功能或错误修复)合并到我们的私有存储库中。

实现这一目标的最佳方法是什么?我读到可以合并两个或多个Mercurial存储库,但历史记录将丢失。由于许多冲突,合并也可能是痛苦的。如果我们将来再获得更多客户,我们应该如何管理他们的存储库呢?我们应该使用一个存储库和多个分支吗?如果两个项目版本开始向不同的方向发展,两个存储库变得越来越不同会怎么样?

请分享您对此的体验。

提前致谢!

4 个答案:

答案 0 :(得分:13)

您所描述的是分布式版本控制系统的标准内容:在两个存储库中开发并将一个存储库保留为另一个存储库的子集。首先为私有开发制作一个克隆:

hg clone open private

然后进入private并在那里制作新功能。正常提交。 private存储库现在将包含比open存储库更多的更改集 - 即新功能。

当错误修正和新功能作为常规开源流程的一部分放入open存储库时,您将它们拉入private存储库:

cd private
hg pull
hg merge

这样你保持不变:private存储库总是包含开放版本中的所有内容,以及私有增强功能。如果您正在处理私有版本并发现错误,那么请记得查看开放版本以查看该错误是否也存在。如果是这样,那么首先在开放版本中修复它并将错误修复合并到私有版本中。如果您错误地修复了私有版本中的错误,请使用hg transplant将错误修复复制到其他开放版本。

历史不会有任何损失。执行hg merge时,您必须像平常一样解决合并,并且冲突只会与您的私人更改所需的一样大。

要记住的重要一点是永远不要推(或拉)另一种方式,除非你想开始将一些私有变化发布到开源版本中。

您可以使用不同客户端多次使用此设置,如果多个客户端需要相同的私有增强功能,您还可以根据需要在不同的私有存储库之间推/拉变更集。

答案 1 :(得分:6)

原则上基本模型相对简单;有一个单独的私有存储库,它是公共存储库的克隆(分支),在那里进行所有私有更改,然后定期将公共存储库合并到私有存储库中。在保存历史方面没有任何问题,我不知道为什么你会读到这种情况。

然而,挑战是不要以不可维护的合并地狱结束,这只能通过严格的纪律来实现。

任何长寿分支的最基本的经验法则是:

  1. 保持私有分支尽可能小。最大限度地减少更改量,并保持较小,以免开始重构大部分代码或更改缩进。在像这里这样的单向合并情况下,您修改的任何代码都有可能发生冲突,甚至可能发生冲突。

  2. 经常合并。越频繁越好。如果你不这样做,那么当你想要集成来自公共存储库的更改时,你最终会得到一个有大量冲突的超级合并。

  3. 此外,您还应该在组织和编写代码时遵守规则以促进此方案。对于在哪个分支上的位置以及切断代码片段有明确的规则。

    理想情况下,您可以将自定义功能建模为插件或外部库,甚至是单独的项目。这可能并不总是很容易实现,在这种情况下,至少尝试根据您使用工厂方法实例化的原始子类来编写所有私有修改。通过在仅存在于专用分支上的独立文件中进行所有更改,可以最大程度地降低冲突风险。

    还要编写自动化测试。其中很多。否则,您不会立即检测到合并问题(发生),并且私有分支通常会被破坏。

    最后一个提示:在公共存储库上创建一个推送挂钩,拒绝任何包含您知道的私有变更集的推送;这样可以防止意外发布私人代码,并可能为您节省很多麻烦。

答案 2 :(得分:1)

好吧,一些扩展和变化。

  • 对于Martin的工作流程,您可以使用“每个任务分支”范例(分支必须在基础项目中创建,“打开”)和“push -b --new-branch”(仅发布分支,而不是整个集合)主线也改为“私人”,其中分支也必须合并为默认。

在这种情况下,叉子数量的增加每个叉子花费“+2命令+1存储库”

  • 分支的变化:单个开发人员的回购,许多命名分支(每个目标的分支+每个任务的分支)。 v.1的小偏差 - 只有一个开发回购,其中包含Open& Private命名分支(以及其他短名称分支)。任务也(如第1页)在单独的分支中实现,其中(没有推送)合并到所需的目标(开放和私有)。 用户可见还必须使用push -b
  • 更新Open和Private repos

在这种情况下,叉子数量的增加会使每个叉子的“+1命令+1分支”

  • 基于补丁的模型。单个通用代码,所有更改都在“vanilla Open”代码库更改集之上执行。启用MQ后,仅在队列中应用补丁转换为Open to Private。如果案例“存在于Open中,则不得存在于Private”中,我们将进入3级版本控制Core-Open-Private的情况。对于这种情况,必须使用在Core 之上的不同补丁集。 “不同的补丁集”可以是a)针对不同目标的不同命名分支以及手动应用和控制b)使用防护c)对于相当新鲜的Mercurial,可能是单独的队列,并且对于每个唯一目标也具有唯一队列。任务可以像以前一样在分支机构中开发,也可以在MQ补丁中完成(稍后或未完成)

在这种情况下,叉子数量的增加每个叉子花费“+1补丁”,可能是“+1队列”(见上文)。为了简单和易于管理,我更喜欢带防护的单队列

答案 3 :(得分:1)

项目照常由一组模块组成。 根据我的经验,有时甚至可以将一些模块放在单独的源控件存储库中。例如,某些实用程序模块或核心模块,如Web框架或DAO(ORM)模块。在这种情况下,您可以在源控件中使用分支,因为它们应该 - 支持主干开发并在同一源控件存储库中支持每个已发布的版本,以便能够合并分支。

因此,我建议您重新设计应用程序模块的结构,以便将核心(开源)功能与商业(客户相关)定制分开。 所以要管理开源和&商业版本您需要有一个单独的组装程序 - 它们可以或多或少相似,甚至商业版本可以使用开源版本作为完整工件集并扩展它们。

事实上,这是非常有趣的任务 - 去年我花了很多时间。我的决定是让一个具有完全功能的maven任务的core-repositoy(开源)来释放它。每个客户都有一个单独的仓库,只保留设计定制和一些客户特定的业务逻辑(只需在客户的spring XML中使用别名来覆盖您的“核心”Spring服务 - 请参阅BeanDefinitionOverriding),我的客户的maven-task基于核心工件的使用(通常扩展)其中一些 - 例如参见maven-war-plugin中的“覆盖”,允许扩展现有的WAR)。以这种方式处理您将永远不会在另一个分支中拥有相同类的克隆 - 您将使用它或扩展它,就像您在应用程序中使用log4j类一样。你应该扩展开源版本。

另一个兴趣任务是如何管理配置文件。我建议您查看Maven Remote Resources Plugin而不是默认Maven Resources Plugin。它允许您拥有配置文件模板,并将所有值移动到maven profiles,这些值应该特定于每个客户。请参阅Maven Tiles Plugin - 它帮助我大大简化客户项目中的“pom.xml”(我可以重复使用maven build& assembly过程的“tile”)