用 strace 命令淺析 git push 通過 SSH 工作的原理

howar_u發表於2016-02-25

昨天,突如其來的好奇充斥著我的腦袋:究竟 git push 如何通過 SSH 工作呢?由於我越來越習慣使用 strace 來折騰這類問題,所以我又嘗試用它來練練手。如果我利用 strace(跟蹤)git push 命令到這個網站的(資料)庫,會得到如下顯示:

所以 git push 最終會呼叫 ssh git@github.com git-receive-pack <repo-path>。然後在我的終端(terminal)嘗試輸入以下命令,得到了以下線索:

終端依然在等待我的輸入。SSH 用來解決身份驗證和遠程控制的問題,驗證成功後,SSH 的另一端會執行一段命令來進行資料交換。而上面這幾行就是資料交換的開始。

在網上稍微搜尋了一下,我知道了這個協議是由行組成的,而每一行的 4 位前置碼正是行長度的十六進位制表示。後面跟著提交的 SHA-1 和 ref ,傳送端以一行 “0000” 作為結束識別符號。

上面的每一行對應(資料)庫裡的每一個分支:第一行自帶了一條長長的小尾巴,好像是傳送程式的自我介紹和支援的相關功能。

我在研究這些程式碼的時候,使用 xsel 命令把輸出結果複製到編輯器上面,不過令人困惑的是,我貼上得到的竟然只有第一行而不是所有後設資料。

通過 hexdump –C 檢視完整的輸出後發現,原來在 refs/heads/gh-pages 後面有一個空位元組,而且在末尾處還換行了(用星號 * 標記處):

我在沒有仔細研究的情況下,大膽地做了一個猜想:那些在 github 上做開發的傢伙們,定義了一個相當簡單的長度字首+換行分隔協議(length-prefixed, newline-separated protocol),當他們有需要向協議裡面加入一些後設資料的時候,可以保持和老版本 git 的相容性。這個解決方案巧妙地利用了 C 語言的 0 結尾字串:把後設資料放在空位元組和新行之間。用這種方式讀取資料(讀取到新行之前位置)可以得到所有的後設資料。後設資料處理程式碼在執行時會跳過空位元組,但是現有的協議程式碼卻只能看到到空位元組之前的資料,它們對這種改動完全無感!

所以我之前用 xsel 命令複製資料時,那些空位元組後面的東東被完美地忽略了。

真相就只有一個,謎底就這樣被解開了!(其實wo真不是柯南哦)

相關文章