當前位置:
首頁 > 科技 > Git 協議版本 2 宣布推出:Git wire protocol 的一次重大更新

Git 協議版本 2 宣布推出:Git wire protocol 的一次重大更新

作者是Git核心團隊的Brandon Williams。

今天我們宣布推出Git協議版本2,這是Git的線路協議(wire protocol)的一次重大更新。該協議明確規定了在客戶端與伺服器之間如何傳輸克隆(clone)、拉取(fetch)和推送(push)。這個最新版擯棄了Git協議中最低效的部分之一,消除了可擴展性瓶頸,為將來線路協議方面的更多改進打通了道路。

協議版本2規範可在這裡(https://www.kernel.org/pub/software/scm/git/docs/technical/protocol-v2.html)找到。主要改進包括如下:

為引用(reference)提供伺服器端過濾

為新功能提供易於擴展的特性,比如ref-in-want以及拉取和推送symrefs

簡化HTTP傳輸的客戶端處理

開發新協議的主要動機是能夠為引用(分支和標記)提供伺服器端過濾。在協議v2之前,伺服器以初始引用通告來響應所有拉取命令,列出代碼倉庫中的所有引用。即使客戶端只關心更新單個分支,比如`git fetch origin master`,也會發送這整個列表。針對含有數十萬引用(Chromium代碼倉庫就有超過50萬個分支和標記)的倉庫,伺服器可能最終發送數十MB的數據,但這些數據卻被忽略。這在拉取期間通常佔用大量的時間和帶寬,尤其是當你遠程更新只有幾個提交的分支時,或者甚至當你只檢查是否最新,因而導致無操作拉取時。

我們最近在谷歌推廣了支持協議版本2的工作,看到在含有50萬個引用的倉庫上對單個分支執行無操作拉取的性能提高了3倍。協議v2還使從googlesource.com伺服器發送的開銷位元組(non-packfile)減少了8倍。這種改進主要歸功於將伺服器通告的引用過濾成客戶端表示感興趣的引用。

克服障礙

這些年來,Git 項目作過多次嘗試,試圖限制初始引用通告,或者完全改用一種新的協議,但依然碰到了兩個問題:(1)初始請求僵硬,又不包含可用於在不破壞與現有伺服器兼容性的情況下請求新伺服器修改響應的欄位。(2)錯誤處理機制的定義不夠完善,無法安全地使用現有伺服器不了解的新協議,並快速退回到舊協議。為了改用新的協議版本,我們需要找到現有伺服器會忽略,但可以用來安全地與新伺服器通信的一條旁路(side channel)。

有三種主要的傳輸方式用來支持Git 的線路協議(git://、ssh://和https://),我們用來請求v2的這條旁路在通信時需要確保舊伺服器會忽略發送的任何額外數據,且不會崩潰。http 傳輸方式最簡單,因為我們只需在請求中加入一個額外的http報頭(如「Git-Protocol: version=2」)。ssh 傳輸方式要難一點,因為它需要將環境變數(「GIT_PROTOCOL=version=2」)發送到遠程終端上。這個更具挑戰性,因為它需要伺服器管理員配置 sshd,以便在伺服器端接收新的環境變數。最難的傳輸方式是匿名Git傳輸(git://)。

使用匿名 Git 傳輸方式向伺服器發出的初始請求使用了單個 packet-line 的格式,這包括請求的服務(用於拉取的git-upload-pack和用於推送的git-receive-pack)以及後面跟著一個NUL位元組的倉庫。後來添加了虛擬化支持,主機名參數可以由NUL位元組來附加和終結:「0033git-upload-pack /project.githost=myserver.com」。理想情況下,我們可以添加一個用來請求v2的新參數,只要像添加主機名那樣來添加:「003dgit-upload-pack /project.githost=myserver.comversion=2」。

遺憾的是,由於 2006 年出現的一個 bug(https://github.com/git/git/commit/49ba83fb67d9e447b86953965ce5f949c6a93b81#diff-f1c031e638a78dd61428fbec6d53dd09R405),我們無法添加主機名以外的任何額外參數(由NUL隔開),否則解析這些參數會進入無限循環。這個 bug 在 2009 年得到了修復(https://github.com/git/git/commit/73bb33a94ec67a53e7d805b12ad9264fa25f4f8d),當時添加了一個檢查機制,不允許添加額外參數,因而新客戶端無法觸發舊伺服器上的這個 bug。

幸運的是,該檢查機制不會注意到我們是否發送了隱藏在第二個 NUL 位元組後面的額外的請求參數,這個問題早在2009年就指出來了。這允許像「003egit-upload-pack /project.githost=myserver.comversion=2」這種結構的請求。通過將版本信息放在第二個 NUL 位元組的後面,我們就可以同時繞過無限循環這個bug和明確不允許主機名之外的額外參數。只有新伺服器會知道尋找隱藏在兩個 NUL 位元組後面的額外信息,舊伺服器不會發出任何警告。

現在,無論在什麼情況下,客戶端都可以發起使用v2的請求,使用針對特定傳輸方式的旁路;v2 伺服器可以使用新協議來響應,舊伺服器會忽略旁路,就用引用通告來響應。

親自試一下

若想親自試一下協議版本2,你需要最新版本的Git(支持v2 的功能最近已合併到Git的主分支中,預計會成為Git 2.18 的一部分)以及支持v2的伺服器(googlesource.com和Cloud Source Repositories上的倉庫支持v2)。如果你啟用了追蹤機制,運行「ls-remote」命令以查詢單個分支,就可以看到伺服器使用協議版本2時,發送的引用數量要少得多。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 雲頭條 的精彩文章:

8500000 只交換機因「嚴重漏洞」面臨攻擊……

TAG:雲頭條 |