码云即将支持 Git v2 Protocol

美国当地时间 5月18日 Google 开发者发布了一篇博客 Introducing Git protocol version 2 宣布了 Git v2 Protocol,v2 协议又叫做 Git Wire Protocol,新协议旨在改进 Git 的传输过程。

通常情况下,Git 传输协议 (v1 smart protocol)是非常高效的,但是当一个存储库体积巨大或者引用(分支)数目巨多时,用户体验将变得非常差。

在 v1 协议中,任何请求都是从发现引用开始,比如 git clone https://host/repo.git 的第一步就是 GET /repo.git/info/refs?service=git-upload-pack,这个时候,服务器要列出所有的引用,如果存在一万个引用就意味着一万个引用都需要解析然后传输,而基于 Pull-Request 模型的开发流程这种情况将更加突出(每一次创建一个 PR 相当于创建若干引用),这就给 Git 的传输带来了很大的压力,用户也需要花费更长的时间等待。

在 v1 协议中,git 传输的扩展性非常差,以 HTTP 浅表克隆为例 , git 需要协商需要的 commit 深度,由于 HTTP 协议是无状态协议,请求流程是 Request-Response ,因此在第一个 HTTP post 请求时,fetch-pack 需要输出 depth 的值,然后等待 upload-pack 计算此上限 commit id,fetch-pack 再次发送请求告知 upload-pack 需要的上限 commitid,在 v1 协议中只能别扭的实现此功能,Http Git 服务器开发者也需要注意此处需要及时的让 git-upload-pack 退出,否则会出现竞争长时间挂起。

而在 v2 协议中,直接告知服务器,当前分支最新的 commit 以及 deepen 值,让服务器计算,简化了传输流程。

为了方便读者直观了解,这里有两个 Gist 片段 https://gitee.com/ipvb/codes/y1b4ew3vqfgom5298iznh69 分别是 v1 和 v2 传输协议的抓包分析。

由于 Git 2.18 还未发布,用户想直接用上 v2 功能得直接从源码构建 Git。在 Git 的 pu 分支,我们还发现协议相对于 master 分支有所修改,主要是增加了 filter 这个功能主要是为了支持部分检出,类似与 Git GVFS,这种功能将极大的优化巨型存储库的访问,这正是 v2 扩展性好的体格体现。

If the 'filter' feature is advertised, the following argument can be
included in the client's request:

    filter <filter-spec>
	Request that various objects from the packfile be omitted
	using one of several filtering techniques. These are intended
	for use with partial clone and partial fetch operations. See
	`rev-list` for possible "filter-spec" values.

v2 与 v1 协议最大的区别在于,v1 协议是基于服务 (service) 的,服务功能单一,fetch-pack —> upload-packfetchsend-pack —> receive-packpush,而 v2 协议则变成了可以执行多个命令,这与 svn 协议又有了相似之处。不同类型的版本控制系统也在互相吸取经验。

Wire Protocol 协议的内容可以在这里找到。

码云的进展

笔者在了解到 v2 协议发布后,花了几天实现了对 v2 协议的兼容。

HTTP 协议需要检测请求头中是否有 Git-Protocol 然后 GET 请求时不再输出 # service=git-upload-pack 这样的标志,添加环境变量 GIT_PROTOCOL=version=2 去执行 git-upload-pack/git-receive-pack.

SSH 协议支持 SetEnv 修改环境变量,所以只要接受客户端的 SetEnv 请求,然后和 HTTP 一样即可,SSH 没有 # service=xx 因此也没有那一不需要修改。

Git 协议需要重新解析数据头,判断是否存在 version=2,类似头部 003egit-upload-pack /project.git\0host=myserver.com\0\0version=2\0

码云架构早已经变成了分布式的, 我们拥有统一的 Git 传输服务 git-srv 有 HTTP/SSH/GIT 适配,在与 git-srv 协商时,增加一个 version 字段,然后 git-srv 在启动 git 命令时,如果发现不为空就设置环境变量即可。

目前包括 Git SSH HTTP 协议都已经做到了兼容 v2 协议。但由于 Git 2.18 还未释放,协议也在改进,这些x新功能也需要一些时日才能上线。

Name Protocol Tech v2 Status description
Basalt Sshd SSH C++,libssh OK backend git-srv
Brzo HTTP C++, asio OK backend git-srv
Boite Git C++,asio OK backend git-srv
Git-diamond Git C++,asio OK Internal Transfer Server
Bawl SSH Go OK Distributed, backend git-srv
Bsshd SSH Go OK Single Machine
Brzo(Go) HTTP Go OK backend git-srv
Brack HTTP Go OK Single Machine

Go 的单机版主要是为一些小企业优化。

如何使用

用户需要从源码编译 git . 然后找到一个支持 v2 的服务器,目前主流的 Git 托管平台都暂不支持(除了 Google 的),官方的 git-http-backend 支持,这里有一个笔者贡献的 git-http-backend 已经支持:https://github.com/asim/git-http-backend。 需要注意服务器上的 git 也需要是最新版,否则无法使用。

git -c protocol.version=2 clone http://domian/repo.git

为了简化操作,可以在 .zshrc 或者 .bashrc 中新建别名:

alias git2='/path/to/git/installdir/bin/git -c protocol.version=2'

大家有更多的疑问可以去 https://gitee.com/oscstudio/git/issues 打开一个 Issues。

彩蛋:https://gitee.com/oscstudio/git 的 HTTP2 分支支持 HTTP2,需要服务器支持。