碼雲即將支持 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 傳輸的擴展性非常差,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 值即可,簡化了傳輸流程。

這裏有一個 v1 與 v2 的簡短比較

細節Git Smart ProtocolGit Wire Protocol
協議細節一對命令的輸入輸出交換一組命令的輸入輸出交換
git-fetch-pack/git-upload-packls-refs(get refs),fetch(fetch pack)
git-send-pack/git-upload-packpush 暫未利用 Wire Protocol
數據格式pktlinepktline

查看協議內容:protocol-v2 我們可以發現主要是 Capability AdvertisementCommand RequestCapabilities 能夠更好的指定 capabilities,特定的 command

爲了方便讀者直觀瞭解,這裏有兩個 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新功能也需要一些時日才能上線。

NameProtocolTechv2 Statusdescription
Basalt SshdSSHC++,libsshOKbackend git-srv
BrzoHTTPC++, asioOKbackend git-srv
BoiteGitC++,asioOKbackend git-srv
Git-diamondGitC++,asioOKInternal Transfer Server
Basalt Sshd (Go)SSHGoOKDistributed, backend git-srv
BsshdSSHGoOKSingle Machine
BrzoxHTTPGoOKbackend git-srv
BrackHTTPGoOKSingle 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'

檢測遠程服務器是否支持 v2

GIT_TRACE_PACKET=1 git -c protocol.version=2 ls-remote

大家有更多的疑問可以去 https://gitee.com/oscstudio/git/issues 打開一個 Issues。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章