Token导航 LogoToken导航

AI 资讯 · AGIHunt

OpenAI 为 monorepo 启用新 Git 后端 Pando

更新 2026-11-03AGIHunt正文 4203字阅读约 14分钟10 张图片

刚刚,OpenAI 在工程博客上发了一篇长文,讲他们给自家 monorepo 换了一个新的 Git 后端,名叫 Pando。

图片

现在 OpenAI monorepo 里的全部开发,都已经跑在 Pando 上了。

这事,怎么感觉有点眼熟?

其实。在三周前,Anthropic 也刚刚讲完他们是怎么把整个 monorepo 从 git 上迁走的,见:刚刚,Anthropic 重新发明了 git。

不到一个月,御两家便先后讲起了同一个问题。Claude 和 Codex 让写代码的速度暴涨,结果被那些为人类规模设计的源码控制给拖住了。

只是两家给出的解法,几乎是反着来的。

Anthropic 是把 git 推倒重来,自研了一整套版本控制系统 Scion。

而 OpenAI 则留下了 Git,客户端和协议都保持兼容,工程师和 agent 该怎么用还怎么用,只把服务端整个换掉了。

换完之后,中位 push 延迟降了近 80%,现在每天要分发近 1 PB 的 Git 数据。

源码控制的瓶颈

01

Anthropic 今年 5 月平均每天合并约 2500 次代码变更,到了 9 月,已经超过了每天 3 万次。

仅 main 分支的历史就接近 100 GB,CI 每天要 checkout 约 2500 万次,高峰时得靠几百台内部 git 镜像来扛。

OpenAI 这边,过去一年 monorepo 的体量和需求,也涨得飞快。

有了 Codex,工程师能同时推进的事情多了很多,于是分支更多了,push 和 merge 更多了,开发环境、研究任务和 CI 发起的 fetch、clone 也更多了。

而且这些需求还会叠加,一个工程师手上同时有好几个改动在等 CI,每一次 push 又会让好几个任务去 fetch 同一个 commit。

图片
两家撞上同一堵墙

从去年年底开始,OpenAI 支撑 monorepo 的那套系统就渐渐扛不住了。fetch 和 push 都很慢,merge 也会失败,可靠性一度掉到了 90% 以下……

于是 OpenAI 开始琢磨,到底是 Git 本身到了极限,还是他们服务 monorepo 的方式到了极限呢?

两条路线

02

Anthropic 认为是 git 本身。

git 是为离线、分布式设计的,每一份 clone 都是一个完整的仓库。可当客户端几乎全是永远在线的 agent、仓库又越来越大时,git 最擅长的分布式和离线工作,反倒成了扩展的阻碍。

所以 Scion 从客户端、仓库格式到协议,全都是新的。查历史、做 blame、创建提交、merge 和 rebase 都交给服务端来做,客户端自己几乎不做计算,只维护一个有上限的本地缓存。

图片
换车,还是换发动机

OpenAI 的答案则是后者。

Git 起源于一个靠邮件交换内核补丁的社区,二十多年来,开发者一直在围绕 Git 调整自己的工具和流程,OpenAI 不想丢掉这些积累。

所以 Pando 只是一个后端,工程师和 agent 手里用的还是原来那个 Git,工作流一点都不用改。

而且建在 Git 之上,客户端上做的改进,OpenAI 以外的人也能用得上。

提交与分支

03

Scion 里,每一次 commit、rebase、merge 都直接发生在服务端,压根没有什么需要 push 的。

它的基本单位叫 changeset,是预写日志里的一条记录,存的是相对第一个父提交改了哪些文件,以及指向文件内容的内容寻址指针。

创建一个 changeset 的开销只跟改动大小有关,跟仓库多大没关系,而且全程无锁。

除了 main 和发布分支,Scion 连 ref 都不太鼓励用,移动一个 ref,也只是对 Spanner 里的一行数据做一次 compare-and-swap。

Pando 则还守着 Git 的老规矩,先在本地 commit,再 push 上去。

不同的是,Pando 让每个分支各自独立更新,推到不同分支的 push 可以完全并行,只有同时推同一个分支时才会冲突。

图片
提交在哪里落地

代价是,同时看好几个分支的人,看到的未必是同一时刻的状态。

说起来,两家其实都放弃了「整个仓库在同一时刻保持一致」这件事。Anthropic 是干脆不鼓励用 ref,OpenAI 则让各个分支各管各的。

读取与 packfile

04

Scion 的客户端从不告诉服务端自己已经有什么,只说自己要什么。它从根树开始,查本地缺了哪些 tree 再递归地请求,最后对 blob 重复一遍。

每个请求都对应一个永远不会变的对象,所以 scion-daemon、scion-cache、scion-server 三层缓存都能直接应答。

Scion 里,也干脆没有 packfile 这种东西。

而 Pando 正好相反,它把 packfile 当成了存储和分发的基本单位。

push 上来的 packfile 原样存进 blob 存储;有人来 fetch 时,Pando 从已经存好的 pack 里挑出对方需要的那几个,直接把下载链接发给对方。

图片
按件取,还是拿现成的包

这样客户端可能会多下载一些本地已经有的对象,但服务端几乎不用计算,同一份数据可以原样发给大量读者。

两家的缓存命中率,也都在 99% 以上。

一家把 packfile 扔了,一家却把 packfile 当成了宝……

性能数据

05

Anthropic 公布的数字更全一些。

Scion 基础设施的 CPU 和内存占用,只有之前 git 镜像方案的 1/200。

开发环境里 status 快了 207 倍,agent 新建一个 worktree,从 7.1 分钟降到了 5.1 秒。

算下来,agent 每提交一个变更请求,花在源码控制上的时间从 34 分钟降到了 3.7 分钟,每天省下约 1.1 万个 agent 小时。

OpenAI 给的则是另一组。

merge 更可靠了,中位 push 延迟降低了近 80%;每天分发近 1 PB 的 Git 数据,用的只是少量 CPU 核。

图片
各自的成绩单

它还曾在一小时内分发过超过 57 TB 的 Git pack(摊下来每秒将近 16 GB……),其中 99% 以上来自本地磁盘缓存(两家的口径不一样,这里就不硬比谁更快了)。

开发周期

06

Scion、代码评审工具 Antique,再加上另外六个左右的相关系统,是 Anthropic 一个 7 人团队花了 5 个月做完、并完成迁移的。

团队手写的导入管道原型,交给 Claude Mythos Preview 的一个早期快照重写之后,内存只剩原来的 1/20,速度快了 4 倍。

参与项目的工程师现在都认为 Claude 写代码比自己强,而他们很多时候的工作,就是对 Claude 说「不」。

而 OpenAI 这边,Pando 是用 Codex 造出来的,从立项到上线只用了 10 周。

核心设计只出自一位工程师,实现交给了多个 agent 团队,最后再由几位工程师部署上线。

图片
7 人 5 个月 vs 1 人设计、10 周上线

另外,Pando 其实从今年 4 月就开始承接生产流量了,只是到今天才拿出来讲,OpenAI 的 Thibault Sottiaux 转发时也说,团队终于可以稍微讲一讲了。

所以「继 Anthropic 之后」,说的只是发文的先后。

对外分享

07

Anthropic 明确表示 Scion 不对外提供。

它和 git 差别太大,别家要用就得把整个开发流程都迁过去,还得配套它自己的代码评审、代码搜索和 CI。

不过他们在文末给 git 社区提了几个方向:git 服务端不必和客户端用同样的方式工作,git 平台可以提供全面的服务端 RPC,协商协议也不必是现在这个样子,各种 sparse 和 shallow 功能可以组合得更好。

图片
关起门给建议,还是送补丁

OpenAI 走的则是另一条路。

他们把客户端上的改进贡献回了上游 Git,自家的 Git 版本也公开在了 GitHub 上,还做了个公开的补丁追踪页,并表示正在探索把 Pando 背后的技术分享给用户。

回头再看 Anthropic 提的这几条建议,「服务端不必和客户端用同样的方式工作」「协商协议不必是现在这个样子」……Pando 做的几乎正是这两件事。

Git 的对象与 ref

08

说回 OpenAI 这篇博客本身。

源码控制的扩展有两个基本方向,一是很多写者往不同的分支 push,二是很多读者 fetch 同一份历史。

Pando 给自己定的目标也是两个,写入要能横向扩展,读取则要快到能把客户端的带宽跑满。前面说的「分支各管各的」和「复用 packfile」,对应的就是这两个方向。

要弄明白这两个选择,得先看看 Git 是怎么存储和传输历史的。

一个 Git 仓库要同步的东西,分为对象和 ref 两种。

对象有四种。blob 存的是单个文件的内容;tree 把名字对应到 blob 或别的 tree,组成目录结构;commit 指向一棵根 tree 和零到多个父 commit,串起仓库的历史;另外还有 tag。

对象用「类型 + 内容」的哈希来标识,而且不可变。改了一个文件就会生成新的 blob,没改的文件和目录则直接复用已有的对象。

分支和 tag 则是指向 commit 的 ref。在分支上提交,就是新建一个 commit,再把分支的 ref 指过去。

图片
Git 对象之间的引用关系

比如图里,refs/heads/main 指向 C2,C2 的父提交是 C1。

两次提交里的 parser.py 分别指向 B1 和 B2,而 docs/ 目录没变,两棵根 tree 便共用了同一个 T3。

对象在 Git 里有两种存法。loose 是每个对象单独一个文件;packed 则是把对象打包进一个 packfile,相似的对象之间可以存成 delta。

图片
packfile 里的 delta

像 parser.py 的两个版本,packfile 里完整存下 A,B 只记成「复制 A 的全部内容,再追加 parse_file()」,另有一个 .idx 文件按对象 ID 来定位它们。

fetch 的时候,客户端告诉服务端自己想要哪些 commit、已经有哪些。

服务端顺着父提交、tree 和 blob,找出从「想要的」commit 出发能找到的所有对象,也就是所谓「可达」的对象,去掉客户端已有的,把剩下的打包成一个 packfile 发过去。

全量 clone 一开始什么都没有,所以要拿走全部可达对象。

push 则反过来。客户端会参照服务端宣告的 ref,不再发送服务端已有的对象,所以推上去的 pack 是依赖服务端已有对象的,光凭这个 pack 本身,还原不出新的历史。

要把一个 Git 仓库复制到多台服务器上,对象和 ref 都得同步。副本在对外提供某个 ref 的新值之前,必须先能提供从它可达的所有对象。

系统还要协调相互竞争的 ref 更新,也就是从同一个旧 tip 出发、想把同一个分支推到不同新 commit 的那些 push。

如果给一个仓库的所有写入强行排一个总顺序,那推到无关分支的 push,也只能互相等着。

三阶段提交与预写日志

09

设计 Pando 时,OpenAI 先评估了两种扩展 Git 的常规做法。

文章标签OpenAIAnthropic

资讯来源:由AI资讯编辑整理自互联网公开内容,版权归原作者所有,未经许可,不得转载。