GVFS协议与工作原理

最近微软开源了一个新的项目GVFS——Git Virtual File System,这是一个很棒的项目,也已经有很多相关的报道了。出于兴趣,我也通过阅读文档与源码,理解了GVFS的协议与工作原理。

一、环境要求

GVFS的客户端,必须在Windwos 10下编译。而且还必须是最新的版本。Win+R -> winver.exe查看一下,如果版本号不是1703(或以上),就无法正确的运行。

下载免费的Visual Studio 2017社区版,就能够编译。GVFS的Reame里面“Building GVFS”一节,介绍得还是很清楚的,照做即可。

二、如何测试GVFS

目前服务器端对GVFS的支持,只有微软自家的平台才行,不过Visual Studio Online也是免费的,可以自己注册一个。在创建的项目中,记得添加.gitattributes文件,还要包含一行* -text

编译出来的SetupGVFS.exe,执行一下,就可以在git bash里用了。记住,要以管理员的身份运行git bash!

三、执行过程

在执行完成之后,当前目录下,会出现一个MyFirstProject目录,但是在这个目录下的src文件夹,才是实际的git仓库。

类似于执行以下命令:

# mkdir MyFirstProject
# cd MyFirstProject
# git clone https://zhuangbiaowei.visualstudio.com/_git/MyFirstProject src

四、执行细节

1. GET /gvfs/config

按照文档的说法,还挺像那么回事的,事实上,微软自己的服务器,返回的内容很简单。

{"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。返回的内容大概是这样的:

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的规则如下:

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

最后,调用git自身的pack-objects命令,将这些对象打包成一个文件,就可以返回了。

如果我们将请求得到的数据保存为一个文件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}]

在第一次调用时,lastPackTimestamp会等于-1,相当于请求所有历史上曾经在服务器端打包过的文件。之后这个lastPackTimestamp,会变成最近一次的请求时间。

根据我的推断,服务器端应该根据时间戳,将所有这个时间点之后打包的文件,全部计算出来,再打包一次,发送给客户端。

如果将这个获取到的二进制文件打开,我们会看到这样的数据结构:

4750 5245 2001 0100 ddaa 5759 0000 0000
# 'GPRE '开头的5个字母
# 无符号整数代表版本号,目前为1
# 2个字节,代表包数量,目前始终是0100,表示只有一个包
# 8个字节,代表一个时间戳,0x5957aadd转换成十进制数是1498917597,是一个UNIX时间戳
# Time.at(1498917597) = '2017-07-01 21:59:57 +0800'
ffff ffff ffff ff7f ffff ffff ffff ffff
# 根据文档,前8个字节代表包的长度,后8个字节代表包的索引,但是目前看来,始终不变。大概因为只有一个包的缘故
5041 434b 0000 0002 0000 005b a502 789c # 从这里开始,后续都是原始的pack的内容
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的操作,全部完成。客户端将会执行mount的操作,利用已经获取到的tree数据,构造出一个FUSE(用户空间文件系统、Filesystem in Userspace,简称FUSE),在过去只有类UNIX操作系统才能支持,现在Windows 10也能够很好的支持了。

有了FUSE,用户看起来就已经获取到了整个git代码仓库,但事实上,客户端只获取到了全部的目录树信息,当用户需要真正需要访问这些文件时,才去服务器端获取。

6. POST /gvfs/sizes

在客户端执行cd src操作之后,用户首次想要查看已经clone下来的git仓库的内容,这时,事实上仓库的内容并不存在。因此,客户端会发起一个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}

在客户端,如果需要打开某一个具体的文件时,客户端才需要真正获取这个文件的内容,通过一个GET gvfs/objects/obj_id,就可以获取到,服务端的处理也很简单,唯一需要注意的时:HTTP头的内容包括:Content-Type: application/x-git-loose-object

五、深入参考

想要更加深入的理解GVFS,可以参考我最近放出的一个开源项目,是基于ruby语言的gitlab-grack,添加了几个接口,初步能够在任何一种操作系统(不必是Windows)运行一个支持GVFS的git http server,欢迎大家来围观。Grack-with-GVFS

关键的修改,在lib/grack/server.rblib/grack/gvfs_helper.rb这两个文件里。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 本片内容转自CSDN http://blog.csdn.net/ithomer/article/details/7...
    五娃儿阅读 4,832评论 2 88
  • 点火送君归化外, 残躯伛偻世间囚。 白头年少三张嘴, 黑冢妻哀一地愁。
    迷蝴庄生阅读 256评论 0 13
  • 2017年7月11日 天气晴 因为临时被放了鸽子,饭后独自跑到滨江散步。沿着江边走了接近一个小时,听了三个路边“...
    钟小笛阅读 316评论 0 0
  • 突然间想到了外公他老人家,时间怎么样都会逼着我往前走,我很想能早点遂了他们的愿望。歉疚已经随时间越来越加深严重了。...
    囫囵思阅读 156评论 0 0