为什么需要
传统自托管 Git(Gitea、Forgejo、GitLab)都把仓库放在本地 NVMe + 一个数据库记录「哪个仓库在哪台机器」,这套架构在仓库超过单机容量时就崩——NFS 早被证伪,复制到多个 pet 节点要三阶段提交 + 副本集。
walgit 走的是完全不同的路线:对象存储(S3 / GCS)才是真源,每台 walgit 实例都是一次性缓存。
- push 当成不可变对象写进桶日志
- 一份微型 manifest 用 compare-and-swap 写入
- CAS 本身就是共识——没有选举、没有 quorum、没有 primary
- 任何实例都能接 push,两个实例抢同一份 commit 也不会都成功
- 全新实例从日志读出来就有完整仓库
- 读路径先查 store 是否变化(一般 304),不需要协调
- 压缩只做一次(持 lease 的人做),结果写回日志,其他节点下载压缩好的 pack
这套架构源自 Cursor 工程团队 2025 年发的博客《Git at any scale》(Cursor 内部代号 Continuity),walgit 是它的开源 Rust 实现,针对「仓库比机器还大」的单机场景做了适配:远程读(HTTP range)、历史 pack(commit / tree 留本地,blob 走桶)、bundle-uri(克隆字节直接走 CDN)。
怎么用
- 准备一个 S3 兼容桶(AWS S3、MinIO、Cloudflare R2 都行),写一份
walgit.toml配置 server + store WALGIT_TOKEN_ME=$(openssl rand -hex 24) walgit serve --config walgit.toml- 客户端 push 用标准 git:
git -c http.extraHeader="Authorization: Bearer $WALGIT_TOKEN_ME" push https://git.example.com/acme/app.git main - 横向扩展直接复制实例——所有机器指向同一桶,自动成为同一逻辑节点
支持的功能:Smart HTTP v0/v2 完整协议、ls-refs 前缀、filter / shallow / deepen / sideband-all fetch、receive-pack(含原子推送、删除、tag、push options、report-status-v2)、<owner>/<repo> 命名空间、sha1 + sha256 双 hash、bundle-uri(每周全量 + 每日链式 + 每小时增量)、Git LFS 批量上传、Web UI(React 实现)、JSON API + SDK、per-repo push 策略、Webhook。
注意事项
- 仍然依赖对象存储:S3 / GCS 账单要算进去,按读写次数 + 存储量计费;冷仓库便宜,大仓库流量贵
- bundle-uri 是真正的杀手锏:克隆 / fetch 走 CDN 不走服务器流量,节点本身可以很小
- 生态新:5 天内 Stars 2.2K+,118 forks,但相比 Gitea / Forgejo 还在早期,生产用要自己压测
- GitHub 上没填 description:仓库元数据简陋,但 README 13800+ 字符写得非常详细
- 没数据库意味着没后台管理界面:Web UI 是只读的,所有运维靠配置文件 + WAL