鍍金池/ 教程/ Java/ 傳輸協(xié)議
儲(chǔ)藏
Git 樹名
子模塊
使用 Git Grep 進(jìn)行搜索
建立一個(gè)私有倉庫
追蹤分支
Git 的撤消操作 - 重置,簽出和撤消
Git 和 Email
定制 Git
查看 Git 對(duì)象
Git 是如何存儲(chǔ)對(duì)象的
Git 標(biāo)簽
交互式 rebase
獲得一個(gè) Git 倉庫
高級(jí)分支與合并
Git 對(duì)象模型
Git 日志
建立一個(gè)公共倉庫
分布式的工作流程
交互式添加
維護(hù) Git
安裝與初始化
安裝 Git
更底層的 Git
歡迎使用 Git
查找問題的利器 - Git Blame
找回丟失的對(duì)象
比較提交
創(chuàng)建新的空分支
查找問題的利器 - Git Bisect
Git 引用
忽略某些文件
傳輸協(xié)議
打包文件
Git 索引
rebase
正常的工作流程
分支合并
Git 目錄與工作目錄

傳輸協(xié)議

這里我們要看一下:Git 的客戶端和服務(wù)器如何交互傳輸數(shù)據(jù)。

通過 HTTP 協(xié)議抓取

通過 http 協(xié)議的 url 進(jìn)行的 git 數(shù)據(jù)抓取,使用了一個(gè)比較傻瓜化(dumber)的協(xié)議。

使用 http 協(xié)議,所有的邏輯計(jì)算(logic)都是在客戶端進(jìn)行。服務(wù)器不需要特別的設(shè)置,你只要把 git 目錄放到一個(gè)可以訪問的 web 目錄即可。

為了能通過 http 訪問,當(dāng)你的倉庫有任何更新時(shí),需要運(yùn)行一個(gè)命令:git update-server-info。因?yàn)?web 服務(wù)器一般不允許執(zhí)行列出目錄中文件的操作,所以 git update-server-info 命令把可用的打包文件(packfile)和引用(refs)列表更新到‘objects/info/packs’,info/refs 這個(gè)兩個(gè)文件中。當(dāng) git update-server-info 執(zhí)行后,"objects/info/packs"文件看起來就會(huì)像下面一樣:

P pack-ce2bd34abc3d8ebc5922dc81b2e1f30bf17c10cc.pack
P pack-7ad5f5d05f5e20025898c95296fe4b9c861246d8.pack

如果在通過 http 協(xié)議拉取數(shù)據(jù)的過程中找不到松散文件(loose file),git 就會(huì)去嘗試查找打包文件(packfiles)。 "info/refs" 文件的內(nèi)容看起來就下面這樣:

184063c9b594f8968d61a686b2f6052779551613    refs/heads/development
32aae7aef7a412d62192f710f2130302997ec883    refs/heads/master

當(dāng)你從這個(gè)倉庫開始抓取(fetch)數(shù)據(jù)時(shí),git 就會(huì)從這些引用(refs)開始遍歷查找所有的提交對(duì)象(commit objects),直到客戶端得到了它所有需要的所有對(duì)象為止。

例如,你要抓取到(fetch)服務(wù)器上的"master"分支;git 看到服務(wù)器上的"master"分支指向 32aae7ae,而你當(dāng)前的"master"分支是指向 ab04d88。那么很明顯,你需要得到 32aae7ae 這個(gè)對(duì)象。

下面就是抓取時(shí)的交互過程(http協(xié)議層):

CONNECT http://myserver.com
GET /git/myproject.git/objects/32/aae7aef7a412d62192f710f2130302997ec883 - 200

然后返回信息看起來就像下面這樣:

tree aa176fb83a47d00386be237b450fb9dfb5be251a
parent bd71cad2d597d0f1827d4a3f67bb96a646f02889
author Scott Chacon <schacon@gmail.com> 1220463037 -0700
committer Scott Chacon <schacon@gmail.com> 1220463037 -0700

added chapters on private repo setup, scm migration, raw git

好的那么現(xiàn)在它就是開始抓取樹對(duì)象(tree) aa176fb8:譯者注:32aae7ae 提交對(duì)象(commit object)指向的樹對(duì)象(tree)是:aa176fb8。

GET /git/myproject.git/objects/aa/176fb83a47d00386be237b450fb9dfb5be251a - 200

下面這些是返回的樹對(duì)象(tree)信息:

100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3    COPYING
100644 blob 97b51a6d3685b093cfb345c9e79516e5099a13fb    README
100644 blob 9d1b23b8660817e4a74006f15fae86e2a508c573    Rakefile

很明顯,樹對(duì)象(tree)里有 3 個(gè)文件(blob),我們就把它們抓下來吧:

GET /git/myproject.git/objects/6f/f87c4664981e4397625791c8ea3bbb5f2279a3 - 200
GET /git/myproject.git/objects/97/b51a6d3685b093cfb345c9e79516e5099a13fb - 200
GET /git/myproject.git/objects/9d/1b23b8660817e4a74006f15fae86e2a508c573 - 200

這些 http 下載操作實(shí)際上是由 curl 來完成的,我們可以開多個(gè)并行的線程來加快下載速度。Git 遍歷完提交對(duì)象(commit)所指向的樹對(duì)象(tree)后,就會(huì)開始抓取提交對(duì)象(commit)的父對(duì)象(next parent)。

GET /git/myproject.git/objects/bd/71cad2d597d0f1827d4a3f67bb96a646f02889 - 200

返回的父對(duì)象(parent commit object)信息就如下面所示:

tree b4cc00cf8546edd4fcf29defc3aec14de53e6cf8
parent ab04d884140f7b0cf8bbf86d6883869f16a46f65
author Scott Chacon <schacon@gmail.com> 1220421161 -0700
committer Scott Chacon <schacon@gmail.com> 1220421161 -0700

added chapters on the packfile and how git stores objects

我們現(xiàn)在可以看到 ab04d88 是返回的對(duì)象(commit)的父對(duì)象,而 ab04d88(commit)就是我們當(dāng)前的‘master’分支。那么我們只需要得到樹對(duì)象(tree):b4cc00c 就可以了,因?yàn)橹暗乃缘奶峤?commit)我們都有了。為了保險(xiǎn)起見,你也可以加上--recover參數(shù),強(qiáng)制 git 反復(fù)檢查我們是否擁有所有的對(duì)象。你可以點(diǎn)這里:git http-fetch 查看更多信息:

如果有一個(gè)松散對(duì)象(loose object)下載失敗了, git會(huì)下載打包文件索引(packfile indexes), 通過它來查找對(duì)應(yīng)的 sha 串值,然后再下載對(duì)應(yīng)的打包文件(packfile)。

你一定要在 git 服務(wù)器的倉庫里添一個(gè)“post-receive”鉤子(hook), 這個(gè)鉤子(hook)會(huì)在倉庫更新后執(zhí)行git update-server-info; 否則倉庫的相關(guān)信息就得不到更新。

通過 Upload Pack 抓取數(shù)據(jù)

對(duì)于一個(gè)聰明的協(xié)議,抓取對(duì)象的過程(fetching objects)應(yīng)當(dāng)更加高效。不管是用通過 ssh 協(xié)議還是 git 協(xié)議(git:// 協(xié)議,在 9418 端口上運(yùn)行), 當(dāng)客戶端和服務(wù)器建立了一個(gè) socket 連接后,客戶端開始運(yùn)行:git fetch-pack命令,和服務(wù)器創(chuàng)建(fork)的linkgit:git update-pack進(jìn)行通訊。

服務(wù)器會(huì)告訴客戶端它每個(gè)引用(ref)所有擁有的 SHA 串值,而客戶端會(huì)以它所需要的和所擁有 SHA 串值作為回應(yīng)。

這里,服務(wù)器會(huì)把客戶端需要的所有對(duì)象打一個(gè)包(packfile),然后再傳送給客戶端。

讓我們來看一個(gè)例子。

客戶端連接并且發(fā)送請(qǐng)求頭(request header)。例如,克隆命令:

$ git clone git://myserver.com/project.git

上面的命令會(huì)產(chǎn)生下面的請(qǐng)求:

0032git-upload-pack /project.git\000host=myserver.com\000

每行的最前面的 4 個(gè)字節(jié)表示此行的 16 進(jìn)行制長(zhǎng)度(hex length) (包括這個(gè) 4 個(gè)字節(jié),但不包括換行符)。 下面接著的是命令和參數(shù),這之后是一個(gè) null 字節(jié)(#body00)和主機(jī)信息。請(qǐng)求的結(jié)尾是以 null 字節(jié)(\000)結(jié)束的。

這個(gè)請(qǐng)求被服務(wù)器接收并且轉(zhuǎn)換成對(duì)"git-upload-pack"的命令調(diào)用。

$ git-upload-pack /path/to/repos/project.git

這條命令會(huì)馬上返回倉庫的信息:

007c74730d410fcb6603ace96f1dc55ea6196122532d HEAD\000multi_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress
003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug
003d5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/dist
003e7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 refs/heads/local
003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master
0000

每一行開始的頭 4 個(gè)字節(jié)表示此行的長(zhǎng)度(以 16 進(jìn)制表示)。這塊(section)信息以一行“0000”為結(jié)束標(biāo)識(shí)符。

上面這些服務(wù)器產(chǎn)生的數(shù)據(jù)被發(fā)送回客戶端。然后客戶端用另外一個(gè)請(qǐng)求做為響應(yīng):

0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack side-band-64k ofs-delta
p 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe

0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a
0032want 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01
0032want 74730d410fcb6603ace96f1dc55ea6196122532d
00000009done

上面這些客戶端的請(qǐng)求會(huì)被發(fā)送到的git-upload-pack進(jìn)程,這個(gè)進(jìn)程會(huì)返回(streams out)最終的結(jié)果(final response):

"0008NAK\n"
"0023\002Counting objects: 2797, done.\n"
"002b\002Compressing objects:   0% (1/1177)   \r"
"002c\002Compressing objects:   1% (12/1177)   \r"
"002c\002Compressing objects:   2% (24/1177)   \r"
"002c\002Compressing objects:   3% (36/1177)   \r"
"002c\002Compressing objects:   4% (48/1177)   \r"
"002c\002Compressing objects:   5% (59/1177)   \r"
"002c\002Compressing objects:   6% (71/1177)   \r"
"0053\002Compressing objects:   7% (83/1177)   \rCompressing objects:   8% (95/1177)   \r"
...
"005b\002Compressing objects: 100% (1177/1177)   \rCompressing objects: 100% (1177/1177), done.\n"
"2004\001PACK\000\000\000\002\000\000\n\355\225\017x\234\235\216K\n\302"...
"2005\001\360\204{\225\376\330\345]z2673"...
...
"0037\002Total 2797 (delta 1799), reused 2360 (delta 1529)\n"
...
"<\276\255L\273s\005\001w0006\001[0000"

你可以查看“打包文件”這一章,了解響應(yīng)內(nèi)容中的打包文件(packfile)的格式。

推送數(shù)據(jù)

通過 git 和 ssh 協(xié)議推送數(shù)據(jù)(pushing data)是相似的,但是更簡(jiǎn)單?;旧鲜?,客戶端發(fā)出一個(gè)“receive-pack”的請(qǐng)求,如果客戶端有訪問權(quán)限,那么服務(wù)器就返回所有引用“頭”的 SHA 串值(all ref head shas)??蛻舳耸盏巾憫?yīng)后,計(jì)算出服務(wù)器需要的所有數(shù)據(jù)或?qū)ο?,再做成一個(gè)打包文件(packfile)傳送給服務(wù)器。服務(wù)器收到后要么就把它們存儲(chǔ)到硬盤上再建立索引,要么只把它解壓(如果里面的對(duì)象不多的話)。

在這整個(gè)推送數(shù)據(jù)的過程中,客戶端通過 git push 命令調(diào)用:git sendpack 命令,服務(wù)器端通過“ssh 連接進(jìn)程”或是“git 服務(wù)器”來調(diào)用:linkgit:git-receive-pack命令來完成整個(gè)操作。

下一篇:比較提交