在版本控制中从同一代码库维护两个单独的软件版本


45

假设我正在编写同一软件/程序/应用程序/脚本的两个不同版本,并将它们存储在版本控制下。第一个版本是免费的“基本”版本,而第二个版本是付费的“高级”版本,它采用了免费版本的代码库,并在其上扩展了一些额外的增值功能。任何新的补丁程序,修补程序或功能都需要找到进入两个版本的方式。

我目前正在考虑使用masterdevelop分支作为主要代码库(免费版本),以及使用边master-premiumdevelop-premium分支作为付费版本。对免费版本进行更改并合并到master分支(develop当然,在进行了彻底的测试之后),develop-premium通过cherry-pick命令将其复制到分支以进行更多测试,然后合并到中master-premium

这是处理这种情况的最佳工作流程吗?有潜在的问题,警告或陷阱需要注意吗?是否有比我已经提出的更好的分支策略?

非常感谢您的反馈!

PS:这用于存储在Git中的PHP脚本,但是答案应适用于任何语言或VCS。

Answers:


83

您应该以某种方式设计应用程序,使这些高级功能可插入并由配置驱动,而不是通过不同的代码库,而不是使用具有共同基础的两个代码版本。

如果您害怕将这些高级功能(通过配置禁用)与基本版本一起提供,您仍然可以在最后的构建/打包步骤中删除该代码,而只有两个构建配置文件。

有了这种设计,您还可以交付5种不同的口味并变得非常灵活,甚至可以允许第三方做出贡献。


2
是的,这就是我昨晚睡觉前开始考虑的事情。谢谢!
Joseph Leedy 2014年

3
现代Windows是按这种方式设计的,所有版本都具有相同的代码,并且根据使用的许可证密钥具有解锁的功能。
Mooing Duck

39

我强烈建议不要为此目的使用分支。通常,对于以后将(或可能)再次合并在一起的事物,应该考虑使用分支(对于发行分支,您最终将停止其中一个分支的开发)。在您的情况下,您永远不会将“基本”和“高级”版本合并在一起,并且它们都将被无限期地维护,因此分支是不合适的。

取而代之的是,维护源代码的一个通用版本并使用条件编译(例如,#ifdef在C / C ++中,不确定PHP的等效形式)来包括或排除“基本”和“高级”之间不同的代码部分。

看来PHP可能没有内置这样的条件编译功能,因此您可以使用C预处理器(cpp,您可能已经拥有了)来预处理您的通用源代码,并从中生成“基本”和“高级”没有预处理程序指令的版本。当然,如果您选择执行此操作,则应使用make或类似的方法来自动化运行预处理器的过程。


您所说的关于分支机构的说法很有意义!也许相反,我可以创建一个仅包含Premium代码的单独存储库,并使用某种发布脚本或子模块将其与基本代码结合起来?不过,这可能会使TDD变得更难...
Joseph Leedy 2014年

14
创建另一个存储库比创建分支还要糟糕!您绝对希望选择一种解决方案,该解决方案应避免版本代码的重复最少。
Greg Hewgill 2014年

2
第二个存储库的目的是容纳额外的代码-而不是整个应用程序的另一个副本。
Joseph Leedy 2014年

1
啊,我知道了,这更像是“插件”模型,您的基本代码可以加载和运行插件(如果存在)。插件代码是独立的,并提供高级功能。
Greg Hewgill 2014年

4
@Joseph:仅当两个代码库的版本几乎彼此独立时,才使用两个存储库。如果不是这种情况,我强烈建议您执行Greg编写的内容,并将所有内容保存在一个回购中。我唯一想的是使用“ C预处理程序”。我猜想有一个用您选择的语言编写的小脚本(PHP本身很好,Perl或Python甚至更好),该脚本可以在没有高级功能的情况下复制您的代码,从而达到目的。
布朗

8

我们正在使用2个独立的项目,基本项目和高级项目取决于基本项目。不要使用分支,它们通常用于功能。


这对我很有吸引力,因为您可以使用构建脚本来自动创建基本程序和高级程序。
neontapir

1
通常,您需要3个项目:公共部分(通常组织为库)和两个不同版本的自定义部分。
Andriy Tylychko 2014年

3

尽管大多数当前答案都支持条件编译而不是分支,但在一种情况下,使用分支显然有好处:如果您(现在或以后)决定使基本版本的源代码可用,包括所有版本历史记录,但不包括所有高级功能,则可以使用分支方法来实现,而不能使用单个分支和条件编译来实现。

我建议不要采摘樱桃,而是将所有更改从基本版本合并到高级版本。基本版中不应该包含任何功能或错误修复,而高级版中应该没有。为了使事情变得尽可能轻松,您应确保premium分支对通用文件的修改尽可能少。因此,高级分支应主要包含其他文件,并可能对构建指令进行一些细微修改。这样,对基本版本的更改将自动合并而不会引起冲突。

Greg的答案建议您“考虑分支,以便以后将(或可能)再次合并在一起的事物”。我刚刚描述的方法就是这种情况,除了所有提交的最终分支master-premium不是master(实际上是master-basic)。

当然,子模块也是一个选择。这取决于您的构建过程,但是如果您可以将高级版本制作为使用基本版本作为模块的项目,那就很好了。但是,如果您决定将某些功能从高级分支引入基本分支,则可能会遇到困难。在子模块中,这样的更改将表示为两个不同的提交,而在分支中,这将是对基本版本的单个提交,并且下一次合并到高级版本中将知道这些更改已经包含并且没有再次合并。


0

在“硬件”中,通常是这样做的,它们是用来控制混乱的系统,抱歉,我不记得他们叫什么。

一旦“中档”洗衣机发货,它的代码就不会更改,除了修复一个非常重要的错误外,即使几个月后交付的“低端”洗衣机的代码也有所更改。

客户不希望升级到已经带来的洗衣机,也不是每隔几个月就会发货一种新型号的洗衣机。

我们大多数人都不生活在这个世界上,除非您为洗衣机编写软件,否则格雷格所说的话也一样。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.