博客仓库 1.5GB 体检报告:1.1GB 全塞在 Git 历史里

发布于 2026年06月29日 13:12 #DevOps

博客仓库 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 历史里

这篇文章记录体检全过程和后续的瘦身方案。

体检方法,就三条命令

不用装任何工具,系统自带的 dufindgit 就够。

第一条,看顶层目录谁最胖:

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 去。

按文件类型拆,更清楚钱花在哪:

类型大小文件数
webp179.8 MB760
mp483.3 MB15
png70.9 MB140
jpg22.1 MB165
gif2.9 MB3
md4.1 MB296
svg1.4 MB176

两个观察:

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-repobfg 更推荐——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 转 WebPpng 70MB → 15MB低,改路径⭐⭐⭐⭐
mp4 外链化工作区瘦 83MB低,改外链⭐⭐⭐⭐
Git LFS治本,防复发中,配额成本⭐⭐⭐

写在最后

这次体检最大的教训:Git 不是网盘

写博客的时候图省事,截图、录屏直接往 imgs/ 一丢就 commit,半年下来仓库就肿成 1.5GB。文本版本控制系统的设计初衷,从来不是装二进制大文件。

体检是做完了,方案也清楚了。但历史重写那一步我还在犹豫——毕竟要强推 main,得挑个没人构建的时间窗口。PNG 压缩和视频外链这两条零风险的,可以先动起来。

如果你也在维护一个跑了几年的内容仓库,建议现在就敲一句 du -sh .git,看看你的历史里藏着多少没看见的债。

后续我把这几条方案实际执行完,再写一篇「瘦身实录」记录真实收益和踩的坑。

评论互动

© 2026 王若风的技术博客 · Powered by Astro