分支模式在SVN环境下的应用
 

2009-07-31 作者:Microke 来源:微客的BLOG

 

关于分支模式

并行软件开发是企业级环境下软件开发的一种不可避免的模式,这种开发模式可以说是任何大中型软件产品和项目所必需的。然而,并行开发在为我们的开发效率提高保证的同时,也会给我们的开发管理带来诸多问题:

* 什么时候进行分支?
* 什么时候进行合并?
* 如何选择有效的分支策略?
* 如何保证不同分支上的代码同步问题?
* 如果建立对分支访问控制的授权机制?
* 如何避免频繁的合并冲突?
* 如何处理被复用的代码?
* ……

可以说并行开发中的分支与合并是一个涉及到环境、方法和技术平台等诸多因素的综合性难题,在这种场合下,“模式(Pattern)”可能是解决上述问题的一个很好的工具。所谓模式,其实就是解决某一类问题的方法论。你把解决某类问题的方法总结归纳到理论高度,那就是模式。Alexander给出的经典定义是:每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。

在不同的领域有不同的模式,具体到并行开发领域,“分支模式”是专门针对并行开发环境下分支及合并作业中的各种不同的操作方法抽象出来的一套方法论。其主要由以下几部分组成:

结构模式——通过约束和指导分支/代码线的整体结构,实现并行开发的组织结构、开发模式及开发过程的约束和指导。

规则模式——通过对特定分支/代码线实施的约束,实现对该分支/代码线相关的操作进行约束,如访问控制及合并等操作的约束。

创建模式——提供对分支/代码线创建的约束

反模式——以反例的方式展示并行开发中常见的行为误区和陷阱,并提供有效的解决方案。

分支模式在并行开发中的应用难点有两个:一是如何根据企业的实际情况选择适合的分支模式,二是如何构建一个技术平台来将这些分支模式的理论和方法有效的应用于实践。为此,我们专门在此开辟专栏和大家分享并行开发中分支模式相关的理论、方法以及如何将这些理论和方法付诸实现的相关实践。

主线(结构模式)

一、分支模式的相关定义

模式 主线

别名 主干、主锚线、本线、地线(Main Trunk, Main Anchor Line, Home Line, Ground Line )

场景

在开发和维护周期中,因为各种原因需要创建多条代码线,典型的代码线是发布线、维护线和集成线。这在采用每发布代码线、并行维护/开发线和重叠发布线 (或者其任何变形模式)的情况下尤为如此。随着项目的进行,会创建出越来越多的代码线,从而导致项目的版本树越来越宽。

连续的瀑布式分支(应该避免的情况)

问题

怎样确保当前活动代码线的数量在可控范围内,以及避免项目的版本树过宽过深?

动机

  • 通常,每条代码线在某个时间点需要将变更集合并至其父分支。所以越多的代码线就意味着更多的合并,而越多的合并意味着越多的同步工作。
  • 当后续的版本发布时,看起来只有在当前版本的代码线上开创一个新的分支才算是合理的。

解决方案

在每一个分支树中保持一个“主”分支或代码线作为主干,而不是持续的瀑布式从分支创建分支从而使分支树变得宽广而笨重(在每一对父子分支之间需要大量的同步工作)

拥有主线的瀑布式分支

当为主发布创建代码线的时刻来到时,我们不会从前一个发布线创建新的发布线,而是将先前的发布线合并回主线,然后再从主线创建新的发布线。
为特定代码线或分支合并回来的过程被称为“主线化”、“主干化”、“归位”、“锚定”或“接地”("mainlining," "trunking," "homing," "anchoring," or "grounding.")。

变种 稳定接收线(也称为稳定主线、主集成线和基础集成线(Stable Mainline, Main Integration Line, Base Integration Line))

保持一个稳定的,可靠的主要开发主干可以用来从其他代码线导入(接收)稳定基础。在代码线上不会直接发生开发工作,所有集成工作必须来自其他代码线(不是一个单独的离散的活动分支)。这个规则唯一的例外是为了保证代码线构建和功能一致性的集成变更。

(用以接收稳定基线的稳定主线——实际上就是通常所说的基线)

LAG开发线(也称为主开发线、中央线和主流)

把主干作为最新的最好的(LAG)会一直进展的开发线,所有前面的发布的代码线都会在其退休后被合并入其中。主干作为下一步/最后的开发发布(不是维护,而是开发,包括显著的改进和新特性)的开发线使用,因此当B2发布的工作已经准备好开始,而A1发布已经完成或逐渐停止,则建立一个新的分支来结束A1的工作(见延迟分支),而LAG线则用来针对B2(最新的和最好的开发工作)发布的工作。

(LAG开发主线)

对于特定代码线或分支合并到滞后(LAG)开发线的过程也可以称为"LAGging," "mainlining," "LAG-lining," or "mainstreaming."。

尽管作为一个主线使用,LAG线也可以作为主线与稳定接收线结合:

(有一条稳定主线用于接收发布版本的LAG开发主线)

二、模式的分析

主线模式及其变种主要试图从分支的结构上约束其不合理的扩展和延伸,而尽量使主线成为其他代码线创建的来源及合并的目标。稳定接收线(也就是通常所说的基线)用于接受并保存相对稳定的版本,通常情况下其只接受版本(通常情况下为直接保存版本的镜像而非合并操作)而不进行其他任何操作。

三、主线模式在Subversion环境下的实现

如上图所示实际上就是有一条基线伴随的LAG开发线模式,实现该模式相关的约束包括:

1、所有的代码线(不包括分支)都从主线创建

2、主线以外的代码线的合并操作都以主线为目标

3、基线只接受版本(直接保存版本的镜像而非合并操作)而不进行其他任何操作。

宽松访问线(规则模式)

一、分支模式的相关定义

模式 宽松访问线(Relaxed-Access Line)

问题 如何确定代码线访问控制规则的限制或排他程度?

动机

  • 如果许多开发者在代码线上工作,或某一些人缺乏经验,那严格的治理是必要的。
  • 如果代码线上发生的工作具有显著的风险级别或难度,则检入和合并需要更紧密的监控和/或验证。
  • 保证代码线一直处于完整的状态非常重要,这样就不会影响其它工作在代码线上的人们。
  • 如果代码线对应了特定提升级别(见阶段集成线)或生命周期阶段,这也许指明了对特定代码线验证一致性的必要性级别。

解决方案

如果代码线是用来开发或维护(而不是排它或集成),并且工作在代码线上的团队规模相对较小,人员富于经验并可靠,那么给开发者有相对宽松自由的区域,让他们做他们已经知道如何去做的事情:在一起以及时的方式工作。使用最小检查和控制,但是要确保代码线所有者可以认真对待他的工作,并可以一直知道代码线的状态以及确认完整性是否被特定风险/复杂开发任务危害。

相关模式

MYOC(合并你自己的代码)及其变种PYOC(传递你自己的代码)是使用宽松访问线最常见的副产品(或原因),如果在你的环境中,风险较小,而且开发者合并自己的变更到代码线的交流通畅,那么我们有充足的理由使用宽松访问。

二、对模式的分析

这种访问模式通常适用于冲突不严重(如开发初期或彼此按模块独立开发)的情况,要求相关人员具备一定的水准以保证开发的质量,而在开发的后期通常不适用这种模式。

三、宽松访问线(Relaxed-Access Line)在Subversion环境下的实现

如上图所示(相关内容只是相关模式实现的一个实例,实际使用时可根据实际需求对角色及授权进行调整):

1、代码线的所有者对代码线拥有完全的操作权限

2、主管及EPG成员仅对代码线拥有有限的操作权限(读)

3、除测试人员以外的项目成员都对代码线拥有完全的读写权限

4、代码线的所有者以及项目经理和配置经理拥有对代码线锁的权限

大爆炸式集成(反模式)

一、分支模式的相关定义

陷入的误区 大爆炸集成

别名 大怪兽集成

症状

由于某种原因,一直不选择集成,直到(软件)要发布的时候 ,才把所有的分支一下子全部交给倒霉的集成者进行集成。经常性的增量集成看起来是流行的常识性规则(亦称为:尽早且经常合并),大爆炸(的方式)显然在隔离和避免风险方面达到了极致,这是大怪物地结束合并,结果不是一个“大爆炸”,而是以“哭泣”告终。

大爆炸式的集成(需要避免的反模式)

原因

一个原因是我们未能成功找到尽早集成和经常集成的地正确“节奏”和“脉搏”。有时‘大怪兽式集成’是“整合恐惧症”带来的附加品,在尽力缩小合并数量的时候,结果会是一个延期而至的可怕的复杂合并的结尾。因而,在这种情况下,“集成是魔鬼” 变成了自我实现的一种预言,并且在失败的恶性循环中不断加强。

影响

太多人都很熟悉所造成的后果,直到一切已经太晚,如系统不能正确的构建,无法通过测试,或部分代码与其它代码工作不能协同工作,我们才能发现这问题。直到项目生命周期的下一个阶段,这些信息才得到交流,相关的风险和返工将会非常巨大,会导致“合并是魔鬼”或“合并恐惧”的心理。

修复和预防

使用尽早经常性的集成或使用一个或更多的变种来确保以一定频率和间隔执行集成,以在时间充裕,额外工作较少时尽早分解风险和尽快交流问题区域。你可以现在或者将来,或者由你自己决定何时付出,有规律的经常的集成可以迫使你在每次迭代和集成计划中只需要付出很少的努力,通过分解减少随时间积累的合并负担,从而定义了健康项目的“脉搏”。

二、模式的分析

这种大爆炸式的集成在缺乏有效管理的团队中是经常发生的,我们经常可以看到这样的场景:明天就要发版本了(尤其是非计划的发布),今天所有的相关人员都一起合版本,于是大家惊讶的发现系统出现无数的合并冲突和缺陷,而在这种情况下,延迟发布几乎是唯一的选项了。
要避免这种最好的方式就是通过某种机制约束分支的周期和合并。

三、大爆炸式集成在Subversion环境下的规避方式

如上图所示,在创建分支时添加如下约束:

1、创建分支时定义分支的周期(通常任务分支都是需要及时终结的),并强制终结

2、创建分支时定义分支的合并周期和约束方式(包括提醒和强制合并两种可选方式)

通过上述的约束,可以使分支/代码线及时的合并和终结,从而避免大爆炸式集成的发生

代码所有权(规则模式)

一、分支模式的相关定义

模式 代码线所有权(Codeline Ownership)

别名 分支所有权(Branch Ownership )

场景

作为一名程序员,在一组多代码线的环境下,并至少在一条代码线上开发。代码线规则已经为该代码线定义好检入/检出的规则。有些人要在代码线上进行某些工作,但是该规则并没有允许这样的操作,或者就是规则对一些特定事务的描述含糊不清。

问题

能影响代码线的动作能否执行?在保证代码线的完整性和连续性的同时,怎样做上面的决策?

动机

  • “理论上,实践和理论是一致的;但在实践中,其两者却有极大的不同。”没有一个规则能够涵盖所有的情况。 代码线规则从理论上满足了需求,但是在实践中,则还有必要其他一些东西补充理论和实践之间的缺失。
  • 如果代码线规则不是很清楚,则开发人员需要将其定义清楚。
  • 代码线规则是会被违背的,不管是有意还是无意。
  • 代码线必须保持正确且连续的状态,以避免与正在进行的开发任务起相反的作用。.

解决方案

为每个代码线分配一名所有者(owner),其相应的职责如下

  • 如果代码线的规则定义不清晰,则定义清楚;
  • 如果检入的配置项与代码线的规则相抵触,则决定让其保留在代码线中,还是回退到上一个版本
  • 制定相应的规则,防止代码线处于含糊不清状态或不适用实际情况
  • 在该代码线上,辅助或执行变更的集成
  • 决定何时对代码线进行冻结,解冻;何时代码线必须结束生命周期并且合并到主线(Mainline)中

所有权(Ownership)并不一定意味着排他式的访问,但却表示用户认证访问控制。也许只有代码线的所有者才能检入文件(受限访问线);或者,其他人只要在检入前获得代码线所有者的同意,即可检入代码线,又或者在检入后立刻通知代码线所有者(宽松访问线)。代码线规则必须清楚地定义访问控制类型相对应的所有权类型。通常来讲,在代码线上工作的开发人员越多,相应的所有权策略也越严格。同样地,限制程度与代码线包含活动的风险性或是复杂性,或是对稳定性的要求成正比。在较小的项目和团队中的代码线中所包含较少的关键任务,在保证代码线完整性和连续性的前提下,提供比较随意,限制较少的访问控制策略。

变种 代码线专属(Codeline Dictatorship )

代码线的所有权中极其严格的一种形式,其配置项的检出和分支都是严格受限的,当然更包括检入。专属者可能是一个人,或是一个小组。一个常见的例子就是“远程开发线”。远程开发人员可能被禁止从非远程分支中创建新的版本或是分支,因此,本地分支仅仅是“主人(master)”开发的地方。这实质上就是ClearCase Multisite定义的“分支主人身份(branch mastership)”的概念。

导致的场景

  • 只有一个人对代码线的连续性和完整性负责。这样,代码线比较可能处于一种稳定的状态。
  • 保持所有者对代码线的状态负责,降低了代码线规则被践踏或代码线被用于错误目的的可能。
  • 代码线的概念完整性由一个头脑,也就是所有者维护,作为解决代码线问题的单一权威。

二、模式的分析

代码线所有权/代码线专属模式强调的是代码线所有者对代码线的控制权,适用于由专人(或角色)对代码线内容进行全权负责的情况下使用

三、代码线专属(Codeline Dictatorship )在Subversion环境下的实现

如上图所示(相关内容只是相关模式实现的一个实例,实际使用时可根据实际需求对角色及授权进行调整):

1、代码线的所有者对代码线拥有完全的操作权限

2、主管、项目经理及配置经理仅对代码线拥有有限的操作权限(读)

3、代码线的所有者拥有再授权的权限

代码线规则(规则模式)

一、分支模式的相关定义

模式名称 代码线规则

别名 每代码线规则

适用环境 使用多条代码线开发软件的情况下。

问题 开发人员如何知道需要将他们的代码存入哪条代码线中,并且何时保存?

动机

  • 每条代码线都有不同的目的 ;
  • 代码线的名称通常能暗示其目的;
  • 代码线的名称通常不能全部表达代码线的使用要点;
  • 如果代码写入到错误的代码线,而这错误的变更必须要回退,导致生产率的降低;
  • 使用正规文档描述代码线的用法会很有帮助,但是需要额外的记录和维护;
  • 这个文档太过拘谨就会有一点过度规划和专横了;

解决方案

除了给分支/代码线起一个有意义的名称之外,要给每条代码线明确目的,并使用简捷明了的策略描述其目的。其中应该包括以下一些要点:

  • 代码线包含何种工作,例如:开发、维护、一种特定的版本、功能或是子系统;
  • 配置项在怎样的条件下才能被检入,检出,分支,合并;
  • 对于不同的个人,角色,组,代码线该设置怎样的读写权限的限制;
  • 导入/导出关系:代码应该从其它哪些代码线中接受变更,同时应该将变更应用于其它哪些代码线;
  • 代码线的生命周期或结束条件;
  • 预期的工作负载以及集成频率。

让规则简短扼要:一个简单的经验方法是1-3段(各自25行25个字符,一页绝对是上限)。

请切记不是所有的代码线策略都需要上面所有的信息,只需要制定自己所需要的。一些版本控制工具允许在每个分支、代码线的名称上附加详细的注解,这是存放合适简短代码线规则描述的理想地方。开发者可以通过包含代码线名称的命令来查看代码线规则,而无需在别的地方找文档。否则,将代码线规则放在大家都知道的随手可得的地方(或许提供简单的命令或宏,对于给定代码线名称可以快速显示规则)。

二、对模式的分析

代码线规则这种模式实际上就是一种最基本的分支/代码线使用规范,它强调每条分支/代码线都应该以快捷而有效的方式记录其相关的信息,并且这些信息可以随时被方便的访问。

作为更进一步的要求,除了将相关信息记录在案,在某些情况下对其中部分内容(如分支的周期及合并的频率等)进行提醒甚至约束也是有其必要性的。

三、宽松访问线(Relaxed-Access Line)在Subversion环境下的实现

如上图所示:

1、每条分支/代码线代码线创建时都有效的记录相关信息

2、对分支的生命周期和合并周期提供约束控制

注:上述功能的实现是基于在系统底层屏蔽了所有不受控的分支创建操作,而只能在特定应用系统内进行分支/代码线的创建,从而使所有分支/代码线相关操作都处于受控状态


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织