sparse-checkout 让大仓库只检出一部分目录。这篇在 3 万个文件的仓库上看它在 .git 里做了什么:cone 模式的那 4 行模式、index 里的 skip-worktree 标记、稀疏索引怎么把 index 缩小 28 倍,以及越出范围时的行为。
工作区文件数 30,002;index 2,651,434 字节;git status 62.2 ms
完整检出时每个文件都要 lstat,index 记录全部条目。
$ 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 模式匹配简单、快。
$ 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 mssparse-checkout disable 把所有文件检出回来,index 恢复完整。