用 Git 虚拟文件系统来解决大型存储问题

  • Jonathan Allen
  • 足下

2017 年 2 月 28 日

话题:Git语言 & 开发

尽管大家都认为 Git 是广为采用的最好的版本控制软件,但它仍然远远不够完美。有些问题可以用第三方工具来解决,但要把整个代码库都复制到开发者的电脑上时却可能会坏事。微软在试图将他们的 300GB 的代码库从内部系统迁移上 Git 时发现了这个问题。因此催生了 Git 虚拟文件系统(Git Virtual File System,GVFS)。

故事要从大概 2000 年左右开始讲起,当时微软是主要在使用一套名为“Source Depot”的内部系统,这是 Perforce 的一个分支。慢慢地,许多团队都换用了 TFVC,即 Team Foundation Server(TFS)里原来用的版本控制系统,但许多大型团队却没办法判断要花多少代价才能从 Source Depot 上迁移出来。同样,许多团队都是使用了 TFS 的部分功能,但具体用的是哪一部分却因团队而异,还各自混用了许多第三方工具及内部开发的工具。

为了努力简化这样复杂的环境,微软决心将用于工作计划、自动构建和源码控制等的 Visual Studio 团队服务(即云上 TFS)相关的许多团队标准化。其中的最后一点,就是和 Git 一起提供的 VSTS。

为什么是 Git 呢?根据微软员工,以及 reddit 的用户jeremyepling的想法,主要有三点原因:

Git 和 GitHub 上的公共存储都是 OSS 开发的事实标准。微软做了许多 OSS 开发,我们想让我们的 TFS 和团队服务等 DevOps 工具可以和这些工作流一起工作得很好。

我们希望微软里所有团队都使用相同的版本控制系统。标准化将使得人们在项目之间转换,以及形成资深专业经验的过程变得容易。因为 OSS 是捆绑到 Git 上的,而且我们也做了许多 OSS 开发,那 Git 就自然而然的成为我们的首选了。

我们希望能响应并支持社区和我们的 DevOps 客户希望的方向。Git 很明显就是现代版本控制系统的领头羊。

但如Brian Harry 所说,这有一些问题:

关于选择 Git 有许多争论,最主要的一个是规模问题。没有多少公司会有我们这么大规模的代码库。特别是 Windows 和 Office(还有一些其它的),规模非常巨大。有几千个工程师,几百万个文件,几千台构建服务器在不断的构建它——老实说,这是令人难以置信的。说得更清楚些,当我在这篇贴子中提到 Window 的时候,我实际说的是一个非常模糊的概念,这包括了用于 PC、手机、服务器、HoloLens、Xbox、物联网等等方面的所有 Windows 系统。而 Git 是一个分布式的版本控制系统(distributed version control system,DVCS)。它会把整个代码库和所有的文件历史都拷到你自己的机器上。要是对 Windows 也这么做的话会被人笑话的(事实上我们也的确被别人笑话了很多次)。TFVC 和 Source Depot 都针对大型代码库和团队做过非常专门的优化。Git 在这样的问题上却从来没有过先例(哪怕类似规模的也行),所以很多人都断定这种方法肯定行不通。

Reddit 用户 Ruud-v-A 为这个问题提供了一些参考信息:

Linux 内核的代码库有 1.2GB 了,开发了大约 12 年,有 5700 个文件。2005 年第一次提交时记录过,将整个开发历史都导入进来一共是 3.2GB。3.2GB 对应 5700 个文件,由此推算那 350 万个文件就会需要 270GB。

Chromium 的代码库(包含了 Webkit 从 2001 年开始的历史代码)大小时 11GB,有 24.6 万个文件。由此推算要 20 年并且有 350 万个文件的话,需要 196GB 的空间。

将 Windows 代码库拆分成合适大小的子代码库这条路也行不通。假如当初一开始的时候就是这么做的,那还有可能,可是到现在代码库已经这么大了,而且又发展了这么久,要再想回头把它拆分开,这事实上并不可能了。Brian 继续说:

这意味着我们必须开始着手将 Git 增强,让它可以支持几百万个文件,几百 G 的大小,可以由几千个开发者一起使用。同时提供一点参考信息,即使是 Source Depot 也没办法支持得了整个 Windows 的代码库规模。它被拆分成了 40 多个代码库,所以我们才能将它扩展,但必须在它们之上构建一层,这样在许多情况下,才能像使用一个代码库一样使用它们。抽象当然并不完美,有时也会造成一些冲突。

为什么不干脆把历史版本都丢掉,然后重新开始呢?SuperImaginativeName 给出了一个说法:

NT 内核、NT 的驱动、子系统、API、硬件驱动、Win32 API 等,全都被其它系统所依赖,包括客户。你觉得为什么你可以在 Windows 上运行一个已经开发了 30 年的程序呢?没有这些历史版本,以内核团队为例,他们就不会记得 15 年前在一款特别的 CPU 上必须设置一个特殊的标记位,因为它的指令集架构中有个小 BUG,会导致用户无法正常运行某个旧的应用程序。如果把这些历史版本丢掉了,那也就意味着丢掉了非常大量的信息。你总不能期望每个开发者都能用脑子记住为什么一个特别的系统是那样工作的。能想像某段奇怪的代码看起来好象不怎么正确,但事实上它却能让你免于遭受文件损坏之苦吗?如果只是简单地提交一个新补丁,再注释上“解决了一个奇怪的问题,真不明白为什么以前没人发现它”,这么做的后果会是灾难性的。再想想去查看一下代码历史,然后最终恍然大悟原来这么做是有道理的,这两种行为哪个才更正确?Windows 代码的开发是非常严谨的,所有东西都是要经过审核的。

在经过了若干次失败的尝试,包括尝试使用 Git 子模块等之后,微软开始开发 Git 虚拟文件系统了:

我们试过一种“虚拟化”Git 的方案。通常当你用 Git 克隆分支时,它会把所有东西都下载下来。但如果它不下载所有东西,又会怎样?如果我们可以把底层的存储虚拟化,让它只下载需要的东西,又会怎样?这样的话克隆一个巨大的 300GB 的代码库就会非常快了。当我执行 Git 命令,或者在我的文件系统里读写文件时,系统会无缝地从云端获取内容(并且将它保存在本地,这样以后就是本地访问数据了)。这种方法的缺点在于没有离线支持。如果你需要这一点,你就得把所有东西“touch”一遍,假装在本地有它们,除此之外没有任何其它缺点了——你仍然会有 100% 一致的 Git 体验。而且对于我们的巨型代码库来说,这也是行得通的。

Saeed Noursalehi补充到:

有了 GVFS,就意味着他们现在可以有了更好管理的 Git 使用体验:每次克隆只需要花几分钟,而不用 12 多个小时;检出代码只需要大约 30 秒,而不是 2-3 小时;status 命令也只需要 4-5 秒钟,而不是 10 分钟。而且我们还在不断努力,让它些数字变得更好(当然,代价是第一次构建会花的时间更多些,因为它要把它构建的所有文件都下载下来,但以后再构建就不会比正常构建更慢了)。

微软对于 Git 的投入

要让这种方法可以正常工作,就要改进 Git 访问文件的方式。大家平时不一定会注意到,旧版的 Git 在访问本地存储的文件时,它通常会扫描比它真正需要的更多的文件。如果你去年已经注意到微软曾向 Git OSS 项目提交过改进性能的代码,这就是原因所在了。

Jeremyepling 写道:

我们微软 Git 团队为 git/git 和 git-for-windows 做了许多贡献,来提高 Git 在 Linux、Mac 和 Windows 等上面的性能。在 Git 2.10 中,我们做了许多工作来使交互式 Rebase 操作更快。根据包含在 Git 源代码中的一种基准测试结果看来,交互式 Rebase 的最终表现是在 Windows 速度提升了 5 倍,在 MacOSX 上提升了 4 倍,在 Linux 也还有 3 倍的提升。

对 Git 的一些改进列举如下:

Git 虚拟文件系统

Git虚拟文件系统GVFS 的原型是在客户端用一个文件系统驱动,以及一个支持 GVFS 的 Git 版本实现的。这需要“Windows 10 周年”版,或更新的版本。只要 Git 库支持了 GVFS,那平时常用的 Git 命令就仍可以照常使用。GVFS 子系统基于文件系统工作,它会在背后从服务器下载任何你需要的文件。

因为 GVFS 客户端库是开源的,这也是大家研究我们是如何在 Windows 上把虚拟文件系统实现为驱动的好机会。

在服务器端,你需要一些实现GVFS 协议的东西。现在,这指的是 Visual Studio 团队服务,这协议是开源的,所以别的服务提供商也可以提供相同的能力。GVFS 协议本身也是很简单的,包含四个类 REST 的接口。

阅读英文原文Solving the Large Repository Problem with the Git Virtual File System

Git语言 & 开发