最近微軟開源了一個新的項目GVFS——Git Virtual File System,這是一個很棒的項目檐什,也已經(jīng)有很多相關的報道了漾峡。出于興趣,我也通過閱讀文檔與源碼鄙陡,理解了GVFS的協(xié)議與工作原理冕房。
一、環(huán)境要求
GVFS的客戶端趁矾,必須在Windwos 10下編譯耙册。而且還必須是最新的版本。Win+R -> winver.exe查看一下毫捣,如果版本號不是1703(或以上)详拙,就無法正確的運行。
下載免費的Visual Studio 2017社區(qū)版蔓同,就能夠編譯饶辙。GVFS的Reame里面“Building GVFS”一節(jié),介紹得還是很清楚的斑粱,照做即可弃揽。
二、如何測試GVFS
目前服務器端對GVFS的支持则北,只有微軟自家的平臺才行矿微,不過Visual Studio Online也是免費的,可以自己注冊一個咒锻。在創(chuàng)建的項目中冷冗,記得添加.gitattributes
文件,還要包含一行* -text
惑艇。
編譯出來的SetupGVFS.exe蒿辙,執(zhí)行一下拇泛,就可以在git bash里用了。記住思灌,要以管理員的身份運行git bash俺叭!
三、執(zhí)行過程
在執(zhí)行完成之后泰偿,當前目錄下熄守,會出現(xiàn)一個MyFirstProject目錄,但是在這個目錄下的src文件夾耗跛,才是實際的git倉庫裕照。
類似于執(zhí)行以下命令:
# mkdir MyFirstProject
# cd MyFirstProject
# git clone https://zhuangbiaowei.visualstudio.com/_git/MyFirstProject src
四、執(zhí)行細節(jié)
1. GET /gvfs/config
按照文檔的說法调塌,還挺像那么回事的晋南,事實上,微軟自己的服務器羔砾,返回的內(nèi)容很簡單负间。
{"AllowedGvfsClientVersions":null}
所以,客戶端也表示WARNING: Unable to validate your GVFS version
新版本的Visual Studio Online加了一個屬性:
{"AllowedGvfsClientVersions":null,"CacheServers":[]}
2. GET /info/refs?service=git-upload-pack
這是標準的Git via HTTP姜凄,為了取得這個倉庫的相關的refs政溃。返回的內(nèi)容大概是這樣的:
001e# service=git-upload-pack
000000a551292cea1631238803732c7e89b78243f5f8d6c9 HEAD multi_ack thin-pack side-band side-band-64k no-progress multi_ack_detailed no-done shallow allow-tip-sha1-in-want
003f51292cea1631238803732c7e89b78243f5f8d6c9 refs/heads/master
0000
其中51292cea1631238803732c7e89b78243f5f8d6c9,就是master分支最新的一次commit id
3. POST /gvfs/objects
按照上一條命令獲取的commit id态秧,客戶端向服務器提交了一組對象請求董虱。而服務器返回了一個pack文件。這個pack文件包含的objects的規(guī)則如下:
objectIds.each do |object_id|
if object_id.type == "commit"
commit_ids = `git rev-list -${CommitDepth} object_id` #取出所需深度的commit_id
tree_ids = get_all_tree_id(commit_ids) #只要tree對象屿聋,不要blob對象
object_ids << commit_ids
object_ids << tree_ids
else
object_ids << object_id
end
end
最后空扎,調(diào)用git自身的pack-objects命令,將這些對象打包成一個文件润讥,就可以返回了转锈。
如果我們將請求得到的數(shù)據(jù)保存為一個文件file-name.pack
,可以用以下兩條命令楚殿,查看這個pack
# git index-pack file-name.pack
# git verify-pack -v file-name.pack
1a17720ebf1ed29cc384feda3f6b254f71634902 tree 79 86 12
14cc2f21152b7655d869fd8eef97996e54b935f0 commit 232 173 98
......
d2246ca4db84bb71793d1529064f8bd46b283005 tree 63 72 9490
non delta: 86 objects
chain length = 1: 3 objects
objects.pack: ok
4. GET /gvfs/prefetch[?lastPackTimestamp={secondsSinceEpoch}]
在第一次調(diào)用時撮慨,lastPackTimestamp會等于-1,相當于請求所有歷史上曾經(jīng)在服務器端打包過的文件脆粥。之后這個lastPackTimestamp砌溺,會變成最近一次的請求時間。
根據(jù)我的推斷变隔,服務器端應該根據(jù)時間戳规伐,將所有這個時間點之后打包的文件,全部計算出來匣缘,再打包一次猖闪,發(fā)送給客戶端鲜棠。
如果將這個獲取到的二進制文件打開,我們會看到這樣的數(shù)據(jù)結構:
4750 5245 2001 0100 ddaa 5759 0000 0000
# 'GPRE '開頭的5個字母
# 無符號整數(shù)代表版本號培慌,目前為1
# 2個字節(jié)豁陆,代表包數(shù)量,目前始終是0100吵护,表示只有一個包
# 8個字節(jié)盒音,代表一個時間戳,0x5957aadd轉換成十進制數(shù)是1498917597馅而,是一個UNIX時間戳
# Time.at(1498917597) = '2017-07-01 21:59:57 +0800'
ffff ffff ffff ff7f ffff ffff ffff ffff
# 根據(jù)文檔祥诽,前8個字節(jié)代表包的長度,后8個字節(jié)代表包的索引用爪,但是目前看來原押,始終不變。大概因為只有一個包的緣故
5041 434b 0000 0002 0000 005b a502 789c # 從這里開始偎血,后續(xù)都是原始的pack的內(nèi)容
3334 3030 3331 5108 7275 74f1 75d5 cb4d
6160 f46b 4c6f 0ddf 364d 8c49 2570 c752
998f d521 4adf 00bf a40c 3a93 0b78 9c2b
5. Mounting File Tree
至此,Clone+Fetch commit & tree的操作盯漂,全部完成颇玷。客戶端將會執(zhí)行mount的操作就缆,利用已經(jīng)獲取到的tree數(shù)據(jù)帖渠,構造出一個FUSE(用戶空間文件系統(tǒng)、Filesystem in Userspace竭宰,簡稱FUSE)空郊,在過去只有類UNIX操作系統(tǒng)才能支持,現(xiàn)在Windows 10也能夠很好的支持了切揭。
有了FUSE狞甚,用戶看起來就已經(jīng)獲取到了整個git代碼倉庫,但事實上廓旬,客戶端只獲取到了全部的目錄樹信息哼审,當用戶需要真正需要訪問這些文件時,才去服務器端獲取孕豹。
6. POST /gvfs/sizes
在客戶端執(zhí)行cd src操作之后涩盾,用戶首次想要查看已經(jīng)clone下來的git倉庫的內(nèi)容,這時励背,事實上倉庫的內(nèi)容并不存在春霍。因此,客戶端會發(fā)起一個sizes請求叶眉,在POST的body中提交一堆的對象ID址儒。
[
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
]
而服務器端芹枷,則返回每個對象的大小。這種操作离福,在git操作倉庫時可以通過以下命令獲取git cat-file -s obj_id
杖狼。返回的格式如下:
[
{
"Id" : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"Size" : 123
},
{
"Id" : "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"Size" : 456
}
]
7. GET /gvfs/objects/{objectId}
在客戶端,如果需要打開某一個具體的文件時妖爷,客戶端才需要真正獲取這個文件的內(nèi)容蝶涩,通過一個GET gvfs/objects/obj_id,就可以獲取到絮识,服務端的處理也很簡單绿聘,唯一需要注意的時:HTTP頭的內(nèi)容包括:Content-Type: application/x-git-loose-object
五、深入?yún)⒖?/h2>
想要更加深入的理解GVFS次舌,可以參考我最近放出的一個開源項目熄攘,是基于ruby語言的gitlab-grack,添加了幾個接口彼念,初步能夠在任何一種操作系統(tǒng)(不必是Windows)運行一個支持GVFS的git http server挪圾,歡迎大家來圍觀。Grack-with-GVFS
關鍵的修改逐沙,在lib/grack/server.rb
與lib/grack/gvfs_helper.rb
這兩個文件里哲思。