http://www.infoq.com/cn/articles/agile-version-control/
多个敏捷团队之间的版本控制
如果我们有多个敏捷团队在同一个代码库上工作时,如何将彼此之间代码互相冲突的风险最小化?如何确保每个迭代结束时拥有一个干净的、可发布的软件版本?本文讲述了关于如何在敏捷的环境中与多个团队共同进行版本控制工作的实例——这正是我们在《Scrum and XP from the Trenches》中描述的公司所采纳的方式。
本文并非专为版本控制专家所写,实际上这样的专家在本文中找不到新东西。本文是为我们这些希望进行简单、有效的协作的人所准备的。任何直接参与敏捷软件开发的人,无论他承担何种角色,都有可能对其感兴趣——每个人都会用到分支和合并,而不只是配置经理。
读过本文之后,如果你需要一份文档,请去目录结尾处下载全文。
相关厂商内容
滴滴出行iOS客户端架构演进之路!
微信客户端如何应对弱网络!
函数式编程中的Swift与Swift中的函数式编程!
AWS Webinar 5月24日在线课堂|利用AWS Lambda创建应用
国际范 最前沿 不容错过的容器技术盛会
相关赞助商
GMTC全球移动技术大会2016年6月24日-25日,北京,点击了解详情!
目录
- 介绍
- 目标
- 单页总结(可下载并挂在墙上)
- 版本控制模式
- 分支所有者&方针
- “完成”概念
- 何时创建额外分支?
- 从工作分支公开发布至主干
- 如果团队同时在实现多个故事该怎么办?
- 完成包括回归测试在内的工作
- 分叉代码(合并冲突)
- 多个团队——如果其他团队同时向主干中发布代码该怎么办?
- 发布分支
- 大图景
- 模型的变种
- FAQ
- 参考资源
- 可下载的PDF
介绍
本文讲述了关于如何在敏捷的环境中与多个团队共同进行版本控制工作的实例。我假定你已经熟悉了Scrum的基本元素、XP方法和任务板。这些方式不是我发明的,它们是基于“主线(mainline)模型”或“稳定主干(stable trunk)模式”。想阅读更多信息请查看引用部分。
撰写本文,是因为我一直在遇到需要类似内容的团队。许多团队在理解了模型之后,似乎非常喜欢这些模型。它也正是我们在《Scrum and XP from the Trenches》中描述的公司所采纳的方式。它真的可以帮助我们以更敏捷的方式来开发和发布软件。通过以易于阅读的方式来描述模型,也许我不再需要反复在白板前做解释了。:o)
注意这只是众多模式中的一种,不是“银弹“。如果你决定采用该模型,也许你需要做出一些变更来适应你自己的特定上下文。
目标
在多个团队构成的敏捷环境中,版本控制模型必须达成以下目标:
- 快速失败
- 代码冲突和集成问题应该可以被迅速发现。
- 经常修复小问题要胜过不常修复大问题。
- 一直可发布
- 即使经历了一个混乱的Sprint,也要保证至少有些可以发布的内容。
- 简单
- 所有的团队成员每天都会使用这些模式,所以相关规则和程序必须要简单明了。
单页总结(对于挂在墙上的内容)
如果该图让你觉得很迷惑,别着急,阅读本文即可。如果其中的理念对你来说很明显,读读本文也无妨。
本总结也包含在可下载的pdf文件中
版本控制模式
分支所有者&方针
下面是要遵循的一条简单规则。
规则:每个分支(即使是主干分支)都拥有一个所有者和一条方针。
不遵守上述规则,我们只能得到一片混乱。方针说明按照本规则,什么样的内容可以检入到当前分支中。所有者是负责定义和检查规则执行情况的人。
“完成”概念
一个用户故事怎么样才算“完成”?更明确地说,团队什么时候才可以把一个用户故事移入到任务版上的“完成”一栏之中?这又到底意味着什么?
我会给出下面的假定。
假定:“完成”的定义=“可发布”。
所以当一个团队成员说某个用户故事已经“完成”,并将故事卡移入到“完成”一栏中之后,客户就可以马上跑到房间中说:“太好了!我们现在就上线吧!“,而且团队中没有人会说:“别,先等等。”
你可以使用任何你喜欢的“完成”的定义。但是要记得——如果定义中没有完全包含“可发布”的含义,你就要好好想想了:在“完成”定义中没有包含哪些内容?谁会来补齐这些内容?什么时候?如果在“完成”之后出现问题,又会发生什么?
“完成”分支
当一个故事“完成”后,它需要有一个落脚之处。使用我的关于“完成”的定义(“可发布”),就意味着系统中的某个分支可以发布,这样该用户故事对应的功能就可以进入到生产系统之中了。这就是“完成”分支。
任何分支都可以是“完成”分支。我将会使用主干作为“完成”分支(这是一个不错的开始)。有时这被成为“主线”。
假定:主干作为“完成”分支。
主干方针:
- 任何时候都可以发布
- 希望尽早发布
“任何时候都可以发布”,这意味着:在任何时候,产品所有者都可以基于主干的最新版本,发布新版本的产品。
下面是一个示例。
蓝色的线表示主干。每个绿色的球形图案表示一次代码检入活动。可以看到在本次Sprint中共检入了5次内容。虽然通常我们是在每个Sprint结尾时才进行发布,不过还是可以在任何时候从主干中发布产品。如果在接近Sprint结束时,有一半的团队成员生病,因此导致没有时间完成故事#5,我们还是可以进行一次发布。当然,我们也可以选择先不发布,以等待团队在另一个Sprint中完成故事#5。
“希望尽早发布”意味着如果我们不想让某个故事相关功能上线(或者说不关心它是否上线),就不要检入该故事相关的代码。如果我不希望发布上图中的故事#3,这个分支就被我毁掉了。由于这个分支不再是可发布的,我就违反了分支的方针。
规则:不要在单一分支上合并不同的发布周期。
何时创建额外分支?
新分支的创建越少越好。下面是一条基于经验的原则。
推荐:只在下面这种状况下才创建新的分支:当需要检入新的内容,而且没有哪个现有的分支能够在不违反自身方针的状况下完成本次检入。
工作分支
好,假设我们已经有了一个很好、很干净的主干,并且在任何时刻都处于“可发布”状态。
等一下。“可发布”意味着已完成集成测试。这就意味着我们必须要 运行集成测试。也就是说我们要在向主干检入代码之前要运行集成测试。
那我应该向何处检入我认为已经完成、但是在检入主干前需要进行验证的代码呢?当然,我可以在本机上完成测试,并直接将通过测试的代码检入到主干之中。但这还是有点吓人,我相信大家都遇到过“嘿,但是它在我的机器上运行没有问题啊”这样的事情。
另外一个问题是“好吧,我完成了今天的编码任务,现在要回家了。我该把代码检入到哪里?还没有测试过呢,所以不能检入到主干中。我想把它检入到别的地方,这样其他的团队成员就可以在其基础之上继续工作了”(敏捷团队是采取代码集体所有制的,对吧?)。
你看,我们有希望检入的内容,但是不存在不违反分支方针就可以进行检入的地方。这就是建立新分支的一个合理原因了。
我们可将此称为工作分支,团队所有成员都可共享该分支。有人称其为开发分支。
团队A的工作分支方针:
- 代码完成编译和构建,并通过所有的单元测试。
很好,现在我们有两个分支了。一个稳定的分支(主干)和一个稍微不那么稳定的分支(团队分支)。团队分支是红色的,表示它不太稳定;例如:这个分支通过了单元测试,但是它可能还没有完成集成测试,所以还没有稳定到可以发布的状态。好,现在团队有地方可以检入正在进行中的工作了!
嗯,那要是需要同步不同的分支时应该怎么做呢?往下看。
从工作分支公开发布(publish)至主干
最终(我们希望)一个故事可以进入到“完成”状态。或者更明确地说,最终工作分支会达到可发布状态。此时我们可以(而且应该)将此分支公开发布到主干上,比如将工作分支上所有的新代码复制到主干上。完成后,主干与工作分支就完全相同了。
我们可以称此过程为“公开发布”,因为我们已经完成了一些工作,而且现在已经准备好将其“公开发布”回主干供发布用。这样说只是一个有用的隐喻。
下面是一个示例。假设我们已经实现了两个故事:“开户(Register)”和“存款(Deposit)”。它们都是“已完成”的,也就是说通过了单元测试、集成测试,并且处于“可发布”状态。我们已经开始处理“取款(Withdraw)”这个故事,但是它还没有“完成”。任务板看起来会像下面这张图示一样:
每个黄色的即时贴表示一项任务,也就是要完成故事需要做的一项工作。例如编辑类X,更新构建脚本,等等。每项任务通常包含一个人日的工作,每个故事通常包含3至8个人日的工作。
分支的过程会像下面这样:
我们的第一个团队实现了“开户”故事,并将代码检入到工作分支中,运行集成测试,修正一些问题,再次检入,再次运行测试,通过了!“开户”故事就“完成”了!然后将其公开发布到主干之中。
接下来实现“存款”。只需要执行一次检入就可以了。集成测试通过,我们再次把代码发布到主干之中。
现在团队正在实现“取款”故事。他们已经完成两次检入了,但是这个故事仍然没有完成。
注意“发布到主干”并不是说我们仅把某个故事的代码直接拷贝到主干中,而是意味着所有的工作拷贝到主干中,做一次完整的同步。
这样就带来了两个很有趣的问题:
- 如果团队同时在实现多个故事,该怎么办?
- 如果其他团队也正在向主干发布内容,又该怎么办?
我们还是一个接一个地来看这些问题。
如果团队同时在实现多个故事该怎么办?
如果团队每次实现一个故事,将代码发布到主干中并不算什么大不了的事情。只要这个故事相关的代码在工作分支上完成实现并通过测试,我们把所有的相关内容从工作分支上复制到主干上就可以了。搞定!
先等一下。如果团队中同时开发多个故事呢?如果“开户”故事完成了,而“存款”还在进行中呢?
如果我们在这个点上向主干进行同步,就会将尚未完成的“存款”故事同步进去,这时它还不能发布呢!而且违反了主干的版本控制方针!
当然,我们可以等到“存款”故事完成。
(等待……)
好了,现在“存款”故事完成了!太棒了!等一下……现在有人开始开发“取款”用户故事了!没错!同样的问题又发生了!
如果 “存款”故事的一个测试失败了,就很难知道是因为“存款”故事相关代码造成的,还是由于检入同一分支中并且部分完成的“取款”故事相关代码的原因。
等待无法起到任何帮助作用。这样实际上是在滚雪球,期望在未来某个假设的时间点上所有的故事都可以完成(如果这样的情况真的能够发生的话),而且可以进行一次大规模的发布。
上述是一个非常普遍的问题。我们该怎么做呢?
下面是一些应对之策:
- 不要做过多的并行开发。要力图将团队的注意力每次都只放在一个故事之上。
- 如果在“开户”完成之前就有人准备开始“存款”相关的工作,要等到“开户”彻底完成之后再检入“存款”相关代码。或者可以将“存款”相关的代码检入到一个临时的分支之中,如果你喜欢操作多个分支的话。
- 如果在“开户”完成之前就有人准备开始“存款”相关的工作,可以让他先从一些相对安全和不可见的UI元素等部分开始,这些东西的变化不会影响到整个分支的可发布性。比如,“存款”需要开发一些新的代码以及对一些旧有代码的修改,可以先实现新的代码(新的方法、新的类、新的测试等等),而不是先去修改已有代码。如果“存款”需要新的GUI元素,那就让它们先不可见。等到“开户”故事完成而且发布到主干上之后,我们就可以开始实现“存款”的剩余代码了。
下面是一个合适的规则集:
- 任何针对优先级最高的故事开发的人都是“国王”。
- 团队其他的任何人都是“仆人”。
- 要是你想成为“国王”,就试着找路子帮助完成最高优先级故事的相关工作吧。
- “国王”任何时候需要帮助,“仆人”就必须马上提供相应的服务。
- “仆人”不能打扰“国王”的工作。
- “仆人”不能向工作分支中检入不可发布的代码。“国王”可以检入任何他想要检入的东西(当然,只要他不违反分支方针即可)。
- 目前优先级最高的故事一“完成”,任何开始下一个最高优先级的团队成员就成为了“国王”。
你甚至还能为团队争取到一些王冠饰品呢。:o)
总体来说,很多团队都会过高评价同时实现多个用户故事的效果。这样做从开发速度上说感觉好像不错,但却只是一个幻象;因为它将有风险和消耗实践的编码工作推到了最后——包括合并、集成与测试等工作。
这就是Scrum团队应该保持小规模的原因(少于9个人)——这样他们就可以紧密协作而且将注意力集中于自己的工作成果之上。如果任何人都在独立开发自己负责的用户故事,那大概就不会有多少协作的情形发生了。当然可以有人为将来做计划,为下一个故事做准备并做一些实现工作。但是在任何时候,团队的主要工作努力都要放在优先级最高的故事上。
多个团队就是不同的情况了。如果很想并行实现多个故事,那就不妨创建多个团队。我们不久后就会看到如何具体操作。首先我想讨论下关于回归测试,以及分叉代码的相关话题。
完成包括回归测试在内的工作
当一个故事“完成”后,我们会把它移入到“完成”一栏中,并将相关内容从工作分支拷贝到主干中。主干必须一直保持可发布状态。此处有一个重要的暗示。
规则:任何接触到主干的人,必须保证整个主干保持可发布状态——包括之前的全部功能!
实际上,这条规则意味着:对故事A的测试,同样包括运行之前实现的故事的全部相关回归测试。如果上传代码后,故事A没有问题,但是之前故事的测试却通不过了,这是不行的。
稍等。这是不是有点不合理啊?每完成一个故事就要运行所有的回归测试?
嗯,首先我没有说运行所有的回顾测试。我是说所有相关的回归测试。我们已经有了一个干净而且可发布的主干作为基础,现在只是要添加一个故事而已!这是一个很小的增量变更。如果回归测试可以自动化完成,我们就可以全部运行了。要是有需要手工完成的回归测试,那我们就要有选择性了。
最后还是归结到了对风险vs成本的权衡之上。对于每一个手动的回归测试,我们应该评估运行它的成本(比如需要多少工作量来完成测试),同时评估发现任何重要缺陷的可能性,对二者进行权衡。当然还要加入自动化该测试的成本。:o)
分叉代码(合并冲突)
假设我正在兴高采烈地编写调用Widget类的代码,我却不知道团队成员Jim在一个小时之前进行重构时移除了Widget类。现在我们就有了< b>分叉代码。在花费更多时间编写其他调用Widget类的代码前,我希望尽早发现类似问题。
规则:持续不断地(等同于尽早)将你的代码同步到工作分支中。
这种同步是双向的。从工作分支中取得并合并最新的代码,然后检入你的代码。第一步可以叫做“跟上”(等同于我要知道其他人检入了哪些内容)。第二步可以叫做“公开”(等同于我希望我所做的更新可以让团队其他人都知道)。
每小时同步一次是好习惯,基本上在进行任务切换或者没有处于某项工作进行中时,就可以进行同步。这不只是关于“我要尽快知道别人的代码是否与我的相冲突”,还包括“我希望其他人尽快知道我的代码是否与他们的冲突”。要记得不要违反工作分支的方针(通过单元测试等等)。
该规则听起来很浅显,但是请允许我不断重申。我希望大家都能对其一清二楚,因为涉及到多个团队协作时,我们会再次使用类似的思维方式。
多个团队——如果其他团队同时向主干中发布代码该怎么办?
假设有团队A和团队B。他们都是跨职能团队,并且一起开发一个航班订票系统。团队A的注意力放在开发订票流程之上,而团队B主要负责开发后台相关功能。
假设他们现在要开始一个sprint,每个团队有两个用户故事要开发(通常一个sprint中会有更多的故事)。
由于每个团队都要在发布代码到主干前进行测试,因此他们有各自的工作分支。
现在我们遇到了一个有趣的问题。假定我在团队A中,而且有我们自己的工作分支。变更可能先在主干中发生,而不会先出现在我的工作分支中!为什么?恩,因为有另一个团队啊,他们每完成一个故事就会将其发布到主干中!
所以在任何给定的时刻,在主干上都可能有我不知道的新代码。而且这些代码可能(但愿不会如此)会与我的代码冲突!也许团队B中的某人会重新命名Widget类,可我已经在代码中调用了它而且……呃……等一下,我们不是刚讨论过这个话题了么?
没错,是同样的问题。解决方案也相同。但是范围有一点点不同。
规则:每天都从主干向你的工作分支中合并代码。
每天开始工作时,我所在团队中的某人负责将代码从主干中合并到我们的团队工作分支中(等同于“跟上”主干中发生的变化)。
如果我的团队(团队A)发现一个代码冲突,我们会马上解决它——这是优先级最高的事情!如果我们需要团队B的帮助(或者是开发与我们发生冲突的代码的负责人),找他们过来,一起工作,把问题解决掉。重要之处在于我的团队要负责解决问题,而且我们要在自己的工作分支上(而不是在主干上)完成。
规则:在最不稳定的分支上解决冲突。
当然,如果人们不经常向主干发布代码,那从主干合并代码就是浪费时间了。除非有人发布工作到主干上,团队A和团队B之间的任何分歧都会是不可见的。所以接下来的规则如下:
规则:经常从工作分支向主干合并代码,例如每完成一个故事之后。不要等到sprint结束后再做这个工作!
注意这里有一个有趣的副作用:
副作用:先检入代码者为王!
如果两个团队正在开发的代码互相冲突,后一个检入的团队必须负责解决冲突问题。这是一个好的副作用,因为它可以鼓励团队尽早检入代码。:o)
下面是一个完整Sprint的示例图:
两个团队进行了一次6天的sprint。团队A准备实现“预订”和“取消”。团部B准备实现“发票”和“黑名单”。让我们看看发生了什么。
天数序号 | 团队A视角 | 团队B视角 | 主干视角 |
1 | 从主干合并代码。没有新内容。开始开发“预订”,将工作签入自己的工作分支。 | 从主干合并代码。没有新内容。开始开发“发票”,将工作签入自己的工作分支。 | 今日无事发生。 |
2 | 从主干合并代码。没有新内容。完成“预订”的实现。通过集成测试。搞定了!复制到主干。开始开发“取消”。 | 与昨日相同。 | “预订”现在完成了! |
3 | 从主干合并代码。没有新内容。仍在开发“取消”。 | 从主干合并代码。啊哈!有变更了!添加了“预订”相关的内容。在团队B分支中合并我们的代码,解决任何发生的冲突。然后继续开发“发票”。 | 今日无事发生。 |
4 | 与昨日相同。 | 从主干合并代码。没有新内容。完成“发票”的实现。通过(包括“预订”在内的)集成测试,< font color="#008080">复制到主干。开始开发“黑名单”。< /td> | “发票”现在完成了!. |
5 | 从主干合并代码。啊哈!有变更了!添加了“发票”相关的内容。在团队A分支中合并我们的代码,解决任何发生的冲突。然后继续开发“取消”。< /small> | 从主干合并代码。没有新内容。仍在开发“黑名单”。 | 今日无事发生。 |
6 | 从主干合并代码。没有新内容。完成“取消”的实现,复制到主干. | 与昨日相同。 | “取消”现在完成了! |
Sprint完成了!除“黑名单”之外,所有的故事都完成了。但是没关系,我们还是可以发布的!因为我们是以增量的方式完成合并与集成的工作。如果我们等到sprint结束再做,任何分叉的代码就会在错误的时刻发现——此时我们能够用来修复问题的时间最少。
发布分支
假定我们完成了sprint 1并发布了系统的1.0.0版本。现在,我们在进行sprint 2之中的工作时,有人报告说之前发布的版本中发现一个严重的缺陷!不!我们该怎么办?
最简单的方式是:在主干上修复该问题,并发布1.1.0版本。这就是说在sprint 2中任何新实现的故事都会包括在新发布版本中。理论上来说,这样做没有问题;因为主干是“完成”分支,而“完成”的定义就是“可发布的”。所以主干上的内容无论何时都是我们要发布的东西。
不过还是会有一些原因让我们不想马上发布新故事。例如:
- 发现了严重的缺陷,实质上意味着主干在发布时就已经有问题了。也就是说sprint 2的故事都是在一个有问题的基础上构建的。在必须处理新的故事之前,我们会想要修复这个基础。
- 也许利益相关者不希望在sprint当中发布新的功能。
- 从主干中发布包含新功能和全部已有功能的新版本,也许需要一段时间才能完成;所以我们需要一个“hotfix”机制来更快地修复问题。
我们具体该如何做呢?
- 创建一个名为“发布1.0”的发布分支,基于它在发布时的主干内容。
- 在发布分支上针对缺陷打补丁。
- 在发布之后,马上将发布分支合并到主干之上(这样补丁程序就会包含在未来的发布版本之中)。
注意我们在发布1.0.0版本时没有必要创建“发布1.0”分支,可以等到问题出现时再做。这样以来,除非真的需要建立某个分支以让我们对其做些什么,我们就不必创建额外的分支。
大图景
好,现在我已经给出了如何使用该模式的详细范例。让我们往后站一点儿,看看整个的图景是什么样子吧。
在主线模型中,一个分支被称为一条代码线(实际上,分支可以被人物是一条代码线的实现)。有时这些被成为流。
一条代码线的上级(也就是该代码线的起源代码线)被称为它的基线。主线是没有基线的代码线。
所以在上面的例子中,我们可以总结出:
- 主干是我们的主线。它不就是没有上级么?
- 其他所有代码线(发布版本1.0,团队A的工作分支,团队B的工作分支)都以主干作为基线。
下面是一个更复杂的例子:
这个图告诉我们:
- 项目X的代码线衍生自主线。该项目目前已完成,所以分支就结束了。
- 团队A有一个衍生自主线的活跃工作分支。
- 团队A还有一个衍生自工作分支的、正在进行中的Spike(译注:Spike是指团队集中精力在短时间内尝试实现一个功能的活动。)。
- 发布版本2.3已关闭,因为2.3已经从生产系统中撤出而且不再会被维护。
每条代码线有一个相对其基线的坚固水平,也就是说每条代码线要不就比其基线更坚固,要不就不及其基线坚固(更柔软)。
- 一条坚固的代码线是稳定的,通过测试的,很少变更而且临近发布。
- 一条柔软的代码线不稳定,很少测试,经常变更而且远离发布。
在绘制代码线时,坚固的代码线分支向上,而柔软的代码线分支向下。观察上图,我们可以总结出:
- 发布版本2.3比主线更坚固。
- 团队A的工作分支比主线更柔软。
- 团队A的spike比团队A的工作分支更柔软。
使用上述描述绘制图表,对于展示分支历史来说很有用;但是如果同时有很多分支存在,就可能带来混乱。下面是一个更清晰的格式,仅展示了当前存在的代码线以及它们的衍生出处。
我建议以此格式绘制你的分支图,而且可以将其挂到团队所在房间的墙上。在讨论集成问题时参考此图真的很有帮助。
所有的变更都应沿着所在的线索发展,这是一条很重要的规则!所以不能直接从团队A的工作分支向团队B的工作分支中合并代码,这会导致很多混乱。实际上,在团队A工作分支中发生的变更应该流回到主线中,再向下进入到团队B的工作分支中。
任何位于主线之上的代码线都可以称为发布代码线,意味着一个比主线更坚固的代码线。
任何位于主线之下的代码线都可以称为开发中代码线(或工作代码线),意味着一个比主线更柔软的代码线。
协作黄金规则:
-总是接受稳定的变更。
-绝不强制使用会导致不稳定的变更。
那么这对于不同类型的代码线意味着什么呢?
上图以彩色的方式说明了:
- 任何时候在发布代码线上的变更,都应该立即合并到其基线中,并发布到主线上。
- 例:在2.4.2版本中修复了一个bug。这应该立即合并到2.4版本中,并将其合并到主线上。
- 一个发布代码线永远不要从其基线接受变更。
- 例:新的代码检入到主线中。该变更不应进入2.3版本和2.4版本。
- 变更应持续从基线流入到开发代码线中。
- 例:任何针对主线的变更应该迅速向下流入到团队A和团队B中,并从团队A向下流入团队A的spike中。
- 开发代码线的变更只有处于稳定点时才能发布到基线中。
- 例:团队B只有在一个故事完成并通过测试后才能向主线合并变更。
无论变更何时应用到代码线及其基线,有些合并是必须要做的。因为代码合并是一个很容易犯错误的操作,我们希望在两条代码线中稍柔软的一条上进行。一旦合并完成而且通过检验,我们就可以将合并的代码复制回更坚固的代码线。
将坚固代码线比柔软代码线绘制得更高,使用这个惯例,我们可以推出一个简单的规则:
规则: 向下合并,向上复制
示例:团队A注意到主线已经更新了。他们将变更向下合并到自己的开发代码线,并修复任何冲突。只要团队A的开发代码线达到稳定的一个点,他们就可以将代码复制回主线。当然,他们必须检查同时主线上没有发生任何变更。
模型的变种
“版本控制模式”一节描述了一个如何实施主线模型的范例。
“大图景”一节以更通用的方式描述了主线模型。
本节中,我将针对如何实施该模式提出一些典型的变种。
“完成”的定义不必一定是“可发布的”。
先确定“完成”一词的任何定义,然后确保有一个分支可以容纳根据该定义已经“完成”的故事。不过还是要注意别遗漏重要的内容。如果集成测试没有包含在“完成”中,那什么时候进行集成测试呢?
主干不必是主线
这个模式需要一条主线才能进行下去,不过不必是主干(虽然在大多数情况下,使用主干是很自然的选择)。
团队不一定必须有自己的分支
当然可以有多个团队共享同一分支,甚至直接在主线上展开工作。只要保证遵循分支的方针即可。
通常,团队希望有自己的分支,以避免未完成的故事在多个团队之间造成干扰。
不必每次都创建一个全新的发布分支
可以使用同样的发布分支,而不是在每个sprint结束后都创建新分支。那个分支可以称为“最新生产系统”或其他类似的名字。如果在生产系统中总是保持只有一个版本,这当然是很好的模型。
不必在每个sprint结束后都进行发布
可以在每个故事完成后进行发布。或者每三个sprint完成后发布一次。确定你自己的步伐。
不必针对每个故事都运行回归测试
如果在你的环境中,回归测试或集成确实很难实际操作,那么可以在sprint接近尾声时进行。这样就可以针对一批故事进行测试和集成的工作。你自己要承担相关风险。如果回归测试和集成属于“完成”的定义,这可能意味着你在sprint末尾时可能会遇到问题,导致没有任何故事完成的风险。
FAQ
持续集成(CI)在这个模式中如何使用?
CI服务器应该针对哪个分支运行?这要根据具体情况,不过下面的叙述是一个好的起点。
假定主干的方针是“完成而且可发布”,而每个工作分支的方针是“通过单元测试”:
- 对每个工作分支来说,CI服务器自动并持续地检查构建和运行所有单元测试的状况。
- 如果有任何失败,就给出一个红色警告。让机器冒烟……
- 对每个工作分支来说,CI服务器自动并有规律地(如果不能持续地)运行集成测试和回归测试。
- 如果有任何失败,就给出一个分离的警告。因为这不是当前分支的方针。
- 当有人考虑从工作分支向主干发布代码时,要触发该手工测试,以检查故事是否“完成”。
- 对主干来说,CI服务器自动并持续地运行集成测试和回归测试。
- 如果有任何失败,就给出一个红色警告。让机器冒烟、触发警笛、USB火箭发射器,再把国民防卫队叫来。
该版本控制模型使用哪种工具最合适?
不确定。我知道Perforce是可以的,我想subversion应该也没有问题,但是对于CVS我不敢打包票。欢迎任何新的建议。
不过要记得一个重要的事情——切换工具的成本要比不能有效协作产生的成本低得多!所以搞清楚你想怎么做,然后找到合适的工具来支持。
与用户故事无关的检入怎么处理?
不是所有的代码变更都必须与某个用户故事相关的,在例子中,我只是为了描述的清晰才这样做。无论检入何种类型的代码(或文档之类),同样的模型也是可用的。
合并代码很麻烦,所以我想做得越少越好!
恭喜,你得了合并恐惧症——对于合并代码的非理性恐惧!合并让你觉得麻烦,是因为做的太少了。合并得越多,痛苦就越少。:o)
我还有更多问题!
看看后面的参考资源!我相信它们可以解决你大部分的问题。
查看英文原文:Version Control for Multiple Agile Teams
参考资源
如果你想了解更多,我强烈推荐下列资源。
《Practical Perforce》
作者Laura Wingerd。这是一个样章,其中讲述了主线模型的大部分内容。
这是一本关于Perforce的书,但是主线模型不仅限于Perforce。
《High level best practices in Software Configuration Management》
由Laura Wingerd和Chirstopher Seiwald合写的关于版本控制的通用最佳实践的摘要,非常有用。
《Branching and merging – an agile perspective》
一篇由Robert Cowham所写的、与本文主题有关的有趣文章。
《The Flow of Change》
又是由Laura Wingerd所作,对于主线模型的上佳摘要文章。“大图景”一节中绝大部分内容基于这些幻灯片。
可下载的PDF
此处为 本文的pdf格式,可供下载。
关于作者
Henrik Kniberg是位于Stockholm的Crisp公司的一名咨询师,专长于Java和敏捷软件开发。他建立了多个瑞典软件公司,而且对于学习、讲授和应用软件开发的艺术充满了热情。Henrik从事了多种类型的工作,并喜欢扮演诸如经理、开发人员、Scrum Master、教师和教练等多种不同角色。Henrik是流行的InfoQ迷你书“Scrum and XP from the Trenches: How We Do Scrum”的作者。