在本机 git clone 一个本地仓库时,git 其实不是复制对象而是做硬链接。--no-hardlinks、file://、--shared 各自又做了什么?占多少磁盘、快不快、安不安全?这篇用三千多个松散对象的仓库逐种实测。
$ git count-objects -vH | grep -E "^(count|size|in-pack|size-pack):" count: 3003 size: 11.80 MiB in-pack: 4525 size-pack: 388.06 KiB
本地路径 clone 默认把源仓库 .git/objects 下的文件做硬链接(链接数变成 2);--no-hardlinks 真复制;file:// 走与远程相同的协议,把对象重新打包成一个 pack,不用链接;--shared 完全不复制,在 objects/info/alternates 里记下源仓库的对象目录。
方式 耗时 松散 pack 松散链接数 pack 链接数clone 的 objects 额外磁盘 alternates git clone /路径(默认,本地路径) 1977ms 3,003 1 2 2 0.0 MB 无 git clone --no-hardlinks /路径 1103ms 3,003 1 1 1 12.2 MB 无 git clone file:///路径 795ms 0 1 (没有松散对象) 1 0.7 MB 无 git clone --shared /路径 729ms 0 0 (没有松散对象) (没有 pack) 0.0 MB 有 (“objects 额外磁盘”=把源和 clone 的 .git/objects 放在同一个 du 里统计,clone 多出来的部分;硬链接的文件在 du 里只算一次)
硬链接和 shared 不额外占对象的磁盘空间,真复制占 12.2 MB。但硬链接在 APFS 上要创建几千个链接,比真复制还慢;file:// 因为把松散对象打成一个 pack,文件少,最快。
$ ls .git/objects | head -3 | tr "\n" " "; echo "...(松散对象目录)"; ls .git/objects/pack info pack ...(松散对象目录) pack-9d5ddcc0fd80a04f18c67e41f281192786a263fe.idx pack-9d5ddcc0fd80a04f18c67e41f281192786a263fe.pack pack-9d5ddcc0fd80a04f18c67e41f281192786a263fe.rev $ ls .git/objects/info; git config --get remote.origin.url | sed "s#.*/#…/#" packs …/l56src
file:// 的 clone 的 .git/objects 里只有 info 和 pack,没有松散对象目录;路径方式保持源仓库的结构。
$ cat .git/objects/info/alternates | sed "s#.*/ltmp#<ltmp>#"; ls .git/objects | tr "\n" " "; echo "(--shared 的 clone 自己没有对象文件)" <ltmp>/l56a/.git/objects info pack (--shared 的 clone 自己没有对象文件) 在源仓库里把最新的提交永远删掉(reset、清 reflog、gc --prune=now): $ git log --oneline | wc -l | sed "s/^ */源仓库现在的提交数: /" 源仓库现在的提交数: 2 [硬链接的 clone] git fsck → (无输出:完整) [硬链接的 clone] git log → bfb737a 提交 3 | d75eab8 提交 2 | 0db3e3f 提交 1 [--shared 的 clone] git fsck → error: refs/heads/main: invalid sha1 pointer bfb737aa61ba6a7bcbf8862277d4d219aed950c0 error: refs/remotes/origin/HEAD: invalid sha1 pointer bfb737aa61ba6a7bcbf8862277d4d219aed950c0 [--shared 的 clone] git log → fatal: bad object HEAD
硬链接的文件在源仓库删除后仍然存在,clone 是自给自足的。--shared 的 clone 依赖源仓库的对象:源仓库 gc/prune 掉的对象,shared clone 里也就没了,fsck 报 invalid sha1 pointer。所以 shared 只适合源仓库不会清理的场合(如参考仓库、CI 缓存)。