使用 Git-upload-pack 简化 CI 集成

Arnold Noronha Uncategorized 2025年5月9日 2025年5月10日 4 分钟

在 Screenshotbot 的早期,我们做出的一个决定是读取您的 GitHub 仓库。

事实证明,这对我们来说是一个巨大的优势。它让我们的客户在使用我们的产品时更加有信心,并且更容易通过大型企业的安全审查。

而且,它也让我们更容易与其他 Git 提供商(如 GitLab、BitBucket 或 Phabricator)集成。如果我们不依赖自定义 API,我们的代码就可以为所有人工作。(我们仍然需要访问他们的 API 才能在 Pull Request 上发表评论,但在许多平台上,该权限更加精细。)实际上,我们的部分功能也可以在自托管的 Git 仓库上运行。

但是,即使我们不需要读取您的仓库,我们仍然需要访问“图”来进行诸如“找到我们有可用构建的最后一个提交”或“找到最佳合并基础”之类的决策。我们将讨论我们过去是如何做到这一点的,并且还将讨论我们使用 git-upload-pack 协议的新功能,如果您想了解 Git 内部原理,这可能是一篇有趣的文章。

昨天之前我们是如何做的

为了实现这一点,Screenshotbot 在我们的服务器上为每个仓库存储一个“commit-graph”。一个 commit-graph 只是提交的 SHA:对于每个提交,我们存储它的父级。我们不存储关于您的 Git 仓库的任何其他信息。

在您的 CI 运行中,我们会使用类似于 git log --all --pretty="%H %P" --max-count 100 的命令来查看最近的 1000 个提交。我们将它们上传到我们的服务器(同样,只有 SHA),服务器负责将这些图与我们已经知道的图合并。实际上,我们总是可以访问完整的图。

但这确实会导致一些问题:

使用 git-upload-pack

我们最近与一位客户合作,他们绝对需要 shallow clones。如果不使用 shallow clones 克隆他们的仓库,会使他们的构建增加几分钟。

我们想避免依赖 GitHub 特定的 API 来解决这个问题。同样,这很难维护,而且更难测试。这也意味着我们的用户需要执行额外的配置步骤来授予我们 API 访问权限,这是我们不想做的。

但是我们意识到这一点:我们大多数客户的 CI 作业已经具有对其 Git 仓库的 SSH 访问权限。肯定有一种方法可以直接通过 SSH 获取我们需要的信息,并且希望是高效的?

确实有,那就是 git-upload-pack

当您克隆或拉取仓库时,您的本地客户端会建立到远程服务器的 SSH 连接并运行 git-upload-pack。这会启动一个交互式会话,协商需要传输哪些信息。

官方协议是一个丑陋且混乱的二进制协议,并且对实际上不应该进行微优化的事情进行微优化(恕我直言)。但是,如果您忽略确切的线路格式,则该协议大致如下:

Packfile 只是对象的集合。对象可以是提交、关于文件和目录的“tree”信息或 blob。粗略地说,Packfile 是 <类型, 长度, zlib 压缩内容> 的集合。

git-upload-pack 连接到 Screenshotbot

现在我们了解了 git-upload-pack,我们可以执行以下操作:

这就是全部了。据我们所知,没有现有的命令行方式可以做到这一点,所以我们不得不从头开始实现该协议。如果您有兴趣,这是实现

注意事项

如果您尝试使用 git-upload-pack 协议,请注意以下事项:

总结

TL;DR:如果您是 Screenshotbot 用户,您现在可以使用 shallow clones 了!

如果您在这里寻找如何使用 git-upload-pack,我希望这篇评论和我们的实现能帮助您。欢迎在下面提出任何问题。