Git 2.36.1未跟踪缓存失效及git status性能优化问题
Mac端Git操作性能异常排查与解决
问题现象
- 运行环境:Mac设备,初始使用Git 2.35.1版本
- 初始性能表现:克隆仓库后枚举未跟踪文件耗时7秒,执行
time git status耗时约2秒;切换其他分支耗时约15秒,切回主分支后git status耗时达15秒,远超出正常预期。 - 版本升级后异常:升级到Git 2.36.1版本后,原有未跟踪缓存优化方案失效,所有分支下Git相关操作耗时均达到20秒左右。
Git 2.35.1版本临时优化方案
该版本下可通过开启未跟踪缓存减少文件系统遍历开销,配置生效后git status耗时可优化至约4秒:
git config core.untrackedCache true export GIT_FORCE_UNTRACKED_CACHE=1
该方案为社区通用的未跟踪缓存优化手段,核心逻辑是强制缓存未跟踪文件列表,避免每次执行状态查询时全量遍历工作区文件。
Git 2.36.1版本优化失效原因
对比两个版本dir.c源码中未跟踪缓存的判断逻辑,可见2.36.1版本调整了环境变量的默认取值规则:
Git 2.35.1版本dir.c相关逻辑
if (dir->untracked) { static int force_untracked_cache = -1; if (force_untracked_cache < 0) force_untracked_cache = git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0); if (force_untracked_cache && dir->untracked == istate->untracked && (dir->untracked->dir_opened || dir->untracked->gitignore_invalidated || dir->untracked->dir_invalidated)) istate->cache_changed |= UNTRACKED_CHANGED; if (dir->untracked != istate->untracked) { FREE_AND_NULL(dir->untracked); } }
Git 2.36.1版本dir.c相关逻辑
if (dir->untracked) { static int force_untracked_cache = -1; if (force_untracked_cache < 0) force_untracked_cache = git_env_bool("GIT_FORCE_UNTRACKED_CACHE", -1); if (force_untracked_cache < 0) force_untracked_cache = (istate->repo->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE); if (force_untracked_cache && dir->untracked == istate->untracked && (dir->untracked->dir_opened || dir->untracked->gitignore_invalidated || dir->untracked->dir_invalidated)) istate->cache_changed |= UNTRACKED_CHANGED; if (dir->untracked != istate->untracked) { FREE_AND_NULL(dir->untracked); } }
逻辑差异点:2.36.1版本中,若未显式设置GIT_FORCE_UNTRACKED_CACHE环境变量,不会再默认取0值,而是回退读取core.untracked_cache配置判断是否允许写入缓存。旧方案中仅设置配置项+临时环境变量的组合,在切分支等场景下无法稳定触发缓存更新,最终导致优化失效。
性能瓶颈定位
通过GIT_TRACE_PERFORMANCE=1 git status采集全链路性能数据:
首次执行性能日志
12:44:54.433726 read-cache.c:2437 performance: 0.092473000 s: read cache .git/index 12:44:54.915510 read-cache.c:2480 performance: 0.481510000 s: read cache .git/sharedindex.f6119c27ffbee28b22e1baa47e66f355491292e 12:45:05.369546 preload-index.c:154 performance: 10.374954000 s: preload index Refresh index: 100% (1164397/1164397), done. 12:45:05.421952 read-cache.c:1721 performance: 10.427363000 s: refresh index 12:45:05.464869 diff-lib.c:266 performance: 0.040042000 s: diff-files 12:45:05.478549 unpack-trees.c:1884 performance: 0.000028000 s: traverse_trees 12:45:05.493406 unpack-trees.c:424 performance: 0.000008000 s:check_updates 12:45:05.493444 unpack-trees.c:1974 performance: 0.028052000 s: unpack_trees 12:45:05.493454 diff-lib.c:629 performance: 0.028099000 s: diff-index On branch default Your branch is up to date with 'origin/default'.
首次执行时共享索引(.git/sharedindex.*)读取仅耗时0.48s,核心开销集中在preload index阶段,耗时10.37s。
切分支后返回default分支性能日志
GIT_TRACE_PERFORMANCE=1 git status 12:22:24.343325 read-cache.c:2437 performance: 0.112630000 s: read cache .git/index 12:22:42.618493 read-cache.c:2480 performance: 18.274836000 s:read cache .git/sharedindex.5ad8766e997830f32884b42ca5b17c2be6a19f1 12:22:53.559907 preload-index.c:154 performance: 10.840555000 s: preload index Refresh index: 100% (1164397/1164397), done. 12:22:53.646110 read-cache.c:1721 performance: 10.926760000 s: refresh index 12:22:53.685650 diff-lib.c:266 performance: 0.038002000 s: diff-files 12:22:53.713422 unpack-trees.c:1884 performance: 0.000042000 s: traverse_trees 12:22:53.726052 unpack-trees.c:424 performance: 0.000008000 s: check_updates 12:22:53.726085 unpack-trees.c:1974 performance: 0.028672000 s:unpack_trees 12:22:53.726094 diff-lib.c:629 performance: 0.039895000 s: diff-index 12:23:03.568051 read-cache.c:3121 performance: 0.161937000 s: write index, changed mask = c On branch default Your branch is up to date with 'origin/default'. You are in a sparse checkout with tracked files present. Changes not staged for commit: Modified: Modified: ….
切分支后共享索引读取耗时飙升至18.27s,成为第一大性能瓶颈。经确认,.git/sharedindex.*是开启core.splitindex=true配置后生成的共享索引文件,切分支时会生成新的全量共享索引,Mac文件系统下大体积共享索引的读取、校验开销极高,是导致性能异常的核心诱因。
可行临时解决方案
- 关闭splitindex特性,消除共享索引开销:
git config core.splitindex false rm -f .git/sharedindex.*
执行后索引回退为单文件模式,切分支时不会生成新的共享索引文件,可直接消除10s+的共享索引读取耗时。
- 适配2.36+版本的未跟踪缓存规则:显式全局设置
GIT_FORCE_UNTRACKED_CACHE=1环境变量(不依赖配置项的回退逻辑),同时强制刷新未跟踪缓存:
# 若使用zsh/bash,可将export命令写入shell配置文件永久生效 export GIT_FORCE_UNTRACKED_CACHE=1 git update-index --untracked-cache git config core.untrackedCache true
- 针对百万级文件的超大型仓库,额外开启文件系统监控,减少索引刷新阶段的文件元数据遍历开销:
git config core.fsmonitor true git config core.untrackedCache true
内容的提问来源于stack exchange,提问作者checked
相关产品推荐
相关产品推荐

