Epic 开源 Lore 版本控制系统,和 Git 走了一条相反的路
- 采用内容寻址和BLAKE3哈希,直接对原始字节计算地址,存储层独立可复用
- 文件切块实现跨文件去重、大文件小改动高效、随机读,提供FastCDC和固定大小两种策略
- 默认稀疏克隆,只拉取视图声明部分,按需懒加载,分支轻量指针
- 集中式架构但支持离线操作,多租户通过分区实现内建安全模型
- API优先设计,CLI仅为薄封装,提供多种语言SDK,通信协议使用QUIC/gRPC
2026 年 5 月 21 日,Epic Games 在 GitHub 上悄悄上线了一个新仓库。
短短一个月,它冲到 7206 颗星,308 个 fork,主语言 Rust,许可证 MIT,最新版本 v0.8.4(6 月 26 日刚发布)。仓库的名字叫 Lore,官方一句话定位是「下一代开源版本控制系统」。
说真的,看到这个标题我第一反应是,Git 已经统治世界了,还能「下一代」到哪去?但点开它的设计文档看了三小时之后,我得承认 Epic 这次确实在想一件不一样的事。Lore 不是要再做一个更好的 Git,它是冲着 Git 从根上就处理不好的那批负载去的,巨型二进制文件、百万级文件数、几千人并发、多租户托管。
这篇文章我把它的核心设计拆给你看。
先搞清楚,它要解决什么
要做游戏和影视的人都知道一个痛。一个 Unreal Engine 项目,光美术资产就是几十上百 G,贴图、模型、音频、视频,全是二进制大文件。你拿 Git 去管这种东西会非常难受。
你想想看,Git 的存储单元是「一个文件一个对象」。一个 5G 的视频文件改了一个字节,整个 5G 得重新存一遍,hash 也全变了。Git 的官方补丁 Git LFS 本质是外挂,把大文件踢到一个独立存储里,治标不治本。
所以游戏行业几十年来的默认选择是 Perforce,一个集中式、闭源、商业授权的版本控制系统。Perforce 能扛大文件,能做文件级锁,但它要求你几乎每个操作都得跟服务器来回通信(p4 edit 才能改文件),还要求你手动 p4 reconcile 让外部改动可见。体验上相当「重」。
Epic 自己就是 Unreal Engine 和 UEFN(Unreal Editor for Fortnite)的母公司,他们天天泡在这套工作流里。Lore 的诞生动机非常清晰,没人比他们更懂「集中式管大文件」这件事的痛。
它文档里给自己划了三条硬约束。负载必须是内容无关的(代码、配置、构建产物、任意二进制,一视同仁,没有谁是特例);必须每个维度都大(百万级文件、TB 级单文件、百万级历史、几百条分支、几千并发用户、几百个仓库共享一个后端);必须是集中协调的(有一个逻辑上的唯一真相源,但开发者还能离线工作)。
一句话,它要的是 Git 的内容寻址优雅,加上 Perforce 的集中式大文件能力,再叠加多租户安全和一份完全开放的协议规范。
这种组合,确实在现有系统里找不到。
内容寻址,但是「裸字节」
Lore 的存储底座是内容寻址,这一点跟 Git 同源。但有一处差异特别关键,我读文档时专门停下来琢磨了一会。
Git 算一个对象的 hash 时,会先在内容前面拼一段自己的头,比如 blob <size>\0。所以一个文件在 Git 里的 SHA-1,不等于这个文件原始内容的 SHA-1,它依赖 Git 的那层包装。你要是想脱离 Git,拿一段外部工具去算同一个文件的地址,算出来对不上。
Lore 反过来。它的 fragment(存储的最小单元)hash 是直接对原始字节算的,用的是 BLAKE3 而不是 SHA-1。文件的元信息(大小、标志位、压缩算法)单独放在 fragment header 里,不参与 hash 计算。
这意味着什么呢?你拿 b3sum 这个公开工具去 hash 一个文件,跟 Lore 客户端算出来的地址完全一致。任何外部工具、任何第三方实现,只要有原始字节,就能推导出 Lore 地址,不需要懂 Lore 特有的任何封装。
这是个很「干净」的设计决策。它直接把存储层变成了一个可以脱离 Lore 独立使用、独立审计、独立重新实现的东西。文档里反复强调一句话,存储子系统是一个一等公民的公开 API,版本控制只是它的一个消费者,不是拥有特权的上层。这个边界感我挺欣赏的。
hash 选 BLAKE3 而不是 Git 的 SHA-1 也不是随便选的。BLAKE3 是现代密码学哈希,速度快、抗碰撞,而 SHA-1 早就被证明在对抗场景下不安全了。
切块,是这篇文章真正的硬核
到这里我必须讲 Lore 最值钱的一个机制,文件切块(chunking)。这是它跟 Git 拉开代差的根本原因。
还记得前面说的吗,Git 是文件粒度。Lore 把文件往下再切一层,切成一个个 fragment,每个 fragment 独立寻址。一个文件的「身份」不再是它全部字节的 hash,而是「它所包含的那串 fragment 地址列表」的 hash。
这一刀切下去,三个老大难问题全解了。
第一,跨文件去重。两个文件有一 G 相同内容、只有一 KB 不同,在 Git 里它们是完全不同的两个对象,得各存一份。在 Lore 里,相同的那一 G 对应相同的 fragment,天然只存一份。
第二,大文件的小改动。一个多 G 的二进制文件改了一个字节,Git 要把整个文件重新上传。Lore 只要重新存被改动的那几个 chunk,加上新的地址列表。
第三,随机读。想读一个超大文件中间的某一段,Git 模型下你得先把整个文件拉下来。Lore 里,fragment 引用里记着每个 chunk 在重组内容里的字节偏移,做一个二分查找就能定位到覆盖目标偏移的那个 fragment,只拉你要的那一小段。
具体怎么切?Lore 提供了两种策略,这个细节特别能看出设计者的功力。
内容定义切块(FastCDC)。用一个滚动 hash 滑过文件,凡是 hash 命中某个魔数模式的地方就切一刀,受最小 32KB、平均 64KB、最大 256KB 的尺寸约束。切点是内容决定的,不是位置决定的。所以你在文件开头插一段,后面的切点整体跟着平移,没变的内容还是落在原来的 chunk 里,去重得以保留。这对「大文件局部编辑」的场景特别友好。
固定大小切块。就按固定偏移切,不扫内容。切分几乎零成本,任意字节区间的地址可以纯靠偏移算出来,连文件都不用读。
讲到这里,坦白讲我一开始觉得,FastCDC 明显更优啊,为什么要保留固定切块。读完那一段我才知道这里头有个很深的坑。
FastCDC 有个隐性问题。真正稀疏的写入要求「复用上次未改动部分的切点」。否则你从头重新跑滚动 hash,魔数可能在不同的地方命中,导致没改动的区域也产生新的 chunk,去重直接崩盘。所以实现上要用一种「时间一致性」策略,重新切块时先复用旧切点,只在内容真的变了的地方才重新跑 CDC。
但这又引入一个代价,地址不再是规范的。一个文件的地址依赖它的切块历史,而不只依赖字节。同一份内容,从头切块和增量切块可能算出不同的顶层 hash。换句话说,「不同地址 ⇒ 不同内容」这条性质,在 CDC 下不成立。
固定切块就是为了保住这条性质。切点只看偏移,同一内容永远切出同样的 chunk、同样的顶层 hash,任何一方只读那个 chunk 的字节就能算出确定的地址。
所以 Lore 的态度是,两种都给你,让上层应用自己决定。哪个内容类型用哪个策略,是调用存储子系统的应用说了算,Lore 自己不内置映射。CDC 给需要跨编辑去重的源码和大数据文件,固定切块给需要规范地址的场景。存储子系统压根不在乎一个 chunk 是哪种策略切出来的,到它眼里都是普通的内容寻址 fragment。
这种「我不替你做决定,我把权衡给你讲清楚」的做法,我觉得是工程素养的体现,而不是偷懒。
还有一个细节值得一提,递归切块。一个大文件能切出成千上万个 chunk,那张地址列表本身可能就几百 M,超过单个 fragment 的上限。Lore 的处理是把列表本身也切块、也当成一个带标志的 fragment 存起来,于是文件被描述成一棵 fragment 列表的树,而不是一张扁平的表。每一层独立寻址、独立去重、独立懒加载。读一个字节区间,存储层自己顺着树走,只取覆盖那段所需的 fragment,还能并行乱序拉取。对使用者来说,API 层面这棵树几乎是隐形的。
默认稀疏,而不是默认全量
Git 的 clone 默认把整个仓库的历史和树全拉下来。Lore 的 clone 默认只拉你的 view(.lore/view)声明的那一小部分,剩下的全懒加载,用到才去远端取。
这不是 Git 那种 opt-in、还有一堆毛刺的 partial clone 加 sparse-checkout 的组合拳,而是 Lore 的默认行为。一个工作目录可以一直很轻,按需水合。这对「TB 级仓库、我只想改两个文件」的场景是刚需。
同样的哲学贯穿在分支上。分支在 Lore 里是一个轻量的可变指针,创建和切换几乎零开销,不复制底层数据。这块跟 Git 的思路一致,Git 早就证明了「分支是指针而不是目录拷贝」是对的设计,Lore 没必要重新发明。
顺带说一句它对「工作区真相」的处理。Perforce 要求你手动 reconcile 才能让外部工具的改动可见,Lore 则是直接读文件系统。你用什么编辑器都行,Lore 走一遍工作树就能发现变化。这个体验差距,用过 Perforce 的人都懂。
集中式,但能离线,而且天然多租户
Lore 在拓扑上选了集中式,这是它跟 Git 最根本的分歧。
Git 是真正的对等分布式,每个 clone 都是完整参与者。Lore 认为它要服务的那种负载(访问控制、审计、冲突仲裁、存储分层)需要一个逻辑上的唯一真相源。这个「源」不一定是单台机器,一个 Lore 部署可以是一组服务器进程,前面挂缓存、后面挂只读副本和分层存储。
但集中式不等于必须联网。Lore 的常规客户端操作,暂存、提交、分支、切换、diff,全都能在本地完成,不碰远端。离线时把 revision 排队,等上线再推。这点它把 Perforce 的「在线才能干活」彻底改掉了。
多租户这块是设计里最「承重」的一个概念,叫 partition(分区)。每个分区是一个 16 字节的不透明 ID,授权会把一个会话绑定到一个分区,所有内容查找都在分区作用域内进行。分区就是访问边界。
一个仓库在存储层对应一个分区。想要一个目录有独立的访问策略?那就把它提升成一个独立的子仓库,用 link 链回父仓库,子仓库有自己的分区、自己的权限。每次操作,服务器自己查 ACL,不是靠外层平台去包一层。
这套模型的好处是,Lore 的服务器可以堂堂正正地当多租户公共服务来跑。Git 本身没有多租户安全模型,租户隔离全靠 GitHub、GitLab 这些周边基础设施去补。Lore 把它做进了系统内核。
API 优先,不是 CLI 优先
这一条对开发者尤其有启发。
Git 是 CLI 优先长出来的。第三方想集成,要么去解析 CLI 输出(跨版本不稳定),要么依赖 libgit2,一个独立项目用宽容许可证重新实现 Git 的核心,因为 Git 本身是 GPL-2.0、压根不是设计来被链接的。libgit2 被广泛使用(GitHub、GitLab、Azure DevOps 都用),但它永远跟不上 Git 自己的功能演进,两个代码库、两套节奏。
Lore 把这个坑直接避开了。它的首要产物是 C 库加各种语言绑定,CLI 只是一层薄薄的封装,跑在同一个 API 上。官方同时提供 JavaScript、Python、C#、Go 的 SDK,还规划了 Rust、C/C++ 的原生接口。没有第二个平行实现需要你去同步。
通信协议也走的是新路线,服务器是独立的服务进程,说 QUIC 和 gRPC 这种带类型的二进制协议,结构化命令、服务端管理的会话、JWT 作用域授权都内建在协议里。对比一下,Git 服务器本质上是个走通用传输(SSH、HTTPS、git-daemon)的线上协议端点,结构化命令、会话、授权作用域全得靠前面的平台去补。
它诚实地保留了什么,拒绝了什么
我特别喜欢文档里 §25.4 那一段,标题叫「我们保留了什么,拒绝了什么,以及为什么」。一个项目敢把取舍讲这么白,可信度会高很多。
它说,Lore 在没必要创新的地方绝不创新。内容寻址、revision 的 Merkle DAG、分支是指针、文本的三方合并、文本无法合并时的文件级锁、稀疏懒加载,这些前人证明过是对的东西,原样继承。
然后它列了拒绝清单,每一条都说清了为什么。拒绝 Git 的分布式默认模型,因为目标负载需要唯一真相源来管访问控制、审计和持久性,规模也远超「每个 clone 都是完整副本」能承受的。拒绝 Git 的文件粒度,因为多 G 的二进制塞不进这个模型,fragment 级寻址在更细粒度解决了同一问题。拒绝 Perforce 的在线工作流,因为开发者不该断网就停摆。拒绝 Perforce 的 delta 编码存储,因为内容寻址天然提供了 fragment 粒度的去重,不需要每文件一套 delta 机制。拒绝 Mercurial/Sapling 的文本优先取向,因为目标负载根本不是文本形状的。
作者最后那句总结很到位,把这些决定剩下的东西拼起来,就是一个集中式、二进制优先、多租户、稀疏克隆、内容寻址、带公开规范的 VCS。单独拎哪一条都不新鲜,重点是没有任何一个现存系统同时承诺了全部。
几个得说清楚的边界
按我这边的规矩,讲项目不能只吹不踩。Lore 现在的边界我得诚实摆出来。
它是 pre-1.0。README 顶部一个醒目的 NOTE 写得很明白,接口、磁盘格式、API 都可能在版本间变动。现在最新是 v0.8.4,拿去上生产得自己掂量。它内置在 UEFN 里是真的,但开源版能不能 talk 到 UEFN 还不行,因为 UEFN 用了一套开源项目没法一起发布的私有压缩格式,Epic 正在把 UEFN 迁到开源版用的同一套开放压缩格式上,这个 gap 还没填平。
CDC 的规范地址问题前面讲过了。同一个内容可能有多个合法地址,如果你的工作流强依赖「地址唯一对应内容」,你只能用固定切块。这不是 bug,是数学性质,但选型时必须知道。
压缩目前只有 Zstd。好在架构是开放的,codec 列表可扩展,地址算的是未压缩内容的 hash,换压缩算法不改地址,这点设计得留了余地。
分叉(fork)还没实现。设计文档 §26 把它列为开放问题,想要的特性是 copy-on-read 的 fork,让建 fork 又快、存储成本只跟 fork 实际碰到的内容挂钩,而不是一上来就把源分区每个 fragment 都注册一遍。模型已经能容纳,API 和工具还没落地。
生态还很年轻。35 个 open issue、35 个 PR,topic 只挂了一个 open-source,社区讨论主要在 Discord。对一个想取代 Perforce 和补 Git 短板的项目来说,路还很长。
对我们普通开发者的启发
你大概率不会明天就把仓库迁到 Lore。但它身上有几条思路值得带走。
第一条,遇到「特例」就把它做成一等公民。Git 把二进制当文本模型的二等公民,于是需要 LFS 这种外挂补丁。Lore 干脆把所有内容当成不透明的字节流,文本反而是叠在上面的能力。反过来想你自己做的系统,有没有哪个「特例」其实该被扶正?
第二条,把存储和业务彻底解耦。Lore 的存储子系统是可以独立使用的公开 API,版本控制只是它的一个消费者。这种分层让你的核心能力可以被复用、被重新实现、被独立审计,而不是跟业务逻辑焊死在一起。
第三条,API 优先能省掉未来十年的债。Git 当年 CLI 优先,结果养出了 libgit2 这个永远追不齐的平行实现。Lore 从第一天就 API 优先、CLI 只是壳,所有语言绑定共享同一份事实。如果你在做工具,这件事越早定越好。
第四条,敢公开说「我不替你决定,权衡给你」。两种切块策略并存、由应用选,这种设计比强行二选一要诚实得多,也更适合承载真实的多样性需求。
Epic 这次开源 Lore,与其说是在送一个产品,不如说是在公开他们想了很久的一套工程答案。至于这套答案能不能长成下一代标准,得看接下来一两年社区的判断。
但有一点我挺确定,一个能同时把 Git 的优雅、Perforce 的能力和多租户安全揉在一起、还全部 MIT 开放的项目,至少值得你花一个下午把它的设计文档读完。
地址我放这儿了,https://github.com/EpicGames/lore,文档站是 https://epicgames.github.io/lore/,那份 system-design 长文尤其推荐。
评论互动