码云即将支持 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-pack
的 fetch
和 send-pack
—> receive-pack
的 push
,而 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
,需要服务器支持。