开发者生态
evening
我教了一个桶说 Git
2026-06-24
1 阅读
xena
如果我只是将 git 服务器指向对象存储桶会发生什么?当我将代理沙箱移植到 Go 时,我在 billy(Go 的文件系统抽象)之上构建了所有内容。该项目的全部技巧是教 Tigris 存储桶的行为足够像文件系统,以至于 shell 解释器及其工具无法区分其中的差异。比利是使整个立面到位的关键层。在我让一切正常工作后,我了解到我在正常用例之外使用 billy way。它最初是为 go-git 制作的,go-git 是 git 协议和数据格式的纯 Go 实现。它根本不依赖于现有的 /usr/bin/git 二进制文件。 billy 文件系统接口上的每个方法的存在纯粹是因为 go-git 需要它。这给了我一个可怕的想法:我已经有了一个可以像文件系统一样嘎嘎叫的存储桶,而 go-git 的母语是“文件系统”。这可以正常工作吗™?让我们来看看吧。 Git 一直是一个对象存储如果你剥去瓷器,git 存储库包含 4 个基本内容:对象或压缩的数据块。任何单个存储库中的大多数对象都是文件。树,或映射到其他对象的对象。 TL;DR:树是文件夹。提交,或指向一棵树及其父提交的对象。这使您可以确定哪些文件属于一个逻辑更改集。引用、分支和标签,它们是指向对象堆的微小的可变指针。注意 在我开始研究这个之前,我的印象是 git 只存储对空文件夹完成的补丁,这就是它重建存储库历史的方式。事实并非如此。它实际上会跟踪整个文件,这解释了为什么大的二进制 blob 会如此频繁地伪造工具。 diff 心理模型对于日常使用 git 来说效果很好;只是存储层出了问题,也就是这篇文章所在的层。例如,假设我刚刚创建了一个新的 git 存储库并向其提交了 README.md。 .git 文件夹的树看起来像这样: $ tree .git .git ├── COMMIT_EDITMSG ├── config ├── HEAD ├── index ├── 对象 │ ├── 5e │ │ └── b8151eb669aa4467b6dea2c4bce19183cd0b41 │ ├── 6a │ │ └── 6a8ecfcae2632152486aca3d9150ef83dedd66 │ ├── f4 │ │ └── d2487a1c6d742c8037c0296ddf80625190bd80 │ ├── 信息 │ └── pack └── refs ├── Heads │ └── main └── Tags 正如你所看到的,共有三个对象。其中之一是提交 5eb8151eb669aa4467b6dea2c4bce19183cd0b41 ,下一个是树,最后一个是 README 文件。主分支还指向该提交: $ cat .git/refs/heads/main 5eb8151eb669aa4467b6dea2c4bce19183cd0b41 最酷的部分是其中一半是内容寻址的。内容寻址位一旦提交就永远不会改变。 Git 对象非常适合 Tigris 的内部模型,因为它们是仅附加存储,就像构建 Tigris 的基本模型一样。经常发生变化的是引用,它们被更新以指向最新的提交。不过,这些文件都很小,这意味着 Tigris 可以毫不费力地处理它们。然而,当我们在服务器上托管 git 存储库时,我们最终会创建单点故障。我们的 git 存储库托管在可能会崩溃的单台机器上。整个实现依赖于 git 对象与文件系统对象 1:1 关联,因为每个人(甚至 GitHub)都使用 git 二进制文件来实际存储文件。托管 git 存储库成为我们无状态云原生环境中最有状态的服务之一。当然,理论上 git 是去中心化的,但我们大多数人最终都使用它来将我们的 git 存储库放在一个正常运行时间实践存在问题的大型商店中:GitHub。公平地说,GitHub 的运营规模是我们所有人都无法真正想象的。自成立以来,他们一直在挑战极限,他们必须让 Engine Yard 不断为他们建造更大的服务器来处理负载。他们必须使用大型安装文件系统来完成所有操作,因为 git 工具没有给他们其他选择。对超出人类理解范围的恐怖的嘲讽现在假设这种怪异现象足以让您烦恼并采取措施解决它。要构建一个 git 服务器而不将所有内容存储在本地文件系统中,你必须以某种方式使用 git,而传统的选项实际上并不是那么好:如果你使用 git 二进制文件,现在你的“库”就是 git 进程的 argv,而你的错误处理是屏幕抓取输出。在内部,git 使用数十亿个子命令来实现其功能,而不是将其全部公开为库。代码库通过对 die() 的承载调用来保持在一起,这会杀死进程。如果您使用 libgit 链接到 git 的内部,您就会继承“当事情变坏时,die() ”行为,并且您的应用程序现在突然开始随机崩溃。这不利于正常运行时间。如果你尝试使用 libgit2(重写的实际上是一个库),你必须考虑到它被 GPL 搞乱了的事实(有一个链接例外,尝试解释一下 t