git 磁盘写满速查:对象库膨胀与六个根因
更新时间:2026-10-24。FAQ 速查页,面向
git commit或git gc跑到一半抛No space left on device、或者发现.git目录不知不觉吃掉几十 GB 的同学。git 磁盘写满的含义一句话:git 的每一次提交都把文件内容以 blob/tree/commit 对象的形式永久追加进.git/objects对象库——它只增不减,大文件哪怕只进过一次历史、后来删掉了,对象库里的那份也一直在,最终把分区顶满。收敛在六根轴上:对象库随提交次数自然膨胀、大文件(二进制、产物、数据集)误入历史、长期不跑 gc 导致松散对象堆积、gc/repack 自身需要临时空间反而在写满时分区直接失败、工作区日志与构建产物挤占分区,以及该上 LFS 的大文件没上、需要迁移出历史。本篇主线命令是git count-objects -vH——一条命令看清对象库多大、松散对象多少。
本文要回答的问题
fatal: Unable to write new index file与No space left on device是仓库坏了还是盘满了?.git目录几十 GB,先砍哪里最快见效?- 大文件早就删了,为什么仓库还是那么大?
git gc自己跑失败说空间不够,死循环怎么破?- 怎么把历史里的大文件整体搬到 LFS?
一、报错原文与典型现象
git 磁盘写满家族的真实措辞如下(路径以 /proj/demo 占位):
$ git -C /proj/demo commit -m "update"
fatal: Unable to create '/proj/demo/.git/index.lock': No space left on device$ git -C /proj/demo gc
error: unable to write sha1 file: No space left on device
fatal: failed to run repack$ du -sh /proj/demo/.git
23G /proj/demo/.git$ git -C /proj/demo count-objects -vH
count: 183240
size: 8.42 GiB
in-pack: 51203
packs: 1
size-pack: 14.76 GiB
prune-packable: 0
garbage: 0读现象抓三个信号:No space left on device 是操作系统层的真话——分区真的满了,不是 git 逻辑错误;count-objects -vH 里 size(松散对象)特别大而 size-pack 小,说明长期没 gc,跑一次 gc 能省很多;size-pack 本身就巨大,则是历史里沉着大文件,gc 也压不下去,得清历史或上 LFS。先分清「盘满」与「库肥」哪个是主因,顺序反了会白删东西。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| 任何写操作报 No space | 分区真满 | df -h . 看 Use% | 先清工作区垃圾腾空间 |
| count-objects 松散对象巨大 | 长期不 gc | size 字段以 GiB 计 | git gc --aggressive --prune=now |
| size-pack 巨大、删文件没用 | 大文件沉在历史里 | git rev-list --objects --all 找大 blob | filter-repo 清历史或 LFS 迁移 |
| gc/repack 自己报空间不足 | repack 需等量临时空间 | 满盘时必现 | 先清别处分区空间再 gc |
| .git 不大但分区满 | 日志/构建产物挤占 | du -sh 逐目录定位 | 清产物与日志,与 git 无关 |
| 仓库必须囤大文件 | 选型错了,该用 LFS | 大文件为二进制资产 | git lfs migrate import 迁移 |
三、排查决策图
下面这张图是标准排查路线,颜色按角色区分:红是报错入口与命中的根因,蓝是排查动作,绿是通过分支,橙是修复动作,紫是防复发沉淀。
读这张图抓主线顺序:第一步永远是先量再砍——df 看分区、count-objects 看对象库、rev-list 看历史大文件,三个数字摆出来根因已经收敛一半;第二步才是对因处置,盘满先腾空间(否则连 gc 都跑不动)、松散多跑 gc、历史大文件只能改历史。清历史是会改写提交哈希的破坏性动作,放在确认非做不可之后,不能一上来就抡。
四、逐条排查展开
第一条:分区真满——先腾空间再谈 git。 No space left on device 是内核返回的错误,git 只是转述。先 df -h . 确认 Use% 顶到 100%,然后别急着动 .git——工作区的构建产物、日志、临时文件通常比对象库更好删也更该删。腾出的空间要留够 gc 的余量:repack 会在磁盘上先写出完整的新 pack 再删旧的,峰值需要接近一份对象库的额外空间,这就是满盘时 gc 必失败的机械原因。
第二条:松散对象堆积——gc 欠账。 git 日常提交产生的对象先以松散文件形式落在 .git/objects/ 下,积累到一定数量才自动打包。count-objects -vH 的 count 十几万、size 以 GiB 计,就是欠账现场。处置:
git -C /proj/demo gc --aggressive --prune=now--prune=now 把不可达的松散对象一并清掉,多数仓库跑一次能回收大半松散空间。注意它要临时空间,盘满时先执行第一条。
第三条:大文件误入历史——删文件不减肥。 git 的历史是追加式的:git rm bigfile.bin 只是让新提交里没有它,旧提交里的 blob 还在对象库里,克隆照样全量拉下来。找历史里最大的对象:
git -C /proj/demo rev-list --objects --all |
git -C /proj/demo cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
awk '/^blob/ {print $3, $4}' | sort -nr | head确认大文件清单后用 git filter-repo --path bigfile.bin --invert-paths 之类工具整段清出历史,再强制推送并要求全员重新克隆——这是破坏性改写,动手前的评估思路与 git 索引陈旧速查 里「重建前先备份」的兜底哲学一致,先把仓库整体镜像备份一份。
第四条:gc 自己失败——临时空间的死循环。 满盘时 git gc 报 unable to write sha1 file 是典型死循环:gc 能省空间,但 gc 要先有空间。破法:清别处分区内容(系统日志、下载目录、别的构建产物)、或把仓库整体 rsync 到有余量的分区 gc 完再搬回,也可以在别的机器上 gc 好整库替换。千万别在死循环里反复重试 gc,每次失败还可能留下半个临时 pack。
第五条:.git 不大但分区满——凶手在 git 之外。 du -sh /proj/demo/.git 只有几百 MB 而分区满了,那就是日志、core dump、构建产物挤占。逐目录 du -sh /proj/demo/* | sort -rh | head 定位真凶。这类问题挂着 git 的报错名头,本质与 git 资源耗尽速查 里的内存与句柄耗尽同族——资源问题先确认是谁的资源、谁的消耗。
第六条:该用 LFS 的没用——选型修正。 仓库的业务就是囤模型权重、设计资产、视频这类大二进制时,清历史只是止血,正道是迁到 LFS:
git -C /proj/demo lfs migrate import --include="*.bin,*.psd" --everything迁移同样改写历史(哈希全变),需要协调团队窗口期。迁完后历史里只剩指针文件,对象库缩到零头。LFS 服务器的容量与带宽成本另算,但它换来的是克隆与拉取的彻底瘦身。
到这里可以给出一个选型判据:历史里的大文件是「误入库的一次性事故」(比如某次手滑提交了打包产物),清历史一次到位即可;大文件是「业务常态资产」(模型、设计稿、视频每周都在新增),清完还会再涨,必须迁 LFS 才是终点。判错方向的代价是同一个手术做两遍。
五、规范做法与预防
- 大文件从第一天走 LFS:二进制资产入库前先问「它会被 git diff 吗」,不会就该在 LFS 里。
- 产物一律 .gitignore:构建产物、数据集、依赖目录写进
.gitignore并入库级评审,防手滑git add -A。 - 定期 gc 写进维护脚本:活跃仓库每周跑一次普通
git gc,不攒松散对象欠账。 - 监控 .git 体积:CI 里加一步
count-objects -vH报警,库肥是慢性病,早看见早处理。 - 清历史类操作必须留备份:filter-repo 与 lfs migrate 都改写哈希,操作前整库镜像备份,通知全员暂停推送。
这一篇在知识体系里的位置
本篇是 Git 排错速查簇的仓库容量篇,定位「盘满与库肥」:内存与句柄类资源耗尽看 git 资源耗尽速查;索引假修改与状态异常看 git 索引陈旧速查;清理历史后推送被拒看 git 推送被拒速查;本批同簇的还有 git index.lock 已存在速查 与 git 提交时间作者不对速查;对象库的基础概念见 log、show、blame。
一句话总结
git 磁盘写满的本质是「对象库只增不减撞上了分区上限」:先 df 与 count-objects -vH 量清盘和库,盘满先腾空间(gc 要等量临时空间)、松散对象多跑 gc --prune=now、历史里沉着大文件用 filter-repo 清或 lfs migrate 迁 LFS——清历史改写哈希,先备份再动手。大文件走 LFS、产物进 .gitignore、定期 gc 写进维护脚本,这类故障基本绝迹。