2.2GB 到 371MiB:Git 仓库历史瘦身实录——数据剥离、269 个 commit 重写与三个坑

AI 工作日志 作者头像野生码农 阅读 13 min
返回板块 GEO 源码全文 ✓
2.2GB 到 371MiB:Git 仓库历史瘦身实录——数据剥离、269 个 commit 重写与三个坑

背景与目标

今天做的是 code-cleaner 清理任务的收尾与升级。起因:.git 目录膨胀到 2.2GB(git count-objects 实测:loose 590.83MiB + pack 1.57GiB),而仓库没有远端、磁盘只剩 186GB。目标两层:

  1. 工作区垃圾清干净(根目录杂物、屎山代码处置);
  2. .git 本身不再虚胖——只删当前文件不重写历史,旧体积永远在。

红线:磁盘数据一个字节不能丢(gongwen/、inbox/ 等数据目录全程只动 git,不动文件)。

过程回顾

阶段 1:快照与工作区清理(16:55-17:02)

  • 先打快照 commit(f88186a)再动手,坏了解决不至于伤到无备份状态;
  • 删 1167 个文件:误建的 Chrome profile(1160 文件/114MB,路径拼接错误产物)、stackdump、0 字节文件、空壳 summary、21 处 __pycache__ 等;
  • .gitignore 补根目录兜底 3 条 → 提交 0a5ffbe。

阶段 2:屎山代码审计与处置(至 17:29)

  • 派 general 子代理只读审计 177 个代码文件 → 5 高置信 / 5 疑似 / 167 保留,报告落 reports/20260928_屎山代码审计.md;
  • 军师独立复核三条证据(grep 零引用命中/仅自引用)全部对上才动手;
  • 删 3 个一次性死脚本 + 0 字节误入库 ip.db(真库 data/ip.db 3.4MB 未动),归档 2 份过期文档 → 提交 e3c2978。

阶段 3:.git 元凶调查

count-objects -vH + git-filter-repo 的 analyze 报告(packed 体积=真实占用),按路径排出来:

路径 packed 体积 占比 处置
gongwen/ 公文(docx/pdf/dwg/jpg…) 1.39GB 58% 出历史,文件留磁盘
inbox/ 原始产出(oppo 便签附件即 196MB) 260MB 11% 出历史,文件留磁盘
output/ 产物(含在售产品包) 219MB 9% 保留(产品内容资产)
ipms/packages 发布包 174MB 7% 保留(产线事实源)
notes/ 笔记 126MB 5% 保留
误建 chrome profile 历史残留 ~107MB 4% 出历史(磁盘已删)
tools/wechat_watch/build 81MB 3% 出历史

结论:公文和原始数据占七成——正是"数据不该进 git"的典型结构。

阶段 4:备份与数据剥离(17:48 提交)

  1. 镜像备份:git clone --mirror 出 D:\code\thinking_git_backup_20260928.git(全量 2.2GB,回滚唯一凭据);
  2. 记磁盘基线(清完必须逐字节对上):gongwen 691 文件/1304032487 字节,inbox 970 文件/290685589 字节;
  3. .gitignore 补 4 条(/gongwen/* 例外台账、/inbox/、/.pachong_ws/ 等)+ git rm --cached 移出 1854 项 → 提交 e12aa01。只出索引,文件零删除。

阶段 5:历史重写(18:23 前完成)

  • 先删 2 个指向 tree 的无效 codex ref(重写会卡);
  • git filter-repo --force --invert-paths 清 5 个路径族,269 个 commit 全部重写,18.12 秒;
  • 结果:.git = 371.34 MiB(loose≈15、garbage 0),git fsck 退出码 0,历史 268→269(含重写后的补提交)完整;
  • 副作用处理(见踩坑 1):台账 7 文件从备份原样找回 → 提交 2d91a81。

阶段 6:验证与顺手修存量(至 18:32)

  • 状态干净、数据基线逐字节一致、config_lint P0=0、kb 可检索;
  • pytest 暴露 1 失败 → 快照 worktree 复现定位为存量过期测试(见踩坑 3)→ 修复 → 189 passed → 提交 62c730d。

关键踩坑

坑 1:filter-repo 把台账从工作区清走了

  • 现象:重写后 gongwen 从 691 文件变 684,台账 7 文件(161609 字节)消失。
  • 根因:执行前只预演了"历史里会清什么",没预演"旧 HEAD 仍被跟踪的文件会被重写同步清出工作区"——剥离提交里台账还是跟踪态,于是中招。
  • 解决:从镜像备份 git show 按文件取回,逐字节对基线(1304032487/691,零误差)后重新入库。
  • 改进:重写类操作前先列"哪些文件在旧 HEAD 仍被跟踪",要么先剥离再重写,要么准备找回路径。

坑 2:另一条线正在同一个仓里干活

  • 现象:准备重写时发现 docs/config-ledger.md(17:37)、docs/config-writing-standard.md(17:35)刚被别人修改,而我没写过它们——处置卡那条线在并行作业。
  • 根因:全仓级操作(历史重写改所有 commit hash)与并发写入互相踩会丢东西;此前只顾自己任务卡,没做"在途改动"检查。
  • 解决:当场停下问本人拍板("已收口,立即重写"),拿到确认才执行;我自己的三次提交全部只含本人暂存内容,别人的在途改动一行未卷入。结果:重写前后 git log 无他人提交交错,零冲突。
  • 教训:全仓级操作(历史重写/批量改名/force 操作)执行前必查在途改动并让本人拍板,这条以后是固定动作。

坑 3:pytest 冒出 1 失败——快照复现法证明是存量,不是清理伤的

  • 现象:全量跑出 test_geo_first_para_too_long 失败;而此前验证记录写的是"163 passed"。
  • 定位:①三份相关文件(测试/闸门/规则)自快照以来 diff 为空=我没碰;②189 - 163 = 26 恰等于该测试文件的用例数=此前记录漏收集了整个文件;③在快照 commit 的临时 worktree 里跑同一测试同样失败=问题在我动手之前就存在。三步把"是不是我干的"钉死。
  • 根因:GEO 闸口径 9-26 已收敛(answer_len 60→220),旧回归测试还写死"80 字必拦",而 80 落在现行 [40,220] 合法区间内——配置改了,同批回归用例没跟。
  • 解决:测试改为从 CFG 动态取上限+20,口径再变也不会过期;189 passed。
  • 连带纠错:承认此前"pytest 163 passed"的验证记录口径有误(只记了 passed 数没记 collected 数,不可比)——验证记录从此必须带 collected。

坑 4(小):删除命令的"空输出"差点误判成功

第一次删 codex ref 用了通配 pattern 没匹配上,PowerShell 输出被编码搅成乱码,我把空输出误读为"已删"——复核 for-each-ref 才发现 ref 还在。改成显式 refname 删除并复核到"只剩 master"。教训:删除类命令一律二次复核,以复核结果为准,不以命令跑完为准。

最终方案(产出清单)

  • .git:2.2GiB → 371.34 MiB(约 -83%),fsck 0、garbage 0;
  • 数据处置:1854 项移出索引+历史,磁盘文件逐字节零损失;台账是 gongwen 下唯一保留跟踪目录;
  • 防复发:gitignore 四条数据目录规则,新数据从此进不了 git;
  • 备份:D:\code\thinking_git_backup_20260928.git(裸仓,可整体回滚,旧 hash 全在);
  • 附带:屎山审计报告入库、3 死脚本删除、2 文档归档、1 存量过期测试修复;
  • 代价(知情拍板):全部 commit hash 变化,文档里旧 hash 字面失效(本仓无远端、无兄弟仓依赖)。

做得好的地方

  1. 先备份再重写:镜像备份是敢动手的前提,坑 1 全靠它兜底;
  2. 数据红线用逐字节校验兑现:不靠"应该没动",拿基线数字对;
  3. 验收全部机器可判定:fsck/count-objects/pytest/lint 退出码当证据,不写"感觉没问题";
  4. 并发冲突主动叫停不硬上:发现第一现场(两个文件 mtime)就停下来拍板,没有赌"应该没事";
  5. 存量问题不甩锅不掩饰:快照复现法先证清白,顺手修掉,连同自己的记录口径错误一起纠正。

复盘与改进

  1. 重写类操作必做副作用预演清单(历史清什么/工作区动谁/refs 动谁)——坑 1 的根治;
  2. 全仓级操作前置检查在途改动——坑 2 的制度化;
  3. 验证记录必带 collected 数——"passed 163"这种半截数字以后不出现;
  4. 配置口径变更必须同批改回归用例——坑 3 的根治(这次是口径 9-26 改的,测试没跟)。

个人成长评估

目的先于打分:沉淀"什么指令带来什么结果"的可验证经验。分数是索引,指令实证是本体。

五维评分(10 分制,只对事)

维度 分 证据
指令与提示词 8 两处拍板(question 双答"已收口,立即重写/确认执行")一次到位零纠偏;起手方向纠偏 1 次("要清理的要清理",按 rubric 主干清晰+1-2 次小纠偏=8)
判断与结论 8 并发收口判断与 hash 变更后果均知情拍板;但并发风险是 AI 先发现的,非本人预判
思维方式 8.5 "要清理的要清理"突破"工作区干净=清完"的默认,直指历史体积——一句话把工作范围从删文件升级到重写 269 个 commit
最终结果与产出 9 2.2GiB→371.34MiB 实测、零数据损失(逐字节校验)、189 passed、备份可回滚
AI 协作 8.5 question 两次全给关键信息,执行窗口未被中途打断;未预立"全仓操作前查在途"的规则(靠 AI 会话内发现)

低分项改进(指令维 8):拍板"在途已收口"这类事实断言时,先让 AI 打印在途改动清单核验再执行,把口头确认变成机器核验。

指令实证(2 条)

  1. 原话"要清理的要清理" → 行为改变:清理范围从"删工作区垃圾"升级为".git 历史一并瘦身"立项 → 证据:e12aa01 剥离 + filter-repo 重写 + count-objects 2.2GiB→371.34MiB;
  2. 原话"已收口,立即重写"(question 拍板) → 行为改变:并发暂停点解除,全仓历史重写在零交错窗口执行 → 证据:git log 2d91a81..62c730d 仅含军师两次提交,无他人插入。

AI 自评(同场失分点)

  1. 验证记录口径错:"pytest 163 passed"漏收集 test_zhijian_check.py 26 例,今天实测 189 才暴露——半截数字入了 commit message,已纠错并立规则(必带 collected);
  2. 重写副作用无预演:台账被清出工作区属预判不足,靠备份兜底,应在执行前列出;
  3. codex ref 首删误判成功:空输出被乱码搅浑差点错过,靠复核抓回——删除类命令复核纪律从这次立。

素材点(可复用/可对外)

  • filter-repo 会同步清"旧 HEAD 仍跟踪"的文件——历史瘦身前的预演清单模板;
  • "快照 worktree 复现法":证明一个失败是存量还是新伤,三步(文件 diff 为空→测试计数差→旧 commit 复跑);
  • "数据不入库两层论":gitignore 挡新增 + 历史重写清存量,缺一层体积永远在;
  • 验证记录格式:测试数必须 collected/passed 成对记;
  • 本次数字全部可复现:count-objects、filter-repo analyze、逐字节基线对账。
野生码农头像

关于作者 · 野生码农

把 AI 真放进生产环境的独立开发者。自研本地知识库、数字员工团队、软考备考系统、量化交易四套系统,全程公开复盘。

看他的平台账号 →
相关

继续阅读