"幸福的Git仓库都是相似的;不幸的Git仓库各有各的不幸。" —莱纳斯·托尔斯泰
git-sizer
你的Git仓库是否已经快要撑破了?
git-sizer
计算本地Git仓库的各种大小指标,并标记那些可能会给你带来问题或不便的指标。例如:
-
仓库整体是否太大?理想情况下,Git仓库应该小于1 GiB,如果超过5 GiB(没有特殊处理)就会变得难以管理。大型仓库需要很长时间来克隆和重新打包,并且占用大量磁盘空间。建议:
-
仓库是否有太多引用(分支和/或标签)?每次获取时,所有这些都必须传输到客户端,即使你的克隆是最新的。尽量将它们限制在最多几万个。建议:
-
删除不需要的标签和分支。
-
避免将你的"远程跟踪"分支推送到共享仓库。
-
考虑使用"git notes"而不是标签来为提交附加辅助信息(例如,CI构建结果)。
-
也许可以将一些很少需要的标签和分支存储在仓库的单独分支中,普通开发人员不会从中获取。
-
-
仓库是否包含太多对象?对象越多,Git遍历仓库历史所需的时间就越长,例如在进行垃圾回收时。建议:
-
考虑是否存储了大量可以轻松合并成几个大文件的小文件。
-
考虑将你的项目拆分成多个子项目。
-
-
仓库是否包含巨大的blob(文件)?Git最适合处理中小型文件。有几个兆字节范围的文件是可以的,但它们通常应该是例外。建议:
-
考虑使用Git-LFS来存储大文件,特别是那些不便于差异比较和合并的文件(如媒体资产)。
-
另请参阅"仓库整体是否太大?"一节。
-
-
仓库是否包含许多版本的大型文本文件,每个版本与前一个版本略有不同?这些文件可以很好地进行增量存储,所以它们可能不会导致你的仓库急剧增长。但对Git来说,重建完整文件并对它们进行差异比较的成本很高,而这是许多操作内部需要做的。建议:
-
避免在Git中存储日志文件和数据库转储。
-
避免在Git中存储巨大的数据文件(如巨大的XML文件),特别是如果它们经常被修改。考虑使用数据库代替。
-
-
仓库是否包含巨大的树(目录)?每次修改文件时,Git都必须为通向该文件的每个树(即路径中的每个目录)创建一个新副本。巨大的树会使这个过程变得昂贵。此外,遍历包含巨大树的历史记录非常耗时,例如在进行
git blame
时。建议:-
避免创建包含超过几千个条目的目录。
-
如果必须存储非常多的文件,最好将它们分散到多个较小目录的层次结构中。
-
-
仓库是否在单个提交中的不同路径下重复存储相同(或非常相似)的文件?如果是这样,仓库可能有一个合理的总体大小,但当你检出时,它会膨胀成一个巨大的工作副本。(极端情况下,这被称为"git炸弹";见下文。)建议:
- 也许你可以通过使用标签和分支或构建时配置系统来更有效地实现你的目标。
-
仓库是否包含荒谬的长路径名?这可能无法很好地与其他工具配合使用。即使你在写Java,一两百个字符应该足够了。
-
仓库中是否还有其他奇怪和可疑的东西?
-
互相指向的注释标签形成长链?
-
有数十个父提交的章鱼合并?
-
具有巨大日志消息的提交?
-
git-sizer
计算你的仓库的许多与大小相关的统计数据,可以帮助揭示上述所有问题。这些做法本身并不是错误的,但是你越是将Git推离其最佳使用范围,就越难以享受Git传奇般的速度和性能。特别是如果你的Git仓库统计数据似乎与你的项目规模不成比例,你也许可以通过调整Git的使用方式来使你的工作更轻松。
入门
-
确保你已安装Git命令行客户端,版本 >= 2.6。注意:
git-sizer
调用git
命令来检查你的仓库内容,所以运行git-sizer
时必须将git
命令放在你的PATH
中。 -
安装
git-sizer
。可以选择以下方式之一:a. 安装
git-sizer
的发布版本(推荐): 1. 前往发布页面并下载对应你平台的ZIP文件。 2. 解压文件。 3. 将可执行文件(git-sizer
或git-sizer.exe
)移动到你的PATH
中。b. 从源代码构建和安装。参见
docs/BUILDING.md
中的说明。 -
切换到包含你想分析的Git仓库的完整非浅克隆的目录。然后运行
git-sizer [<选项>...]
不需要任何选项。你可以通过输入
git-sizer -h
或继续阅读来了解可用的选项。
专业提示:如果你将git-sizer
添加到你的PATH
中,那么你可以通过输入git-sizer
或git sizer
来运行它。在后一种情况下,它会被Git找到并运行,你可以在两个单词之间添加额外的Git选项,比如git -C /path/to/my/repo sizer
。如果你没有将git-sizer
添加到你的PATH
中,那么你当然需要输入它的完整路径和文件名来运行它;例如,/path/to/bin/git-sizer
。无论哪种情况,git
可执行文件必须在你的PATH
中。
使用方法
默认情况下,git-sizer
以表格格式输出其结果。例如,让我们用它来分析Linux仓库,使用--verbose
选项以输出所有统计信息:
$ git-sizer --verbose
处理 blob:1652370
处理树:3396199
处理提交:722647
匹配提交到树:722647
处理带注释的标签:534
处理引用:539
| 名称 | 值 | 关注程度 |
| ---------------------------- | --------- | ------------------------------ |
| 整体仓库大小 | | |
| * 提交 | | |
| * 数量 | 723 k | * |
| * 总大小 | 525 MiB | ** |
| * 树 | | |
| * 数量 | 3.40 M | ** |
| * 总大小 | 9.00 GiB | **** |
| * 总树条目 | 264 M | ***** |
| * Blob | | |
| * 数量 | 1.65 M | * |
| * 总大小 | 55.8 GiB | ***** |
| * 带注释的标签 | | |
| * 数量 | 534 | |
| * 引用 | | |
| * 数量 | 539 | |
| | | |
| 最大对象 | | |
| * 提交 | | |
| * 最大大小 [1] | 72.7 KiB | * |
| * 最多父提交 [2] | 66 | ****** |
| * 树 | | |
| * 最大条目 [3] | 1.68 k | * |
| * Blob | | |
| * 最大大小 [4] | 13.5 MiB | * |
| | | |
| 历史结构 | | |
| * 最大历史深度 | 136 k | |
| * 最大标签深度 [5] | 1 | |
| | | |
| 最大检出 | | |
| * 目录数量 [6] | 4.38 k | ** |
| * 最大路径深度 [7] | 13 | * |
| * 最大路径长度 [8] | 134 B | * |
| * 文件数量 [9] | 62.3 k | * |
| * 文件总大小 [9] | 747 MiB | |
| * 符号链接数量 [10] | 40 | |
| * 子模块数量 | 0 | |
[1] 91cc53b0c78596a73fa708cceb7313e7168bb146
[2] 2cde51fbd0f310c8a2c5f977e665c0ac3945b46d
[3] 4f86eed5893207aca2c2da86b35b38f2e1ec1fc8 (refs/heads/master:arch/arm/boot/dts)
[4] a02b6794337286bc12c907c33d5d75537c240bd0 (refs/heads/master:drivers/gpu/drm/amd/include/asic_reg/vega10/NBIO/nbio_6_1_sh_mask.h)
[5] 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c (refs/tags/v2.6.11)
[6] 1459754b9d9acc2ffac8525bed6691e15913c6e2 (589b754df3f37ca0a1f96fccde7f91c59266f38a^{tree})
[7] 78a269635e76ed927e17d7883f2d90313570fdbc (dae09011115133666e47c35673c0564b0a702db7^{tree})
[8] ce5f2e31d3bdc1186041fdfd27a5ac96e728f2c5 (refs/heads/master^{tree})
[9] 532bdadc08402b7a72a4b45a2e02e5c710b7d626 (e9ef1fe312b533592e39cddc1327463c30b0ed8d^{tree})
[10] f29a5ea76884ac37e1197bef1941f62fda3f7b99 (f5308d1b83eba20e69df5e0926ba7257c8dd9074^{tree})
输出是一个表格,显示了被测量的项目、其数值和可能引起关注的值的粗略指示。在所有情况下,只包括从引用可达的对象(即,不包括不可达对象,也不包括仅从 reflogs 可达的对象)。
"整体仓库大小"部分包括关于不同对象的仓库范围统计,不包括重复。"总大小"是相应对象未压缩形式的大小总和,以字节为单位。所有对象的总未压缩大小是一个很好的指标,表明像 git gc --aggressive
(以及 git repack [-f|-F]
和 git pack-objects --no-reuse-delta
)、git fsck
和 git log [-G|-S]
这样的命令会有多昂贵。树和提交的未压缩大小是可达性遍历(包括克隆、获取和 git gc
)代价的良好指标。
"最大对象"部分提供了历史中每种类型的最大单个对象的信息。
在"历史结构"部分,"最大历史深度"是历史中最长的提交链,"最大标签深度"报告指向其他带注释标签的带注释标签的最长链。
"最大检出"部分是关于提交检出到工作副本的大小。"最大路径深度"是工作副本中文件的最大路径组件数,"最大路径长度"是以字节计的最长路径。"文件总大小"是单个最大提交中所有文件大小的总和,如果同一文件出现多次,则包括重复计数。
"值"列显示计数,使用单位"k"(千)、"M"(百万)、"G"(十亿)等,以及大小,使用单位"B"(字节)、"KiB"(1024字节)、"MiB"(1024 KiB)等。注意,如果一个值溢出其计数器(这应该只发生在恶意仓库中),相应的值在表格形式中显示为 ∞
,或在 JSON 模式下截断为 2³²-1 或 2⁶⁴-1(取决于计数器的大小)。
"关注级别"列使用星号来标示与"典型"Git仓库相比似乎较高的值。星号越多,表示您的仓库的这一方面可能会造成更多不便。感叹号表示极高的值(相当于超过30个星号)。
脚注列出了表格中引用的"最大"对象的SHA-1值,以及更易读的<提交>:<路径>格式,描述了该对象在仓库历史中的位置。给定一个大对象的名称,您可以在命令行中输入:
git cat-file -p <提交>:<路径>
来查看该对象的内容。(使用--names=none可以省略这些脚注。)
默认情况下,只报告高于最低关注级别的统计信息。使用--verbose(如上所示)可以请求输出所有统计信息。使用--threshold=<值>可以抑制报告低于指定关注级别的统计信息。(<值>被解释为与星号数量对应的数值。)使用--critical只报告关注级别达到临界值的统计信息(相当于--threshold=30)。
如果您想要机器可读格式的输出,包括精确数字,请使用--json选项。您可以使用--json-version=1或--json-version=2来选择旧式或新式JSON输出。
要获取其他选项的列表,请运行:
git-sizer -h
Linux仓库按大多数标准来说都很大。如您所见,它正在挑战Git的一些极限。确实,对Linux仓库进行某些Git操作(如git fsck、git gc)确实需要一些时间。但由于其结构合理,没有任何维度与代码库的大小严重不成比例,因此内核项目成功地使用Git进行管理。
以下是一个著名的"git炸弹"仓库的非详细输出:
$ git-sizer
[...]
| 名称 | 值 | 关注级别 |
| ---------------------------- | ---------- | ------------------------------ |
| 最大检出 | | |
| * 目录数量 [1] | 1.11 G | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
| * 最大路径深度 [1] | 11 | * |
| * 文件数量 [1] | ∞ | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
| * 文件总大小 [2] | 83.8 GiB | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
[1] c1971b07ce6888558e2178a121804774c4201b17 (refs/heads/master^{tree})
[2] d9513477b01825130c48c4bebed114c4b2d50401 (18ed56cbc5012117e24a603e7c072cf65d36d469^{tree})
这个仓库被恶意构造成具有病态的树结构,相同的目录重复出现。因此,尽管整个仓库的大小不到20 kb,但检出时会膨胀成超过10亿个目录,包含超过100亿个文件。(git-sizer为blob计数打印∞,因为真实数字已经溢出了用于该字段的32位计数器。)
贡献
git-sizer正在经常使用并仍在积极开发中。如果您想提供帮助,请参阅CONTRIBUTING.md。