bigywater.tech
Git · 41

sparse-checkout

sparse-checkout 让大仓库只检出一部分目录。这篇在 3 万个文件的仓库上看它在 .git 里做了什么:cone 模式的那 4 行模式、index 里的 skip-worktree 标记、稀疏索引怎么把 index 缩小 28 倍,以及越出范围时的行为。

30,002 → 1,002
cone 模式 sparse-checkout 之后工作区的文件数
2.65 MB → 94.5 KB
稀疏索引把 index 缩小的幅度(1,060 个条目)
62 → 18 ms
cone 模式下 git status 的耗时(skip-worktree 的文件不用 lstat)

基线

实测 · 本机执行输出
  工作区文件数 30,002;index 2,651,434 字节;git status 62.2 ms

完整检出时每个文件都要 lstat,index 记录全部条目。

cone 模式

实测 · 本机执行输出
  $ git sparse-checkout init --cone 2>&1; git sparse-checkout set mod05 mod06 2>&1; ls; echo "---"; git sparse-checkout list; echo "---"; cat .git/info/sparse-checkout; echo "---"; git config --get-regexp "sparse|index"
  Makefile
  README.md
  mod05
  mod06
  ---
  mod05
  mod06
  ---
  /*
  !/*/
  /mod05/
  /mod06/
  ---
  core.sparsecheckout true
  core.sparsecheckoutcone true
  工作区文件数 1,002(原来 30,002)

git sparse-checkout init --cone 加 set 目录… 在 .git/info/sparse-checkout 里写成 /*、!/*/ 加每个目录的 /dir/,即“根目录的文件 + 选中的目录”;同时设置 core.sparseCheckout 与 core.sparseCheckoutCone。cone 模式匹配简单、快。

skip-worktree

实测 · 本机执行输出
  $ git ls-files -t | cut -c1 | sort | uniq -c; git ls-files -t | grep -E "^S" | head -2
  1002 H
  29000 S
  S mod00/sub0/f0000.txt
  S mod00/sub0/f0001.txt
  index 2,651,434 字节(仍包含全部 30,002 个条目);git status 18.2 ms

没检出的文件仍在 index 里,带 skip-worktree 标记(git ls-files -t 里显示 S)。git 不为它们 lstat,所以 git status 变快。对象库里仍有全部内容——这不是 partial clone。

稀疏索引

实测 · 本机执行输出
  $ git sparse-checkout init --cone --sparse-index 2>&1; git config --get index.sparse
  true
  index 94,511 字节,1,060 个条目(原来 30,002);扩展块 ['TREE', 'sdir']
  其中目录条目(mode 040000): 58 个;例: mod00/
  $ git ls-files --sparse | wc -l; git ls-files | wc -l; echo "(第一行是稀疏索引里的条目数,第二行是展开后的文件数)"
      1060
     30002
  (第一行是稀疏索引里的条目数,第二行是展开后的文件数)
  hint: The sparse index is expanding to a full index, a slow operation.
  hint: Your working directory likely has contents that are outside of
  hint: your sparse-checkout patterns. Use 'git sparse-checkout list' to
  hint: see your sparse-checkout definition and compare it to your working
  hint: directory contents. Cleaning up any merge conflicts or staged
  hint: changes before running 'git sparse-checkout clean' or 'git
  hint: sparse-checkout reapply' may assist in this cleanup.
  hint: Disable this message with "git config set advice.sparseIndexExpanded false"
  git status 17.4 ms

稀疏索引(--sparse-index)进一步把整个没检出的目录在 index 里合成一个 040000 的“目录条目”(sdir 扩展),index 从 2.65 MB 缩到 94.5 KB。读写 index 更快,内存更小;需要看目录内部时会临时展开。

范围外

实测 · 本机执行输出
  $ git add mod20 2>&1 | head -4
  The following paths and/or pathspecs matched paths that exist
  outside of your sparse-checkout definition, so will not be
  updated in the index:
  mod20
  $ mkdir -p mod20 && echo x > mod20/outside.txt; git status --short; echo "(在稀疏范围外手工创建的文件,仍然被当作未跟踪文件报告;同时 git 提示要展开稀疏索引)"; rm -r mod20
  ?? mod20/outside.txt
  (在稀疏范围外手工创建的文件,仍然被当作未跟踪文件报告;同时 git 提示要展开稀疏索引)
  hint: The sparse index is expanding to a full index, a slow operation.
  hint: Your working directory likely has contents that are outside of
  hint: your sparse-checkout patterns. Use 'git sparse-checkout list' to
  hint: see your sparse-checkout definition and compare it to your working
  hint: directory contents. Cleaning up any merge conflicts or staged
  hint: changes before running 'git sparse-checkout clean' or 'git
  hint: sparse-checkout reapply' may assist in this cleanup.
  hint: Disable this message with "git config set advice.sparseIndexExpanded false"
  $ git sparse-checkout add mod20; ls | tr "\n" " "; echo; git sparse-checkout list
  Makefile README.md mod05 mod06 mod20
  mod05
  mod06
  mod20

对范围外路径 git add 会被拒绝并提示;在范围外手工创建的文件仍被报告为未跟踪,并提示稀疏索引正在展开。要改变范围用 sparse-checkout add。

恢复

实测 · 本机执行输出
  $ git sparse-checkout disable; ls | wc -l
        62
  工作区文件数 30,002;index 2,651,434 字节;git status 61.5 ms

sparse-checkout disable 把所有文件检出回来,index 恢复完整。

分步演示

sparse-checkout
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动。仓库由脚本生成(60 个模块 × 500 个文件);耗时 5 次中位数,含进程启动,每次测量前先让 index 刷新(避开 racy 窗口);index 字节数与条目数来自 .git/index 本身
    • 没有验证:非 cone 模式(gitignore 风格模式)的匹配与性能;partial clone 配合 sparse-checkout(下载更少对象,见后面 partial clone 一篇);sparse-checkout 下切换分支、合并时遇到范围外冲突的行为;更大规模(十万、百万文件)下稀疏索引对 status/add 的加速;git sparse-checkout clean / reapply
    京ICP备2026067502号-1京公网安备11010502063487号