本文作者 | 阿里云云效高级技术专家:蒋鑫(花名:知忧)

GitHub 和Gerrit 都是诞生于 2008 年的代码平台,两个平台各自形成了相互独立的生态。GitHub 及其模仿者们成为行业主流,托管着大多数开源项目和商业项目的源代码,而Gerrit 也有一众“粉丝”,像大名鼎鼎的安卓(Android)、OpenStack、Golang等。虽然 GitHub 和 Gerrit 都是Git 仓库的托管和研发协同平台,但是二者背后的技术大相径庭。采用 GitHub 模式的代码平台的后端使用原生Git(cgit)实现,而Gerrit 则采用 JGit(用 java 重新实现 Git 接口)实现。再有两者理念不同,一种采用分布式协同,一种是集中式协同。二者的详细对比参考下面的表格:

我们可以看出 GitHub 模式和 Gerrit 模式各有优劣。那么能否有两全其美的代码平台呢?Git 2.29 让这成为可能。

1. Git 2.29 的新功能,让 Git “牵手” Gerrit

Git 2.29.0 于2020年10月发布,其中包含了两个阿里巴巴贡献的新特性。阿里巴巴贡献的新特性让 Git 牵手 Gerrit,让GitHub 模式的代码平台可以像 Gerrit 一样工作。

1.1 服务端新钩子 proc-receive

Git 2.29 在服务端增加了 proc-receive 钩子。对Git原理熟悉的用户可能知道 Git 服务端经常使用的两个钩子:pre-receive 和 post-receive,在 git 推送时服务端运行的这两个钩子会进行前置检查(授权检查等)和后处理(发送通知、触发构建等)。而新引入的 proc-receive 钩子的执行顺序,是介于这两个钩子之间,用于替代 Git 内置功能完成分支(引用)的更新操作。这个新钩子提供给 Git 代码平台更为强大的服务端定制能力,存在丰富的想象空间。

例如:一个用户使用如下命令向服务端推送:

git push originHEAD:refs/for/master

服务端如果是 Git 2.29 之前的版本,会直接在服务端仓库中创建名为 refs/for/master 的引用。而 Git 2.29  版本引入的 proc-receive 钩子,会接管 Git 更新引用的操作。proc-receive 钩子能做什么,完全取决于开发者的想象:

  • 创建一个代码评审,并在仓库中产生名为 refs/pull/123/head 的引用,便于用户下载相关代码。

  • 或者,推送包含的每一个提交都产生一个独立的代码评审,就像 Gerrit 那样。每个评审都产生类似 refs/changes/ 的引用。

  • 或者,不在服务端产生任何引用,而是将用户新增提交以邮件方式发到邮件列表,类似 GitGitGadget [1] 那样。

那么如何能开发一个proc-receive 钩子呢?相比 pre-receive 和 post-receive 钩子,proc-receive 钩子实现难度稍微大一些,因为它和 Git 服务端程序 git-receive-pack 有着复杂的双向通讯:服务端程序调用钩子,将 git push 的命令以及 push-options(如果有的话)发送给钩子,然后钩子调用 API 替代 Git 完成引用的更新。如下图所示:

1.2 客户端新能力 report-status-v2

阿里巴巴在给 Git 社区贡献的第一个版本中,只在服务端引入新的钩子,并未修改客户端相关代码。为了能让社区接受修改,我以实现 Gerrit 的类似功能作为卖点向社区进行“推销”。Junio(Git 维护者,Google)第一时间承认这个贡献的价值:

And I think it isreasonable to add a new hook that takes over the whole flow in "gitreceive-pack" to do so.

同时指出疑问:向 Gerrit 推送一个引用A (refs/for/master),Gerrit 创建了另外的引用B (refs/changes/1/123),那么 Gerrit 是如何告诉客户端正确地更新本地跟踪分支的?

How do Gerrit folksdeal with the "we pushed to the server, so let's pretend to have turnedaround and fetched from the same server immediately after doing so"client-side hack, by the way?

只有屈指可数的人才能像 Junio 这样发出灵魂的拷问!

于是在后续的代码评审中,与 Junio 以及 GitHub 的 Jeff King (Peff) 之间进行了多次交流,代码迭代了19个版本[2],为 Git 服务端和客户端新增了一个能力:report-status-v2。

简单的说,在老版本的report-status 能力下,如果客户端发送推送命令要求服务器更新 A 分支(如: refs/for/master),而服务端转而创建了分支 B(refs/changes/1/123)。这种情况下,服务端也只能通知客户端分支A被创建,而非分支B,否则客户端会报错:“服务端没有按照我的要求去执行”。

扩展后的report-status-v2,服务端可以报告给客户端实际修改的分支,可以报告不同的分支初始指向和最新指向,甚至客户端的一条命令可以对应多条分支的更新。支持该能力的客户端也能正确地将服务端实际更新的分支显示出来。

2. 云效Codeup 是业界第一个支持 git 2.29 新功能的代码平台

阿里云云效Codeup(https://codeup.aliyun.com)是业界首个支持 Git 2.29 新功能的代码平台。当用户执行 git push 命令时,特殊的目标分支会触发服务端 proc-receive 钩子,完成特定功能。

2.1 命令行创建代码评审

在云效Codeup 的“新建合并请求”按钮的下方,有一条低调的提示,如图:

参照提示信息的说明,用户会看到用标准的Git 命令行就可以直接在仓库中创建代码评审。例如用户执行下面命令将当前(HEAD)的更改推送到服务端,向服务端的 master 分支创建代码评审:

git push originHEAD:refs/for/master/local/branch

说明:

  1. 引用表达式的目标分支包含特殊的前缀 “refs/for/",用于向远程仓库特定分支 “master” 发起代码评审。其中的 “local/branch” 通常写做客户端的本地分支名。多次 git push 请求,如果是相同用户、相同的目标分支、相同的 “local/branch",则对应用同一个 pull request。

  2. 此外Codeup 还支持 “refs/drafts/"、“refs/for-review/”等特殊前缀。前缀 “refs/drafts/” 的格式和“refs/for/” 类似,也是针对目标分支创建或者更新 pull request,区别在于创建的 pull request 处于草稿状态,只能发表评审意见,不能合入。前缀“refs/for-review/” 后面跟指定的 pull request ID,用于更新指定的 pull request。

2.2 AGit-Flow 工作流

使用上面介绍的命令行创建代码评审,可以实现无需仓库派生、无需特性分支、无需特殊授权设置,完成代码评审的创建和合入。阿里巴巴代码平台上支持的这种代码协同模式,我们称之为 AGit-Flow。

图中的两个角色,一个是开发者,另外一个是评审者。

开发者通过如下操作,创建和更新 pull request:

  1. 开发者克隆仓库。

  2. 本地仓库内开发,创建提交。

  3. 工作区中执行 git pr命令,推送本地提交到服务器。

  4. 服务器自动创建新的代码评审(例如:pull request #123)。

  5. 开发者根据评审意见,在本地工作区继续开发,新增或修改提交。

  6. 工作区中再次执行 git pr命令,推送本地提交到服务器。

  7. 服务器发现目标分支上已经存在来自同一用户、同一本地分支的 pull request,因此用户此次推送没有创建新的 pull request,而是更新已经存在的 pull request。代码评审者,不但可以给出评审意见,也可以直接发起对评审代码的修改,更新 pull request:

  8. 代码评审者执行 gitdownload 123下载编号为 123 的 pull request 到本地仓库。

  9. 代码评审者本地修改代码后,执行 git pr--change 123命令,将本地修改推送到服务端。

  10. 服务端接收到代码评审者的特殊 git push命令,更新之前由开发者创建的 pull request。

  11. 项目管理者通过点击 pull request 评审界面的合并按钮,将 pull request 合入 master 分支。master 分支被更新,同时关闭 pull request。

2.3 GitHub 是否会引入 Git 2.29 的新功能?

GitHub 引入Git 2.29 新功能没有那么快,原因是 GitHub 的架构是分布式三副本架构,使用的是定制版本的 Git,不能通过升级到 2.29 来支持 proc-receive 钩子,需要另行开发。

当 proc-receive 特性在 Git 社区评审过程中,我邀请了 GitHub 的 Jeff King 参与代码评审。我在邮件中提到了如何在分布式多副本架构中引入proc-receive 钩子的建议,因为我知道 GitHub 的分布式三副本和阿里巴巴的代码平台的分布式架构都面临 proc-receive 钩子可能被多次执行的问题。我们采用的路径是对 Git 协议进行扩展以实现 proc-receive 钩子执行的幂等性。Jeff King 在回复中介绍了 GitHub 的后端实现:

We do run receive-pack on each replica backend. We have a hacky patch for a config option that tells receive-pack to just skip the actual ref-transaction, leaving it up to the proxy layer to do. I've been pushing for us to actually abandon receive-pack entirely, since most of its heavy lifting can be done by sub-programs (for-each-ref for the advertisement, index-pack to receive the pack, and update-ref to update refs). But it's a non-trivial change, and the benefits are only moderate, so it hasn't quite been worth the effort yet.

就是说 GitHub 的分布式多副本服务器上的 git-receive-pack 是修改版本,并不执行引用更新的操作,而是由代理层执行,主要目的是为了避免 pre-receive、post-receive 等钩子的多次执行。阿里巴巴的多副本方案和 GitHub 多副本实现不同,我们的实现可以复用大部分git-receive-pack 的功能。相关讨论如下:

Thanks to Peff for providing technical details of the architecture.  I understand that "receive-pack" of GitHub backend is not involved in references update (executing the commands), so the "proc-receive" hook won't be turned on for GitHub's architecture. While in our architecture (inspired by "spokes" of GitHub), the proxy will deliver not only packfile, but also commands to all three replicas. The proxy will execute "receive-pack" on the replica with a special argument, so the proxy can talk with "receive-pack" with an extended protocol. After running pre-receive hook and release the packfile from quarantine, the replica will stop and wait for the proxy to coordinate. After creating a distributed lock, the proxy will tell all the replicas continue to update the references.  One problem we met is the proc-receive and the post-receive hook must be executed once. We can make the execution of the hooks idempotent, or let only one of the replica run the hook. We choose the latter.
OK, that makes more sense. We solve that by not updating the refs at all via receive-pack (which gives us flexibility to run our own hooks separately on just one replica, etc).

2.4 对于 Gerrit 会有什么影响么?

Gerrit 拥有两个核心特性,一个是集中式的工作流,一个是逐提交评审。集中式工作流可以通过 Git 2.29 的新功能在 GitHub 生态中推广,而 Gerrit 独特的逐提交评审界面依旧具有强大的生命力。

Git 2.29 版本包含的 report-status-v2 特性,可以为 Gerrit 用户带来新的体验。可以预见 Gerrit 会在服务端增加 report-status-v2 相关实现以便更好地适配 Git 新客户端。

3. 用 git-repo 扩展 Git 命令集

Gerrit 生态包含多款客户端工具,例如:Google 为安卓项目开发了名为 repo 的客户端工具实现多仓库管理;OpenStack 社区开发了名为 git-review 的工具,以便简化 Gerrit 工作流的命令行操作。

我们也为阿里巴巴的 AGit-Flow 工作流设计了一款名为 git-repo 的客户端工具,这款工具既能像 OpenStack 社区的相关工具那样对单仓库执行,也能像 Android 社区的 repo 那样实现多仓库项目的协同。

我们将 git-repo 开源,仓库地址:https://github.com/alibaba/git-repo-go

关于 git-repo 的安装和使用,访问网址:

https://git-repo.info

git-repo 除了可以适配阿里巴巴的代码平台(如:云效 Codeup)、Gerrit 之外,还可以通过扩展支持其他实现了 Git 2.29 新特性的代码平台,详见 git-repo 相关文档。

未来已来,全新的 Git 体验,访问云效Codeup。

[1]: https://github.com/gitgitgadget/gitgitgadget

[2]:https://public-inbox.org/git/20200827154551.5966-1-worldhello.net@gmail.com/

更多精彩

长按识别二维码PC端参与

(PC端打开)

⬇️⬇️⬇️

git 为什么不能断点_Git 2.29 让 Git 成功“牵手”Gerrit相关推荐

  1. git 覆盖本地修改_Git拉力–如何使用Git覆盖本地更改

    git 覆盖本地修改 When you learn to code, sooner or later you'll also learn about Version Control Systems. ...

  2. git 代码回滚_git代码版本管理(1)——git版本回滚

    git代码版本管理(1)--git版本回滚 1.问题背景 在利用github.gitlab.Gitee等代码管理器中对代码的管理,我们有时会出现错误提交的情况,此时我们希望能撤销提交操作,让程序回到提 ...

  3. Git2.29让Git成功“牵 手”Gerrit

    GitHub 和 Gerrit 都是诞生于 2008 年的代码平台,两个平台各自形成了相互独立的生态.GitHub 及其模仿者们成为行业主流,托管着大多数开源项目和商业项目的源代码,而 Gerrit ...

  4. git如何切换用户_git切换用户、多用户切换的正确方式 git commit和git push 切换用户...

    由于最近自己搭建了一个GITLAB服务器,为了测试权限.所以会涉及到使用不用的用户进行git commit 和git push 操作. 通过百度搜索以后,发现绝大部分给的答案是: git config ...

  5. git ssh创建分支_Git(2):在gitlab中创建开发用户,以及master分支的安全管理

    一.创建用户 1.创建管理gitlab的开发人员的用户 2.配置用户信息 3.将用户添加到java-daem组中 4.用户登录成功后,在用户界面为用户添加ssh认证 5.在linux主机中将maste ...

  6. git 强制推送_Git 常用命令清单,掌握这些,轻松驾驭版本管理

    工程下载.分支的增删查改 工程下载: clone 远程工程:git clone https://XXXX.git fetch 远程分支到本地某分支:git fetch origin : 分支的增删查改 ...

  7. git add 文件夹_Git的下载安装以及基本操作

    二,配置git:用户名和邮箱 在桌面右键-[Git Bash Here] 输入命令: git config --global user.name "lijiang" git con ...

  8. git 修改标签名称_Git常用命令汇总,希望能帮到你

    展示帮助信息 git help -g 回到远程仓库的状态 抛弃本地所有的修改,回到远程仓库的状态. git fetch --all && git reset --hard origin ...

  9. git stage 暂存_Git撤销暂存区stage中的内容

    $ git add readme.txt提交到了stage中. $ git status On branch master Changes to be committed: (use "gi ...

  10. git项目比对_Git实战之Git对比SVN

    相信很多测试和开发的小伙伴都用过svn,那么svn作为入门级的版本管理工具应用的企业也是比较多的.那么我们先来介绍一下关于svn的一些特点吧,在给大家回顾一下.说的简单点就是svn需要一台svn服务器 ...

最新文章

  1. 革命性移动端开发框架-Flutter时间简史
  2. 用脑科学支持人工智能
  3. mysql8.0连接jdbc url_mysql8.0 jdbc连接注意事项
  4. python开发好学吗-Python人工智能开发难学吗
  5. 更新版PowerBI发布了-- Power BI Report Server Update – March 2018
  6. Python3 多线程问题:ModuleNotFoundError: No module named 'thread',原因及解决办法。
  7. ORACLE 导空表结构
  8. leetCode 50.Pow(x, n) (x的n次方) 解题思路和方法
  9. 23种设计模式C++源码与UML实现--简单工厂模式
  10. 【小摘抄】关于C++11下 string各类用法(持续更新)
  11. 常见笔顺错误的字_最全汉字书写笔顺规则
  12. AC日记——字符串P型编码 openjudge 1.7 31
  13. mongodb 3.0版本安装
  14. 欲求不满之 Redis Lua 脚本的执行原理
  15. rsync+lsync实现多服务器多文件夹双向同步
  16. 从Linux基础到k8s进阶,马哥_K8s进阶实战(11)Kubernetes系统扩展
  17. 计算机板卡设计仿真,电子技能训练(1-1)201492.ppt
  18. Java程序与设计11_一些题目
  19. tm影像辐射定标_Landsat-TM-辐射定标和大气校正步骤
  20. 【git】------git的基本命令 (此文章转载我的老师 Alley-巷子)

热门文章

  1. ASP.NET基本对象介绍
  2. 计算机视觉:关于Graph cuts的简介及相关资源
  3. leetcode刷题日记-472. 连接词
  4. C++排序算法——归并排序
  5. 《剑指offer》面试题41/42——和为s的两个数字VS和为s的连续正数序列(C++代码实现)
  6. 为numpy数组增加一个维度的方法
  7. ENVIArcGis——植被覆盖度提取
  8. ENVI5.3.1使用Landsat 8影像进行主成分分析实例操作
  9. 实习踩坑之路:Git无法拉取最新分支代码?怎么解决?
  10. 数据结构与算法-------希尔排序------交换法+移动法