本文作者向华是资深游戏开发工程师,拥有8年游戏测试开发经验。他是前原神项目P4 Admin,也是一名持续集成开发者。
作为Perforce Helix Core的用户,他结合自身项目实践经验,带来关于解决文件合并冲突的实操干货。
立即联系Perforce授权合作伙伴——龙智,获得更多关于Perforce Helix Core的咨询、试用、服务等信息。
接上文 P4Python:合并实践指南之如何脚本化integrate流程 提到合并的第二个关键步骤是 Resolve & Submit,Resolve 是做什么呢?简单讲就是解决冲突。
接下来,希望这篇文章能帮你捋清楚这是怎么一回事。
冲突的发生原因
有两种常见情况会导致冲突的发生:一是当你尝试从一个分支合并文件到另一个分支时;二是当你 check out 了一个文件并做了一定的修改,在提交前,另一个人已对该文件进行修改并完成了提交。
这里提到的修改,可能是文件内容上的,也可能是文件属性/类型上的,当然也包括更改文件名或新增和删除文件这类变动。
当冲突发生后,P4 会要求你必须解决这些冲突,才能允许提交。
冲突解决的 3 种原子性方式
与其他 VCS 类似,P4 针对 Resolve Conflict 有三种原子性方式:
Accept Source:目标文件将会被覆盖成源头文件的模样。
Accept Target:目标文件将不变。
Accept Merged:如果没有冲突,将会合并源头文件和目标文件之间的变化,最终形态是一个大融合的结果。
在用 P4V 的朋友应该了解,右键点击带有冲突标记的文件-选择 Resolve 后,会弹出下图对话框。这里能够可以看到这 3 种 Resolve 方式。
用实例说明几种典型场景
先来假设一下分支模型,假设我们有两个分支,主干 Main,发布分支 Rel。
这是一个最小开发模型,在 Main 上开发,在 Rel 上发布。
实例一:Rel 修复了一个bug,需要合并该修复到 Main。
所有在 Rel 上的提交都需要合并回 Main。理想情况下,所有在 Rel 上的变更都需要及时合并回 Main。这里说的及时,是指提交 Rel 后的下一秒就 Merge 回 Main。
但现实往往并非如此,提交者提交到 Rel后,会因为其他事务搁置合并操作,比如被叫去解决更大的 bug 或者参加会议。
这样,就会有很大概率,在 Rel 上因修复 bug 而改动的文件在 Main 上已经被其他开发新功能的同事提交了多次改动。
合并时遵循以下策略:
如果没有冲突,合并时选择Accept Merged。
如果存在冲突,打开 Merge Tool,进行冲突解决操作。
如下图,Rel#2 到 Main#4 是通过 Accept Merged 合并过来,从结果上来看,Rel#4 融合了 Rel#2 和 Main#3 的改动。
Rel 上出现了这个 bug,Main 上一定也是有的,但修复方式可以不同。
线上的 Bug 修复通常是紧急临时、没有通过深思熟虑的,在 Merge Down 回 Main 时,甚至可以抛弃线上的临时修复方式,在 Main 上经过更长时间的准备以完成更稳妥的修复。
这种情况通常会灵活对待,比如 Resolve 可以选择 Accept Target,并在此基础上,对 Main 进行进一步的修复。
如下图,Rel#3 到 Main#5 是通过 Accept Target 合并过来,从结果上来看,Rel#3 被忽略了(Ignored)。
实例二:Main 上制作了新功能,需要发布到 Rel。
对于游戏业务,都有周期性更新的场景。
周期性更新的内容如果包含了线上未有的内容,此时就需要从开发分支集成进发布分支。
大多数情况,集成方式都选择 P4 Copy。
由于 P4 Copy 相当于 Accept Source 操作,会把 Rel 上的文件覆盖成 Main 上对应文件的模样,这里有一定风险。
对于新功能新模块的外放,大多数情况下可以放心 Copy。
如下图,Rel#1 和 Rel#5 分别是 Main#1 和 Main#7 Copy Up 而来,从结果上来看,Rel 上的文件此时完全等同于 Main 上对应的文件。一次完整发布就结束了。
但正如上文所述,Copy 的结果会将文件覆盖,操作前可以仍需观察前一个合并动作是什么,辅助集成的最终决策。
继续看上图,Main#6 到 Rel#4 是一次 Merge 操作,原因应该是暂时不能将 Main#2 和 Main#3 的内容发布到 Rel 上。直到时机成熟(比如周版本外放时间),Main#7 可以携带 Main#2 和 Main#3 的变更内容一起发布到 Rel,最终形成了 Rel#5。
这些细节,就需要提交者通过 Diff 判定 Copy 过去后,内容是否符合预期,再完成提交。再次提醒,很多线上问题是携带了不该外放的内容而引发的。
就如图所示的 Main#2 和 Main#3 在 Main#7 之前都是未完成内容,无法直接 Copy Up 到 Rel。
实例三:尝试合并的 Revison 比最后一次完成合并的 Revison 要低。
有时候,我们可能会调整外放节奏(很多情况下是形势所迫),将一个尚未开发完成的功能模块先外放出去一部分上线。
此时就需要翻旧账了,找到过去的一次提交,将那次提交进行合并。
但这里要注意了,相关的文件可能在后面已经参与过合并很多次,当然这些合并都是小心翼翼的 Merge 操作。
继续看实例二的最后一张图,最后䘣完成合并的 Revision 是 Main#6,现在聚焦 Main#2 和 Main#3,我们需要提前外放 Main#2 这一部分的改动。
先考虑一下 Merge 后的结果,会在 Rel 上生成一个新的版本,这里是 Rel#5。Rel#5 需要是 Rel#4 和 Rel#2 的融合版本。
于是这个情形与案例一是类似的,中间的 Rel#4~6 的内容都能有所保留,只需要提交者打开 Merge Tool 进行谨慎选择。
别忘了 Diff。
结语
在本篇,通过实例,主要介绍了 P4 Resolve 的一些常见逻辑。
另外我们多次借助过去文章中提到的 P4V 内置工具 Revision Graph,来进行集成决策。
强大的 RevisionGraph 都有哪些方便的功能,我们未来再讲。
本篇结束。感谢阅读。
如需免费试用Perforce Helix Core,请立即联系Perforce授权合作伙伴——龙智:电话:400-775-5506邮箱:[email protected]