三类代码协同模式,你要如何选?——Git

Git代码协同模式基础

为团队规划研发协同模式之前,有必要了解Git的几种协同模型。
其中多种协同模型中使用了代码评审。代码评审在不同的代码平台有不同的称谓。
  • GitHub 应该是受到了 git 命令 git request-pull的启发,将代码评审称为拉取请求(pull request, PR)。

  • GitLab 因为代码评审最终被合并(也支持变基 rebase)到目的分支,因而称其为合并请求(merge request, MR)。

  • 阿里云·云效的代码平台,代码评审不但用于分支提交变更的评审,也将支持分支创建、分支删除、强制推送行为的评审,因此云效代码平台将代码评审称为变更请求(change request),简记为 CR。


GitHub-Flow:开源社区标配的协同模型

GitHub 在2008年一经推出,就凭借其创造性的“仓库派生 + pull request”的工作流征服了开源社区。这是因为这一协同模式解决了开源社区最大的痛点:仅对少数人开放代码仓库写权限和人人皆可贡献开源的矛盾。

GitHub 工作流的使用

对于开源项目的参与者,可以划分为两类角色:维护者和贡献者。
  • 维护者:具有代码仓库最高权限,可以创建分支、删除分支、更新分支,以及向分支强制推送。

  • 贡献者:没有仓库的写权限,只拥有读权限,不能向代码仓库直接推送。

GitHub 创造了“仓库派生”这一机制,解决了代码仓库的只读用户向代码仓库贡献的途径。我们以 k8s 社区为例,介绍 GitHub 工作流。(图示摘自[1]
GitHub 工作流说明:
  1. 创建派生仓库。说明:作为 k8s 项目的贡献者,没有 k8s 仓库的写权限,但可以通过 GitHub 的 fork 功能将 k8s 项目(kubernetes/kubernetes)派生到自己名下,如仓库 $user/kubernetes。用户对于自己的派生仓库拥有写权限。

  2. 将自己的派生仓库克隆到本地。执行命令:

    • git clone https://github.com/$user/kubernetes.git

  1. 本地仓库和上游仓库分支(如master分支)同步。说明:派生仓库并不能和上游仓库自动同步,为避免本地提交和上游仓库出现过大的偏离导致合并冲突,在每一个功能开发前,以本地仓库为中介,将远程上游仓库的新提交同步到远程的派生仓库中。

    • git remote add upstream https://github.com/kubernetes/kubernetes.git

    • git fetch upstream

    • git switch master

    • git rebase upstream/master

    • git push origin master

  1. 创建一个本地分支(如 mybranch分支)。说明:为每一个开发任务创建一个单独的本地分支是一个好习惯,这样可以在一个本地仓库中,用不同的本地分支关联不同的 pull request。

    • git switch -c mybranch master

  1. 在本地分支中开发。

    • git add <file>

    • git commit -s

  1. 将本地分支推送到远程派生仓库。

    • git push -u -f upstream HEAD

  1. 创建代码评审(pull request)。即:通过 GitHub 的 Web 界面,从远程派生仓库向远程上游仓库创建 pull request。

  2. 本地仓库中新的改动(提交)再次推送到远程的派生仓库,会自动刷新代码评审中的提交。

优势和缺点

GitHub-Flow 的优势:
  1. 满足了开源项目人人皆可贡献的目标。在2008年,仓库派生是基于原生 Git 实现开源协同的最易用的方案。

  2. 易于管理。将代码管理工作化整为零,每个派生仓库由派生者自行管理,开源项目的负责人无需进行复杂的授权管理和分支管理。

  3. 派生仓库是一个独立的拷贝,不会受到上游变更(许可证变更、开放性变更、授权变更等)的影响。
GitHub-Flow 的缺点:
  1. 仓库同步操作复杂。同步操作涉及到远程上游仓库、远程派生仓库、本地克隆仓库的三方数据同步,开发者要熟悉 git remotegit rebase等命令。

  2. 派生熔断导致仓库授权失去管控。在仓库的开放性发生变更(例如从公开项目转换为私有项目),主项目和派生项目之间的派生关系被切断,成为没有派生关系的不同的项目,派生项目的管理权也从主项目自动交接给派生项目的用户。

  3. 不适合于多仓库类型的项目(如 Android 项目)。由于多仓库的存在,放大了派生仓库和上游仓库同步的复杂度。

  4. 派生仓库导致服务端存储出现冗余。

上述缺点,尤其是派生熔断导致仓库授权失去管控,无法被企业管理者所接受。因此,GitHub 工作流只适合于外部开源或者企业内部开源项目,而不适合企业私有项目。


分支评审:仅适用于私有仓库的协同模型

GitHub 的派生工作流会因为仓库可见性从公开变为私有导致派生熔断,造成仓库授权失去管控,因此派生仓库协同模式通常不被企业所接受。进而,被所有代码平台所支持的分支评审模式自然成为企业私有仓库协同的最直观的选择。

分支评审模式的使用

企业私有代码仓库的协同者通常包含三类角色:管理员、开发者、贡献者。
  • 管理员:即仓库的维护者,具有代码仓库最高权限,可以创建分支、删除分支、更新分支,以及向分支强制推送。

  • 开发者:具有仓库的读写权限,除了不具备删除仓库等危险操作的权限之外,和管理员一样,具有创建分支、删除分支、更新分支,以及向分支强制推送的权限。

  • 贡献者:没有仓库的写权限,只拥有读权限。

上述三个角色中,只有开发者和管理者拥有写权限,因此只有这两种角色才能够使用分支评审模式参与代码仓库的协同。
采用分支评审模式协同,开发者在服务端仓库中创建临时分支,将本地提交推送到相应的临时分支上,再从该临时分支创建到目标分支的代码评审。临时分支通常在代码合入后清除。用户在服务端创建的临时分支,根据目标分支的不同,有着不同的名称。如:
  • 特性分支(topic branch):为完成某一功能开发而创建的临时分支,通常基于主干分支创建。不同的项目对于特性分支可能有不同的命名规范,例如:topic/<name>feature/<name>
  • 热修复分支(hotfix branch):为修复已发布的历史版本的缺陷,基于历史发布版本(如 v1.0 版本)创建的分支。不同的项目对于热修复分支可能有不同的命名规范,例如:hotfix/<name>

下面的示意图演示了一个开发者如何通过分支模式将特性分支(如 topic/1)合入到主干分支(如 master)。
说明如下:
  1. 开发者将远程仓库克隆到本地:

    • git clone https://example.com/group/repo.git

  1. 本地创建分支,如 topic/1分支:

    • git switch -c topic/1

  1. 在本地分支中开发。

    • git add <file>

    • git commit -s

  1. 将本地 topic/1分支推送到远程服务器上。

    • git push -u origin HEAD

  1. 开发者访问 Web 界面,选择从 topic/1分支向 master分支创建代码评审(如编号 123 的代码评审CR #123)。

  2. 本地仓库继续修改,创建或更新提交。

    • git add <file>

    • git commit -s

  1. 执行推送命令,自动刷新远程仓库中的代码评审(CR #123)。

    • git push

  1. 代码评审通过,从 Web 界面访问 CR#123,点击合入完成分支 topic/1到分支 master的合入。

  2. 分支 topic/1合入后,可以手工删除该分支。

GitLab 的保护分支功能

保护分支功能最早由 GitLab 实现,目的是确保分支能够严格采用分支评审模式进行协同。
  • 通过输入名称通配符,确保与之匹配的分支被加以保护。

  • 可以设置被保护的分支必须使用代码评审才能够合入,不能直接推送。

  • 可以设置被保护的分支不能够被强制推送,管理员也不例外。

保护分支由管理员进行设置,通常仅对代码仓库的主干分支、维护分支进行保护,而不对特性分支进行保护。

优势和缺点

分支评审模式的优点:
  1. 如果某个特性的开发周期比较长,创建一个长期存在的特性分支,便于开发者能够随时执行推送命令将本地提交推送到服务端,避免本地误操作而丢失数据。

  2. 如果某个特性由多人协同开发,设置一个特性分支,便于多个用户进行工作协同。

分支评审模式的缺点:
  1. 如果开发者无论是否必须、无论特性的大小,都采用分支评审模式,且不能及时删除过时的特性分支,就会在服务端遗留大量过时的分支。仓库中过多的特性分支和仓库的核心分支(主干、维护分支)混杂,会给开发者带来困惑,也会导致用户操作过程的性能下降。

  2. 缺乏有效的分支授权管控,无法限制对于非保护分支的推送、创建、删除权限的管控,可能导致特性分支被误删,或者特性分支因多人协同时强制推送被覆盖,造成代码丢失、协同效率的下降。

分支评审模式的改进

采用 AGit-Flow 协同模式,直接通过命令行创建代码评审,而不在仓库中创建临时分支,可以解决了分支评审模式中特性分支过多、混乱的问题。
至于分支评审模式缺乏对特性分支创建、删除、强制更新的授权,阿里云·云效代码平台提供推送评审模式,实现自助式分支管理,以解决代码仓库中分支管理无序的难题。