bigywater.tech
Git · 78

大仓库:对象数量增长时 status 和 log 的耗时

仓库变大之后,哪些命令会变慢、慢多少、跟什么有关?是提交数、文件数还是对象数?这篇分别把提交数、文件数各放大几十倍,测 status、log、rev-list、add、clone 的耗时,再对比松散与打包存储,以及 commit-graph 与 Bloom filter 的作用。

12 → 12 ms
提交数 1,000 → 40,000 时 git status 的耗时:status 看的是工作区,与历史长度无关
16 → 88 ms
工作区文件数 1,000 → 50,000 时 git status 的耗时
246 → 76 ms
40,000 提交上 git log -- 目录:没有 commit-graph 与加上 --changed-paths 的对比

随提交数:只看最近的与走完整段历史

实测 · 本机执行输出
                                        1,000 提交   10,000 提交   40,000 提交
  git status                               12 ms       12 ms       12 ms
  git log -1                               11 ms       11 ms       12 ms
  git rev-list --count HEAD                14 ms       39 ms      124 ms
  git log --oneline(全部)                    19 ms       65 ms      213 ms
  git log --stat -n 100                    15 ms       15 ms       15 ms
  1,000 提交:对象 count: 0 in-pack: 3000 .git 目录 396 KB
  10,000 提交:对象 count: 0 in-pack: 30000 .git 目录 3060 KB
  40,000 提交:对象 count: 0 in-pack: 120000 .git 目录 11992 KB

提交数增长 40 倍,git status、git log -1、git log --stat -n 100 的耗时没有变化;rev-list --count 与完整的 git log --oneline 要遍历所有提交,耗时随提交数增长。

随文件数

实测 · 本机执行输出
                                        1,000 文件   10,000 文件   50,000 文件
  git status                               16 ms       30 ms       88 ms
  git status -uno                          16 ms       24 ms       61 ms
  git add -A(没有改动)                         15 ms       29 ms       84 ms
  git ls-files | wc -l                     13 ms       15 ms       20 ms
  git diff --stat HEAD                     15 ms       24 ms       63 ms
  git commit --allow-empty                 16 ms       28 ms       74 ms
  1,000 文件:.git/index 82,970 字节(每个文件约 83 字节)
  10,000 文件:.git/index 803,071 字节(每个文件约 80 字节)
  50,000 文件:.git/index 4,003,071 字节(每个文件约 80 字节)

文件数增长 50 倍,status、add -A、commit 的耗时增长 4 到 6 倍(都要检查每个已跟踪文件),ls-files 只读 index,增长很小。index 文件约每个文件 80 字节。

松散对象与打包对象

实测 · 本机执行输出
                                           1,000      10,000      40,000   (提交数)
  对象总数                                     4,000      40,000     160,000
  打包:.git 目录 KB                              576       5,260      27,128
  松散:.git 目录 KB                           16,092     166,904        (未测)
  松散:对象文件数                                 4,000      40,000        (未测)
  rev-list --objects --all(打包)             22 ms      131 ms      948 ms
  rev-list --objects --all(松散)            118 ms     1368 ms        (未测)
  cat-file --batch-all-objects(打包)         16 ms       38 ms      116 ms
  cat-file --batch-all-objects(松散)        128 ms     1325 ms        (未测)
  git status(打包)                           17 ms       29 ms       74 ms
  (这一组里工作区文件数也随提交增长到 40,000 个,所以 status 的增长来自工作区而不是历史)

同一份历史,10,000 个提交的松散存储遍历全部对象比打包慢约 10 倍,磁盘占用约 32 倍(166,904 KB 对 5,260 KB)。40,000 个提交的松散存储没有测。

带路径的 log

实测 · 本机执行输出
                                           1,000      10,000      40,000   (提交数)
  没有 commit-graph                          18 ms       67 ms      246 ms
  有 commit-graph(无 Bloom)                  16 ms       37 ms      122 ms
  commit-graph + --changed-paths           16 ms       27 ms       76 ms
  结果行数(三种相同)                                  20         200         800
  commit-graph 文件大小                        68 KB      656 KB    2,620 KB

git log -- 目录 要比较每个提交的树。commit-graph 把 246 ms 降到 122 ms,再加 --changed-paths 的 Bloom filter 降到 76 ms(40,000 提交)。

克隆

实测 · 本机执行输出
                                           1,000      10,000      40,000   (提交数)
  完整克隆                                    143 ms     1215 ms     6284 ms
    .git 大小                               600 KB    5,412 KB   28,224 KB
  --depth 1                               130 ms      858 ms     3296 ms
    .git 大小                               276 KB    1,652 KB    6,360 KB
  --filter=blob:none                      181 ms     1314 ms     7482 ms
    .git 大小                               692 KB    6,228 KB   31,272 KB

file:// 克隆的耗时随提交数增长;--depth 1 在这里也要 3.3 s,因为最新提交的树里有 40,000 个文件;--filter=blob:none 在这个带检出的场景里最慢。耗时里包含检出,没有拆开。

倍数与固定开销

实测 · 本机执行输出
  git status                             12 ms →      12 ms       1.0 倍   (规模 ×40)
  git log -1                             11 ms →      12 ms       1.1 倍   (规模 ×40)
  git rev-list --count HEAD              14 ms →     124 ms       8.8 倍   (规模 ×40)
  git log --oneline(全部)                  19 ms →     213 ms      11.0 倍   (规模 ×40)
  git log --stat -n 100                  15 ms →      15 ms       1.0 倍   (规模 ×40)
  git status                             16 ms →      88 ms       5.6 倍   (规模 ×50)
  git status -uno                        16 ms →      61 ms       3.7 倍   (规模 ×50)
  git add -A(没有改动)                       15 ms →      84 ms       5.4 倍   (规模 ×50)
  git ls-files | wc -l                   13 ms →      20 ms       1.5 倍   (规模 ×50)
  git diff --stat HEAD                   15 ms →      63 ms       4.3 倍   (规模 ×50)
  git commit --allow-empty               16 ms →      74 ms       4.6 倍   (规模 ×50)
  rev-list --objects --all(打包)           22 ms →     948 ms      43.5 倍   (规模 ×40)
  cat-file --batch-all-objects(打包)       16 ms →     116 ms       7.3 倍   (规模 ×40)
  log -- 目录(无 graph)                     18 ms →     246 ms      13.8 倍   (规模 ×40)
  log -- 目录(Bloom)                       16 ms →      76 ms       4.7 倍   (规模 ×40)
  完整克隆                                  143 ms →    6284 ms      44.0 倍   (规模 ×40)
  --depth 1                             130 ms →    3296 ms      25.4 倍   (规模 ×40)
  --filter=blob:none                    181 ms →    7482 ms      41.3 倍   (规模 ×40)

每条命令都有约 11 ms 的启动开销,所以小规模时倍数偏低。表里的“随历史”“随文件”“不变”三类划分来自上面的测量。

分步演示

大仓库的耗时
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动。历史由 git fast-import 一次造出;每个数字是 5 次(最大规模 3 次)的中位数;每个命令都经过 shell 调用,包含进程启动;克隆用 file:// 加 --no-local;未启用 fsmonitor、untracked cache
    • 没有验证:真正的大仓库(百万级提交、数十万以上文件);网络克隆的耗时;松散对象慢的具体原因(只测了耗时与文件数);40,000 提交的松散存储;克隆耗时里检出与传输各占多少;稀疏检出、部分克隆下的 status;Linux 上的文件系统差异;冷缓存(重启后第一次)的耗时;这些数字在其它机器上的绝对值
    京ICP备2026067502号-1京公网安备11010502063487号