看Black Rock 是如何使用代码分支的

来源:互联网 发布:蜘蛛纸牌知乎 编辑:程序博客网 时间:2024/05/18 00:43

Black Rock (《争分夺秒》,《Pure》)核心技术团队成员Julian Adams展开说明代码分支实践的来龙去脉 – 包括他的团队已经应用它们,提高生产率,以及这一方式中的折中点。

动机

这段时间大家都使用版本控制系统(VCS)。像其他游戏开发者一样,我使用的第一套VCS是Visual Source Safe。追朔到上个世纪90年代,它让人难以置信。我们团队中所有五个人共享我们的工作,汇总我们的工作并标注我们的发布。基本上,我们都知道工作进展如何,我们都坐在一起。很少有问题,所有事情总体不错。

甚至在团队增加到20人左右的时候每件事都还好……大部分。现在,就算出现了问题,构建失败,你可能不会知道出问题的那些代码;你甚至可能不知道是谁的代码出了问题。你仍然能够同团队中的任何人自由的协调 – 但有时候你需要同直接开发某个功能的人共享完成一半的代码,这很痛苦。你尝试并确保中途提交不会破坏任何人的工作,但谁不犯错呢?

这些年,游戏开发已经变了。《争分夺秒》在它高峰时有40位程序员。《Pure》有25位以及其他开发核心技术的15名程序员 – 所有人基于同样的代码工作。还有65人在单独的每个项目Perforce支持部门开发资源。

让我们把“破损的”构建看作阻止别人工作的家伙。将最聪明的55个人聚集在干线上,构建还是会因为他们其中某人而断裂。

随着开发团队规模日益扩大,缓解构建失败问题的一种方式是开始加入自动测试来测试构建是否良好,然后当代码被签入时使用持续集成来运行这些测试,得到对构建状态的持续反馈。

如果测试足够快,它们能够由编码员在签入任何代码前运行。如果测试没花时间就覆盖了某个人感兴趣的所有范围,那就是:你永远不会记入有损害的代码,因为你已经针对最新的代码运行了测试,最新的总是好的!

当然,测试从来不能达到100%的覆盖率,因此还是会有通过测试的代码由于某人而受损。

另一方面,测试运行时间(潜在因素)意味着你看到的某个分支的测试结果是过期的。这导致你可能不会在提交前自己运行所有测试,或者针对你合并前更新的代码运行所有测试,但不是最新的合并后的代码。

非零测试时间整合和不完全覆盖整合意味着构建会时常中断。由于更多人签入单一分支,分支在既定时间内存在问题的机率大大的增加了。

通过一个大的代码基础你将有机会了解怎样修复构建失败,或者甚至向谁讯问,降低失败几率。分支解决了这些问题。

术语

之所以谈到VCS,是因为这个术语并不标准。我将要谈到的大多数内容都明显是针对用过VCS的人。我用到的大多数词条是标准的,且是维基百科的版本定义功能之上的定义输入。

分支的术语就不那么清楚。我将要讲到的“提交”,是指在库中存储本地更改,而不让其他人看到;关于“推送”,是指将更改从一个分支上发布到另一个分支上。

分支是什么?

分支是VCS中的一组文件,相较于主线而被更改了,并独立进化。不同于很多人能从中开展工作并推送至分支的工作目录(对它来说也是个事实)。

图 1 分支从主线中分离并同主线合并

分支能带给你什么?

分支让你重新回到较小的团队开发。你直接同5-10人一起工作。现在你只看到同你正在从事的工作相关联的更改,相关的测试构建失败,并有个小团队持续跟踪。你就可以只同直接工作伙伴共享代码,在牢固的基础上开发很酷的新功能。待它稳定后同主线相合并,并将你的结果发送给其他团队。

对主线会有什么好处?

反过来说,当团队稳定了他们的分支然后将其推送至主线,主线会变得更为稳定。再强调一次,这只不过是个数字问题。不是70个人向主线推送代码,也许是7个团队向主线推送代码。这立即减少了要在主线中更改代码的几率。这种代码同时已经经过测试并在分支中运行稳定,所以它减少了问题的产生。

这不是分支而是合并

至此,我已经说了很多关于分支的问题,但是分支总归是简单的。Visual Source Safe能够分支,CVS能够分支。从另一方面说,合并才难以置信的困难,不同于这里讨论的普通的工作流。

这段时间,很多VCS让分支和合并像本地更改一样方便。从这个角度看:你所做的每个本地更改实际上都是一个分支。合并你的工作目录就是合并一个分支,提交更改就是接合你的本地工作目录(分支)和主线。分支和合并应该这样简单,如今在很多VCS中就是这样。

分布式VCS(DVCS)在过去两年间已经成为VCS主流趋势。你也许已经听说过Git 和 Mercurial。简而言之,这些系统的理念在于每个分支都是一个完整的库,因此,除了从非本地分支中进行更新外,版本控制功能不用网络连接即可使用。就像DVCS更擅长合并或者它不会有那么多用户。

Mercurial有一个出色的在线教程。如果你没有使用过DVCS系统,那很值得试一试这个。它是命令行驱动方式,其简单性正在于此,如果你想要的话,还可以用GUI。如果你的VCS不能很好的进行分支和合并,也许是时候看看这个备选方案了,或者联系厂商!

在这个阶段,你也许觉得对游戏资源来说,让每个分支都带有库副本不切实际。这是事实!我不是在建议你随便找个系统然后开始用,但是可随意使用它们,显示分支层次,在美术阶段提供合并支持。虽然我给出的例子是分布式系统,集中系统同样能提供好的分支支持。我们用Accurev。

最佳实践

图 2 每个团队的分支

当我们开始了,我们选择简单的分支编排。工作室中每个团队一个分支,如果团队很大,就为他们的亚团队分出子分支。实际上这非常有用。每个分支上的每个团队都紧密相连,集中精力,这是成功的大部分因素。

每个团队都能够为极其不同的项目使用同样的核心数据库。在过去的两年中,我们团队分别开发《Pure》,《争分夺秒》和技术开发本身。

开诚布公的说,我们有很多测试都是在服务器上运行的;从单位测试到模块测试到游戏测试,还有一个反馈系统,以便持续跟踪分支。

不幸的是,某些完整游戏测试很慢(总共要好几个小时),这影响了我们对分支的使用。如果有时候测试在一个团队的分支中失败。这没问题,我们修复它。

如果你尝试在那个时间合并就很不方便。是什么导致构建失败:合并还是你的开发更改?我们在一对分支中使用“Airlock”系统来解决这个问题。合并分支脱离于母分支,团队开发分支独立于合并分支。

图 3 开发和合并分之中的“Airlock”配对

通过这种编排,我们只需要向通过全部测试的合并分支中推送事项处理。我们在合并分支中看到的任何测试失败都是合并导致的。这一合并分支总是稳定的,我们使用它来跟踪母分支,递增合并更改上层和下层。

随着团队使用分支的经验逐渐增长,我们已经看到了团队开始为单独功能建立新的分支。从很多方面来说,这都是开发的完美方式:在任何指定阶段只同你的直接工作伙伴协作。

在开发X功能前你甚至不用为此建立分支。只是在你的团队分支下开展工作目录中的开发工作。如果或者当你开始就X同其他同事协作时,把你的工作目录转向它,你的同事也同样这么做。

图4 功能分支

这种灵活性在实践中简直好极了,尽管它也许会突出底层开发中其他没有同样灵活性的领域。对我们来说,为新分支建立测试要比建立分支花费更多时间,这是我们要在未来改进的。

对于《争分夺秒》开发,我们从中看到了分支的结构是用于当前的开发状态。在Accurev和很多DVCS中,能够容易地移动分支和工作目录,因此他们有了新的母分支。

《争分夺秒》的开发从一个小团队开始,以单一主线分支为单位进行工作。一旦项目发展到完整制作阶段,开发团队就被分成了9个分支。从实践上来说,对于一个单一工作组分支好像太多了,由于在主线中看到的更改数量和破损。这些开发分支归于两组合并分支之中(合并1和合并2),合并分支归于主线之下。

图5 使用合并分支开发

当项目功能完备并进入最后的bug修复和调整阶段时,合并1和合并2被撤除,所有分支直接归属于主线。这时候需要合并的代码少了很多。这就要必须要更直接的关联于主线。

图 6 Bug 修复直接针对开发分支

对于长时间运行的分支,更改将从其中推送至主线,这就有了合并时间的问题。直到你希望推送至主线才会要求进行合并,因此,也许这是合并分支之间冲突的唯一时刻。

在实践中,这就像赌博。实际上这就是,恰好在功能交付的这一时刻进行未知工作量的合并。实际工作中,我们发现最好时刻注意冲突(不用实际合并,你的VCS应该能够显示这项)并正则合并。

由于对其涵盖的两个分支的稳定性都有把握,我们能够马上发现合并冲突,这让我们占据优势。正则合并能够推送更多的正则内容至主线,这将推送内容变得更小也更容易合并。这跟持续集成原理相同。

从这我们可以很清楚的看出,跨越基于同一个库的不同分支的团队管理存在普遍现象。如果另外一个团队几个月都致力于总体架构更改然后将它们推送至主线,那么让一个团队合并并推送就没有好处了。

在单一游戏开发团队中同样都有强大的技术和项目管理组织以保证信息流通能够准确到达。尽管如此,跨越多个游戏团队和技术团队,就不会有组织和群体基础能够跨分支共享代码。

游戏是独立的个体,通常必然会以此为单位来进行管理。如果几个游戏开发团队使用一个给定的数据库,就会在团队之间针对数据库产生分歧。我们解决这一问题的方法是,保证工作室中任何等级的同事之间定期进行面对面的交流。举个例子,所有工具开发人员每周开一次会讨论当前工作和未来规划。我们有一个小组跨越工作室来寻找每个专业领域中的问题,同时我们在合并期间力争将冲突降至最低。

必须要有所决断

会有一些权衡。最好能考虑到,如果可能的话提前考虑备选方案,这很重要。第一是必须通过分支进行合并。没有分支,每个开发者要将他/她的更改推送至主线。当冲突产生时,每个开发者了解他们更改的目的所在。如果工作室文化中的群体概念是健康的,那么他们也更愿意了解为什么存在冲突。

分支需要同主线合并以补充普通合并。必须有人来做这个,并且那个人可能不是进行冲突更改的人。在任何既定时间,一个人负责一个分支的合并。我们通常每周以循环方式轮换。对于合并工作,如果合并不能完全显现,我们鼓励负责合并的人去抓那个做原始更改的人。这种方式不够完美,并会有一些缺陷,但这在实践中就足够了。

责任是关键所在:需要有人对每个分支负责,测试它,解决任何问题,推送更改至母分支。如果不这样做,分支就不会成为传播更改的方式。尽管我们几乎总是共享各个分支的所有权,但是有一个人承担这样的责任同样有效。

总体来说,在主线中输入更少,更大的变更,向分支中的推送就越简单。尽管如此,这个结论还是在某一特定时间下是错误的:在里程碑中。这里每个人都要在里程碑中更改。时间通常很短。如果每个人都等着慢速测试在合并分支中通过,那么这要比所期望的上推更改时间长。

候补策略是为了几乎同步地从众多分支中上推更改。这些更改都在它们的分支中经过单独测试。它们之间的合并在推送至主线前并未完整测试过。在主线上更改更为快速,然而更改更可能在初始状态下让游戏开发断裂。这种情况并不是个问题:合并里程碑功能的时间降低了,在短期内的交付架构断裂并不会阻碍开发团队在分支上的工作。

任何工作流上的二进制文件都是不可忽略的,它依赖分支间的平行编辑和文本合并。这些文件不能被合并,因此一旦发生冲突,你必须选择一项或者另外一项更改。传统上,当在单一分支上开发时,这些文件已经建立了,所以你必须在编辑它之前得到排它锁。不要求合并。

伴随多重分支你能否有一个应用于多重分支的锁?是的,但是如果编辑了一个文件,而你想要在另外一个中的排它锁,会发生什么?你要编辑什么版本?

在分支中编辑这个版本就像你想要的。尽管你有了排它锁,这还是会带来冲突。刨除分支中剩余文件而从这个分支中带入更改是不正确的,因为任何给定分支中的更改通常是相互依存的。我还不知道有哪个VCS应用了这类解决方案。通常应用候补都只是针对单一分支的排它锁。一旦两个分支之间在合并工程中产生了冲突,就丢弃一个文档。

为测试文件,我们只是去接受。这些文件支持我们运行的测试且合并不到位,这并不是痛处。我们在单独的Perforce库中存储游戏资源。这一设置一直保持进化。我们不会对这些资源进行分支,并使用排它锁。

最初在Accurey中分支代码,在Perforce中为资源创建单一分支,会让美术很难自由地相互协调,对程序人员也一样。Perforce中的开发资源不会同Accurev中所有分支的代码充分融合。架构经常断裂。

为协调Perforce中的资源修订和Accurev中的修订我们现在将每个顶端目录编排入Perforce对应Accurev中的单一分支。在Accurev中,我们跟踪Perforce目录中文本文件形式的已知好修正。因为这些是文本文件,他们能容易的合并就像在分支间推送。

图7 代码分支对应的相关目录

当你从Perforce中获取新的资源,我们的工具为你现在所在的Accurev分支提取最新版本的资源,以及其他每项内容的已知好版本。当代码从Accurev分支中推送出来时,我们使用脚本来自动更新基于测试得到的已知好的Perforce修正。这一设置让我们在工作中使用Accure中的多重分支并同美工自由的合作。它戏剧性的改进了整个工作室的工作流。

这一系统的一个弱点是,资源必须为单一分支所有,不能在多重分支钟编辑。由于这一系统在我们建立资源结构后得到了进化,我们也有多个团队想要编辑文件的情况发生。比如游戏性团队和车辆团队都想更改汽车设置的比率。最后我们重新组织了数据来解决这个争端。

未来

使用VCS系统,让分支和合并变得灵活多变,简单适用,这对Black Rock来说是个大成功。还有很多问题我们想要持续改进,这将进一步改善我们的工作流。我希望研究如何将所有事项整合在一个VCS中。在两个VCS中跟踪修正让事情变得复杂了。在Black Rock,这种编排已经显露苗头了,也许我们能让单一系统得到加强。

我们的游戏适当的允许丢包或过期资源。游戏中不相容数据和缺乏容错控制由于需求代码贯穿分支仍然会让架构断裂。我们打算让游戏负载的全部内容达到前后兼容,所有系统都能适当的处理载入失败。这将让每位致力于游戏开发的成员得到显著的经验增长。

虽然分支增强了主线的稳定性,但是它同样增加了更改到达主线的时间。从主线中获取带有全部新功能的架构非常困难;有时候滞后性甚至是1到2周。一旦他们已经完成并经过测试,我们想让它更为顺畅和容易的将功能推送至主线来降低滞后性。

我们持续增加这我们架构和测试系统的灵活性以便让它们更容易的操作即席分支,并最为有效的使用编译伺服器。这将大大减轻目前仍存在的即席分支障碍。我确定在两年内,当我们完成另外一款游戏时,会有更多内容分享。

鸣谢
感谢Tim Swan, Jez Moore, Jim Callin, Neil Hutchinson, Rory Driscoll, Tom Williams, Steve Uphill 和 Shawn Hargreaves对这篇文章给出了宝贵的评论和反馈。

法律声明:IIEEG专稿,作品受《著作权法》保护,转载请注明出处

原创粉丝点击