如果我的Git托管服务是一个静态网站生成器会怎样?

Lobsters Hottest 工具

摘要

作者探讨了用静态网站生成器替代传统Git托管平台的方法,以减少服务器负载,并介绍了'sorcery'这个工具,它能为Git仓库生成静态HTML,并使用客户端渲染来查看历史记录。

<p><a href="https://lobste.rs/s/6syfar/what_if_my_git_host_were_static_site">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/09/13 16:51

# 如果我的Git托管服务是静态站点生成器会怎样?——charlotte! 来源:https://char.lt/blog/2026/09/sorcery-repo-viewer 截至目前,我运行个人Git托管平台已有近半生时间 :o 我喜欢自己搭建开发基础设施,不仅因为我几乎总是身处距 `us-east-1` 120ms+ 的地方,更因为系统管理本身就充满乐趣 :3 2015年,我运行过Gogs实例,后来演变为Gitea实例,再后来又成为Forgejo实例。此外,我还为参与过的多个团队多次部署过GitLab/Forgejo。我热爱社区协作式的Git托管平台,而Forgejo在这方面表现优异!但我的Forgejo服务器频繁面临磁盘空间耗尽(因未更新的Git仓库打包时崩溃)以及在爬虫常规负载下宕机/OOM的问题。 对我而言,这显然是选错了工具类型:在我用于个人基础设施的小型机器上,这类软件难以承受互联网的“宇宙背景辐射”级负载。同时,我希望能简化体验,仅开放真正会用到的功能——Forgejo及其同类平台提供的功能远超我的需求:Issue、PR、Release、Wiki……一堆我并不在意的GitHub兼容特性,却不得不为它们付出代价 :\( ## 发布到开放网络 针对Forgejo资源耗尽的常规解决方案是通过Web应用防火墙(如Anubis)(https://anubis.techaro.lol/)屏蔽爬虫。该方案通过JavaScript工作量挑战来控制对Web应用的访问。但这与开放网络的理念相悖,对吧? 浏览器(作为“用户代理”)被迫执行对用户不友好的操作,运行近乎无用的代码(挑战的目的就是让CPU持续运转!),若拒绝执行,则完全无法显示任何用户相关信息。那些不支持JavaScript(或不支持即时编译JavaScript)的替代浏览器要么被彻底屏蔽,要么被迫经历极其冗长的等待。这加剧了现代网络的寡头文化——我认为这并非好事。 此外,部署此类方案无异于承认失败——我们屈服于“前端应用在真实互联网流量下无法正常工作”的预设。但当我们面对读操作远多于写操作的负载时,这难道不可笑吗?服务写入稀疏的数据在实践中应该非常廉价——GitHub Pages 曾多年仅用一台机器运行!! ## 极低服务器计算的Git仓库浏览 Sorcery(https://git.t4t.associates/char/sorcery)的核心设计形如静态站点生成器:当收到Git仓库更新时,它会为该仓库重建一批磁盘HTML——概览页面、各分支最新提交的目录树,以及每个文件在最新提交时的语法高亮源码渲染。 这种设计让我们通过一次性的预处理成本来服务大量未来请求,这意味着我们能抵抗爬虫负载(因为“发送后即忘”的操作成本几乎可忽略)。然而,由于为每个文件的每个版本提前渲染HTML成本过高,我们选择不提供静态的仓库历史视图。 但仓库历史浏览是Git Web界面的核心功能,因此我们直接提供 `.git` 目录,在JavaScript中实现基础的只读Git客户端,并在客户端渲染所有仓库“富视图”——尽管仓库站点生成器确实需要输出一些辅助JSON数据来帮助Git客户端(因为我们无法可靠地列出Git仓库中的目录),但这仍然是静态的! 由于浏览历史可能涉及大量不同对象(提交、树【即仓库目录列表】、数据块【即文件内容】)的请求,高延迟连接会导致直接对象获取和遍历极其缓慢。更糟的是,Git将这些对象以压缩的delta编码包文件形式存储:在 `linux.git` 中获取一个提交差异的朴素方式需要消耗超过400MiB带宽!而智能扫描范围会降低缓存命中率,并导致一系列依赖前序请求数据的瀑布式请求。 因此,作为一项非静态优化 [1](https://char.lt/blog/2026/09/sorcery-repo-viewer#fn1),我们还提供了服务端路由,可通过对象ID列表获取特定Git对象(`QUERY ///obj`),返回简单的二进制“Git对象包”格式,在客户端可直接解析。这减轻了客户端导航包文件的负担。 此外,即使对于松散对象仓库,我们仍可通过“智能获取”模式优化往返次数:根据特定访问模式遍历引用对象ID(例如通过 `commit.parent->parent->...` 遍历提交历史,或获取两个提交的树中的所有数据块以进行差异比较)。 ### 直接从松散Git对象获取历史 浏览器获取提交C,获知其父提交B,获取B,再获知并获取A。每次父提交查找都需要额外的往返。 **直接对象获取** ``` 浏览器→服务器 GET C → C的父提交B GET B → B的父提交A GET A → ... 3次提交 / 3次往返 ``` ### 使用可选智能获取同一历史 浏览器请求从A开始的历史。服务器在本地遵循父链接,一次性返回A、B、C。 **智能获取** ``` 浏览器→服务器 QUERY .../obj (服务器遵循父链接) 包:A + B + C 3次提交 / 1次往返 ``` 借助服务器大幅减少往返次数! ## 非功能特性 Sorcery是**“Git仓库查看器”**而非**“Git托管平台”**,因为它完全省略了用户账户、SSH/GPG密钥管理,以及Issue+补丁功能。实际上,Sorcery本身是完全只读的!仓库仅通过Git over SSH进行写入,这与Sorcery分离。 你可以将 `sorcery-ssh`(https://git.t4t.associates/char/sorcery/ref/main/tree/extra/sorcery-ssh/)设置为Git用户的SSH `ForceCommand`,它将提供“首次推送时自动创建仓库”的功能,以及编辑仓库描述的能力。这种设置的好处在于,你的公开部署的安全性等同于其SSH守护进程的安全性——在2026年,这让人安心 :) 历史视图确实需要JavaScript,但我并不支持对网络JavaScript的全面排斥(因为除非你住在Ashburn,否则在设备上运行UI代码几乎总是更好的!)。Sorcery使用[我的前端微框架](https://github.com/char/aftercare)+一系列内置Web平台特性+刻意的代码分割,以超轻量级的方式创造丰富的客户端体验。例如,加载项目概览页面仅传输约9KB压缩后的JS,以支持最近提交分页/语言筛选/跳转到提交差异的链接——站点最大的部分最终是语法高亮语法;我想尝试为tree-sitter语法和查询编写纯JavaScript执行器,以便摆脱部分WASM体积(尤其是每个高亮语言WASM包中重复打包的代码)。 ## 总结 我仍然喜爱社区化的Git托管平台!!对于个人项目,我主要需要的是一个能推送代码、可从手机浏览/分享给他人查看的地方:我不需要协作功能,而这样也更轻量。阅读我的代码不应涉及向服务器证明你有权接收超文本的仪式。 [Sorcery中的一个提交差异](https://git.t4t.associates/char/sorcery/#commit/1acdf74737b6ad81a929b6ab717b087033bba32f) 近期我想添加CI注解支持(我对此有明确看法,将在未来博客文章中讨论!!快点订阅RSS按钮),也许未来我会将仓库查看器扩展为一个功能完整、更适合朋友间使用的独立托管平台(待我们真正提炼出最重要的功能并摒弃冗余后……) 无论如何,[来看看吧!](https://git.t4t.associates/char/sorcery)

相似文章

用一个定制的静态网站生成器替代 Jekyll

Lobsters Hottest

nullprogram.com 的作者用 ssg 替代了 Jekyll,这是一个约 8000 行代码的定制静态网站生成器,采用「带模板的 C++20」编写,冷启动生成他的博客仅需 150 毫秒,零依赖且核心不依赖 libc。这一改变也解决了 Jekyll 糟糕的 Ruby 部署流程和 macOS 配置问题。

我们应得的代码锻造平台

Hacker News Top

本文讨论了对GitHub可靠性日益增长的不满,并提出基于AT协议的去中心化Git锻造平台Tangled,作为一个结合了中心化便利性与用户数据所有权的有前途的替代方案。