拉取请求

2021年1月28日

拉取请求是 GitHub 推广的一种机制,用于帮助促进工作的合并,特别是在开源项目中。贡献者在中央仓库的分支(克隆)中进行贡献。一旦他们的贡献完成,他们就会创建一个拉取请求,通知中央仓库的所有者他们的工作已准备好合并到主线中。工具支持并鼓励在接受请求之前对贡献进行代码审查。拉取请求已在软件开发中得到广泛使用,但批评者担心会增加集成摩擦,这可能会阻止持续集成。

拉取请求本质上为许多开源项目中存在的开发工作流程提供了便捷的工具,特别是那些使用分布式源代码控制系统(如 Git)的项目。此工作流程从贡献者创建一个新的逻辑分支开始,无论是从中央仓库开始一个新的分支,克隆到个人仓库,还是两者兼而有之。然后,贡献者在该分支上工作,通常以功能分支的方式,将来自主线的任何更新拉入他们的分支。当他们完成时,他们会与中央仓库的维护者进行沟通,表明他们已完成,并附上对他们提交的引用。此引用可以是需要集成的分支的 URL,也可以是电子邮件中的一组补丁。

一旦维护者收到消息,她就可以检查提交,以决定它们是否已准备好进入主线。如果不是,她可以建议贡献者进行更改,然后贡献者有机会调整他们的提交。一旦一切正常,维护者就可以合并,无论是使用常规合并/变基,还是应用来自最终电子邮件的补丁。

GitHub 的拉取请求机制使此流程变得更加容易。它通过其分支机制跟踪克隆,并自动创建一个消息线程来讨论拉取请求,以及处理审查工作流程中各个步骤的行为。这些便利是 GitHub 成功的关键因素,并导致“拉取请求”成为开发人员词汇表中的一个基本部分。

这就是拉取请求的工作原理,但我们应该使用它们吗?如果使用,应该如何使用?为了回答这个问题,我喜欢从机制本身后退一步,思考它在源代码管理工作流程中的工作原理。为了帮助我思考这个问题,我写了一系列管理源代码分支的模式。我发现理解这些模式(特别是基础模式和集成模式)可以阐明拉取请求的作用。

从这些模式的角度来看,拉取请求是一种机制,旨在实现功能分支集成前审查的组合。因此,要评估拉取请求的有用性,我们首先需要考虑这些模式在我们的情况下是否适用。与大多数模式一样,它们有时很有价值,有时很麻烦——我们必须根据我们的具体情况来检查它们。功能分支是一种将逻辑贡献打包在一起的好方法,以便可以将其作为单个单元进行评估、接受或延迟。当贡献者不受信任直接提交到主线时,这很有意义。但功能分支也有一定的成本,即它通常会限制集成频率,导致合并复杂化并阻止重构。集成前审查提供了一个明确的地方进行代码审查,但代价是集成摩擦显著增加。[1]

这是一个对情况的概括性总结(我需要更多文字才能在功能分支文章中进一步解释这一点),但归根结底,这些模式的价值,以及拉取请求的价值,主要取决于团队的社会结构。有些团队在使用拉取请求时工作得更好,而有些团队则会发现拉取请求严重拖累了效率。我怀疑,由于拉取请求如此受欢迎,许多团队默认使用它们,而实际上它们不使用它们会更好。

虽然拉取请求是为功能分支而构建的,但团队可以在持续集成环境中使用它们。为此,他们需要确保拉取请求足够小,并且团队响应速度足够快,以遵循 CI 的经验法则,即每个人每天至少进行一次主线集成。(我应该提醒大家,主线集成不仅仅是将当前主线合并到功能分支中)。使用发布/展示/询问分类可以成为将拉取请求集成到更适合 CI 的工作流程中的有效方法。

拉取请求的广泛使用促进了代码审查的更广泛使用,因为拉取请求为集成前审查提供了一个明确的点,以及鼓励进行代码审查的工具。代码审查是一件好事,但我们必须记住,拉取请求并不是我们唯一可以用来进行代码审查的机制。许多团队发现结对编程提供的持续审查非常有价值。为了避免降低集成频率,我们可以通过多种方式进行集成后代码审查。一个正式流程可以记录对每个提交的审查,或者技术负责人可以每隔几天检查一次有风险的提交。也许最强大的代码审查形式是经常被忽略的一种。一个将代码库视为一个流体系统、一个可以通过反复迭代不断改进的系统的团队,每次开发人员查看现有代码时都会进行精炼代码审查。我经常听到人们说拉取请求是必要的,因为没有它们你就无法进行代码审查——这是胡说八道。集成前代码审查只是进行代码审查的一种方式,对于许多团队来说,它并不是最佳选择。

致谢

Chris Ford、Dan Mutton、Jeremy Huiskamp、Kief Morris、Pramod Sadalage 和 Ryan Boucher 在我们内部邮件列表上对本文草稿发表了评论。

注释

1: 我的一位同事最近计算了一位客户在等待没有评论的拉取请求(占 91%)上花费的时间。2020 年等待 7000 个 PR 的总时间为 130,000 小时。此数字包括夜间和周末过去的时间。