仓库与历史

仓库维护

git 从不告诉你一个仓库花了多少代价。不管它的对象数据库处于什么状态,它都照常干活,所以麻烦的第一个信号通常是一次慢如爬行的克隆,或者一台没有磁盘空间了的笔记本——而那时距离「一条命令就能搞定」的时刻,早就过去很久了。

这个面板就是那份缺失的读数:空间去哪儿了,其中多少能回收,以及每个任务在你运行它之前会做什么。

⌘K → 仓库维护。

磁盘占用被拆成已打包、松散和不可达三部分,下面是各项维护任务

读懂这些数字

所有数据都来自 git count-objects -v 和一次真实的可达性遍历——没有任何一项是估算的。

行 是什么 为什么会涨
已打包 packfile 内部的对象,经过压缩和增量化 这是健康的状态
松散 每个对象一个文件,几乎没压缩 每次提交、每次获取都在写这些
不可达 已经没有任何东西指向它们的对象 被丢弃的提交、被修补过的消息、被放弃的变基

松散旁边那个计数——「n 个对象,其中 m 个已经打包过」——才是值得盯着看的那个。那 m 个东西存了两遍:一遍松散地存着,一遍在包里。它们是纯粹的重复,而 git gc 就是把它们塌缩掉的那把刀。

不可达还不等于垃圾。 正是这些对象,让 git reflog 能把你重置掉的提交请回来。git 是有意把它们留两周的。

那些任务

按钮 运行的是 代价
优化 git gc 几秒到一分钟。几乎永远是正确答案
从零重新打包 git gc --aggressive 大仓库上要几分钟。重算每一个增量
重建 commit graph git commit-graph write --reachable 很快。让日志和图的遍历明显更快
检查完整性 git fsck --dangling 大仓库上很慢,但什么都不改
立刻丢弃不可达对象 git gc --prune=now 毁掉 reflog 这张安全网

优化才是你该伸手去按的那个。它把松散对象打包,丢掉那些不可达已超过两周的东西,并让近期的历史仍然可恢复。

从零重新打包被吹过头了。它把现有的每一个增量都扔掉,从零开始重算,要花好几分钟,而通常也就比普通的 gc 多省下百分之几。导入一份巨大的历史之后做一次值得;例行公事地做,不值得。

立刻丢弃不可达对象会先问你,而且确认框里会写明有多少个对象、多少空间。做完之后,你一小时前重置掉的那个提交就再也找不回来了——reflog 条目也许还列在那儿,但它背后的对象已经没了。

检查完整性

git fsck 会核实:被其他对象引用的每一个对象都确实存在,而且内部自洽。

  • 悬垂对象是正常的。 它们就是那些不可达对象,按名字列出来。一次变基之后有几百个悬垂对象的仓库是健康的。
  • 缺失的对象才是损坏——一次被截断的写入、一块坏盘、一次中断的传输。一旦出现,千万不要重新打包:对一个已损坏的数据库做重新打包,可能把一个还救得回来的问题变成永久性的。从你的远程克隆一份好的副本,再用一个 bundle 把你没推送的分支搬过去。

后台维护

这个复选框会把仓库注册给 git maintenance,由它按你操作系统运行的计划(launchd、systemd 或任务计划程序)来打包和预取。

这里没有任何东西是 Gitcito 专属的:同一份计划也为你的终端服务,而且 git maintenance unregister 在任何地方都能撤销它。取消勾选做的正是这件事,同时会把计划本身留在原处,继续服务其他已注册的仓库。

值得知道的限制

  • 不可达计数需要一次完整的可达性遍历,所以在一个特别大的仓库上打开这个面板会顿一下。那是诚实的数字,不是估算。
  • 大小是磁盘实际交出去的量,不是内容的长度。一个 400 字节的松散对象照样占掉一个 4 KB 的块,这就是为什么一千个这样的对象要花掉好几兆——也是为什么把它们打包起来值得做。
  • 工作树或子模块有自己的 .git,所以这里显示的大小只属于这一个仓库。
  • 维护不能让历史变小。 如果一个 400 MB 的 blob 在某个提交里,它就是可达的,gc 会永远留着它——那属于把一个文件从历史中移除,是另一件事,而且破坏性大得多。
  • Gitcito 从不背着你跑 gc。 git 自己的 gc --auto 仍然可能会跑,一如既往;如果某次失败了,它会在 .git/gc.log 里留一条记录,而这个面板会把它显示出来。

另请参阅:把一个文件从历史中移除 · Bundle 与归档 · 恢复

在 GitHub 上改进这份翻译