博客仓库 1.5GB 体检报告:1.1GB 全塞在 Git 历史里
- 使用 du、find、git 命令诊断仓库体积,发现.git 占 1.1GB 是工作区 3 倍
- Git 历史中二进制大文件(mp4、png 等)反复提交导致 pack 膨胀
- 清理 Git 历史(git filter-repo)可大幅缩减体积但需谨慎操作
- PNG 转 WebP 压缩、mp4 外链化、配置 Git LFS 是零风险或低风险优化方案
- 核心教训:Git 不是网盘,二进制文件应避免直接提交
大家好,我是若风。
这个博客跑了大半年,文章攒到快 300 篇,图片视频一堆。前两天手贱敲了句 du -sh .,回来一个数字:
1.5G .
一个 Astro 博客,源码撑死几百 KB,凭什么 1.5GB?顺手做了次体检,结果挺有意思——真正的毒瘤不在源码,在 Git 历史里。
这篇文章记录体检全过程和后续的瘦身方案。
体检方法,就三条命令
不用装任何工具,系统自带的 du、find、git 就够。
第一条,看顶层目录谁最胖:
du -sh */ 2>/dev/null | sort -hr
第二条,挖到具体文件,看谁是大头:
find src -type f -exec du -h {} + 2>/dev/null | sort -hr | head -20
第三条,也是最关键的一条——查 Git 历史里最大的对象:
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '$1=="blob"{print $2, $3, $4}' \
| sort -k2 -rn | head -20
前两条看的是「现在的工作区」,第三条看的是「历史里都塞过什么」。正是这第三条,挖出了 1.1GB 的真相。
总览:1.5GB 的钱都花在哪了
先把账算清楚:
| 区域 | 大小 | 占比 | 说明 |
|---|---|---|---|
.git/ | 1.1 GB | ~73% | Git 历史对象,重灾区 |
src/content/ | 365 MB | ~24% | 博客媒体资源 |
src/prompts/ | 2.7 MB | <1% | 封面图 prompt |
| 其余源码 | ~0.5 MB | <1% | pages/layouts/lib 等 |
public/ | 4.4 MB | <1% | vendor 静态资源 |
一行结论:源码工作区只有 370MB,但 .git 有 1.1GB,是工作区的 3 倍。
这不正常。正常仓库 .git 应该比工作区小一个数量级。它反过来大 3 倍,只可能是同一批大文件被反复提交、又在历史里堆积。
第一重灾区:.git 1.1GB
深挖 .git 内部,1.1GB 几乎全在 pack 里:
du -sh .git/objects .git/objects/pack
# 1.1G .git/objects
# 1.1G .git/objects/pack
再用上面第三条命令看历史最大的对象,Top 10 长这样:
18.2 MB src/content/blog/2026-04/imgs/gpt-5-5-official-intro-codex-artemis-demo.mp4
16.9 MB src/content/blog/2026-06/imgs/recursive-self-improvement.mp4
14.8 MB src/content/blog/2026-06/imgs/minimax-m3-05.gif
9.4 MB src/content/blog/2026-06/imgs/video-wxv_4541634154270326797.mp4
8.7 MB src/content/blog/2026-06/imgs/wwdc-2026-apple-ai-puzzle-01.png
8.4 MB src/content/blog/2026-06/imgs/glm-52coding-国产-21.webp
7.8 MB src/content/blog/2026-06/imgs/wwdc-2026-apple-ai-visual-intelligence.png
7.6 MB src/content/blog/2026-06/imgs/claude-fable-5.mp4
6.4 MB src/content/blog/2026-05/imgs/codex-mobile-coming-14.mp4
6.4 MB src/content/blog/2026-03/imgs/figma-mcp-demo.mp4
全是 mp4、png、webp、gif。这就是问题所在:
这些大体积二进制媒体文件被直接提交进了 Git。即使后来某个文件被删除或替换,它的旧版本仍然作为 blob 对象永久残留在
.git历史里,pack 还会继续压缩打包它们。
Git 是为文本设计的版本控制系统,对二进制大文件几乎不产生 diff 增量——每次改动都是一份全新副本。一篇 WWDC 文章的截图改一版,仓库就多 8MB,而且再也删不掉。
更扎心的是,这博客部署在 Cloudflare Pages,构建时直接拉这个 GitHub 仓库。也就是说每次 push、每次 clone、每次 CI 构建,都要拖着这 1.1GB 历史跑一圈。
第二重灾区:src/content/blog 365MB
工作区这边也不轻松。src/content/blog/ 一个目录就吃了 365MB。
按月份分布,越新越大,增长趋势很明显:
2026-06 158M ← 最新月,最大
2026-05 92M
2026-03 46M
2026-04 32M
2026-02 25M
2026-01 10M
半年时间,单月媒体体积从 10MB 涨到 158MB,15 倍。照这个曲线再写半年,仓库得奔着 3GB 去。
按文件类型拆,更清楚钱花在哪:
| 类型 | 大小 | 文件数 |
|---|---|---|
| webp | 179.8 MB | 760 |
| mp4 | 83.3 MB | 15 |
| png | 70.9 MB | 140 |
| jpg | 22.1 MB | 165 |
| gif | 2.9 MB | 3 |
| md | 4.1 MB | 296 |
| svg | 1.4 MB | 176 |
两个观察:
1. mp4 是体积刺客。 只有 15 个文件,却吃了 83MB,平均单个 5.5MB。文本内容才 4MB,视频是它的 20 倍。
2. PNG 不该这么多。 项目早统一用 webp 了,但还有 140 个 png 占 70MB,多数是 WWDC、产品截图这种。PNG 没压缩,同样画面转 webp 能压到 1/5。
数了一下,大于 1MB 的文件有 55 个,大于 2MB 的有 21 个。这些就是优化要动的对象。
四条优化方案,按收益排序
体检完,方案其实很清楚,按性价比排:
方案 1:清理 Git 历史(收益最大,也最危险)
用 git filter-repo 把历史里的 .mp4、.png 大文件从所有 commit 中抹掉,可以让 .git 从 1.1GB 直接掉到几十 MB。
# 安装
pip install git-filter-repo
# 备份!这一步不可逆
cp -r tech-blog tech-blog-backup
# 移除历史中所有大于 5MB 的 mp4/png
git filter-repo --path-glob '*.mp4' --invert-paths
git filter-repo --path-glob '*.png' --invert-paths
# 回收空间
git reflog expire --expire=now --all
git gc --prune=now --aggressive
但这里有个致命矛盾:这博客靠 Cloudflare Pages 从 GitHub 仓库构建,文章里的图片视频必须留在仓库里供构建使用。如果把 mp4 从历史里全抹了,老文章的视频就 404 了。
所以正确的姿势不是无脑 --invert-paths,而是分两类处理:
- 当前仍在用的媒体:保留在工作区,但改用 Git LFS 管理(见方案 4),避免再污染历史
- 已经删除、纯历史残留的文件:放心抹掉
git filter-repo 比 bfg 更推荐——bfg 需要 Java,filter-repo 是纯 Python,而且能精确按路径 glob 过滤。
⚠️ 历史重写会改变所有 commit hash,需要
git push --force强推,并通知所有协作者重新 clone。单人项目还好,多人项目这是核武器。
方案 2:PNG 转 WebP 压缩
70MB 的 png(140 个)是性价比最高的肉。项目里已经有 rf-image-compress Skill,直接跑:
# 默认只处理 git 新增/修改的图片,这里要全量压一次历史 png
PNG 截图转 webp,质量 80 肉眼基本无损,体积能压到 1/4 到 1/5。140 个 png 估计能从 70MB 压到 15MB 以内。
这个方案零风险,唯一要注意的是文章里引用的路径要从 .png 改成 .webp。
方案 3:mp4 外链化
83MB 的 15 个视频,本质就不该进 Git 仓库。视频是流媒体,应该放对象存储或视频平台,文章里用外链:
<video controls src="https://cdn.example.com/videos/claude-fable-5.mp4"></video>
候选方案:Cloudflare R2(和 Pages 同生态,流量免费)、阿里云 OSS、或者直接传到 B 站/YouTube 用嵌入。
把 15 个 mp4 挪出去,工作区立刻瘦 83MB,而且新增视频不再污染仓库。
方案 4:配 Git LFS,治本
前三个方案是还债,这个是立规矩。配 .gitattributes 把大文件交给 LFS 管理:
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
LFS 把大文件存到独立的 LFS 存储,仓库里只留一个几十字节的指针。clone 时按需拉取,历史不再膨胀。
代价是 GitHub 免费账户 LFS 只有 1GB 配额,超了要付费。对这个博客来说,配合方案 3 把视频外链出去,LFS 配额基本够用。
一张表收尾
| 方案 | 预计收益 | 风险 | 优先级 |
|---|---|---|---|
| 清理 Git 历史 | .git 1.1GB → 几十 MB | 高,不可逆,需强推 | ⭐⭐⭐⭐⭐ |
| PNG 转 WebP | png 70MB → 15MB | 低,改路径 | ⭐⭐⭐⭐ |
| mp4 外链化 | 工作区瘦 83MB | 低,改外链 | ⭐⭐⭐⭐ |
| Git LFS | 治本,防复发 | 中,配额成本 | ⭐⭐⭐ |
写在最后
这次体检最大的教训:Git 不是网盘。
写博客的时候图省事,截图、录屏直接往 imgs/ 一丢就 commit,半年下来仓库就肿成 1.5GB。文本版本控制系统的设计初衷,从来不是装二进制大文件。
体检是做完了,方案也清楚了。但历史重写那一步我还在犹豫——毕竟要强推 main,得挑个没人构建的时间窗口。PNG 压缩和视频外链这两条零风险的,可以先动起来。
如果你也在维护一个跑了几年的内容仓库,建议现在就敲一句 du -sh .git,看看你的历史里藏着多少没看见的债。
后续我把这几条方案实际执行完,再写一篇「瘦身实录」记录真实收益和踩的坑。
评论互动