--depth 1 的 clone 又快又小,代价是什么?.git/shallow 里记着什么?什么命令会因为历史不完整而出问题?这篇在 2000 个提交的仓库上实测不同深度的对象数与体积,再看加深、恢复,以及按时间或引用截断。
git clone --depth 1 /路径 → warning: --depth is ignored in local clones; use file:// instead.
$ git log --oneline; cat .git/shallow | sed -E "s/[0-9a-f]{40}/<sha40>/"; git rev-parse --is-shallow-repository
cb08777 提交 2000
<sha40>
true
$ git cat-file -p HEAD | grep -E "^(parent|tree)" | sed -E "s/[0-9a-f]{40}/<sha40>/"; echo "---"; P=$(git cat-file -p HEAD | grep ^parent | cut -d" " -f2); git cat-file -t $P 2>&1 | head -1; git log --oneline --parents | sed -E "s/[0-9a-f]{40}/<sha40>/g"
tree <sha40>
parent <sha40>
---
fatal: git cat-file: could not get object info
cb08777 提交 2000用本地路径做 git clone --depth 1 /路径 只会得到警告 --depth is ignored in local clones; use file:// instead,要用 file:// 才真的浅克隆。浅克隆的 .git/shallow 列出边界提交的 hash:边界提交的对象里仍写着 parent,但那个 parent 在本地不存在,git 读到 shallow 文件就把这里当作历史的起点。
克隆方式 可见提交数 对象数 pack 大小 耗时 完整 2,000 6,000 4197 KB 137ms --depth 50 50 199 199 KB 52ms --depth 10 10 79 115 KB 49ms --depth 1 1 52 96 KB 49ms
浅克隆拿到的是最近 N 个提交以及它们完整的 tree(所有 blob),不是只取差异。深度 1 时 96 KB,完整是 4197 KB,约 2%;深度越大越接近完整。
$ git fetch -q --deepen 9; git rev-list --count HEAD; wc -l < .git/shallow | sed "s/^ */shallow 文件行数: /" 10 shallow 文件行数: 1 $ git fetch -q --unshallow; git rev-list --count HEAD; git rev-parse --is-shallow-repository; ls .git/shallow 2>&1 | head -1 2000 false ls: .git/shallow: No such file or directory
git fetch --deepen N 再多取 N 层;--unshallow 取回全部并删除 shallow 文件。
$ git log --oneline | wc -l | sed "s/^ */git log 能看到的提交数: /"; git blame -s f07.txt | head -2 | cut -c1-60; git describe --always 2>&1 | head -1 | sed -E "s/[0-9a-f]{7,}/<sha>/"
git log 能看到的提交数: 5
^b4f5741 1) 版本 1957
^b4f5741 2) qgoelfczqaeeezuicvfdjtygdrwbayzfyktewmppipavkex
<sha>
$ git cat-file -t $(git -C /work/l59src rev-parse HEAD~100) 2>&1 | head -1
fatal: git cat-file: could not get object info
$ git log --oneline HEAD~10 2>&1 | head -2
fatal: ambiguous argument 'HEAD~10': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:git log 和 git blame 只能到边界;describe 找不到标签就只给 hash;边界之前的提交根本不存在,按 hash 访问会失败。
--shallow-since=<第 1990 个提交的时间>:可见提交 11 个(第 1990~2000 共 11 个) --shallow-exclude=mark1950(标签在倒数第 51 个):可见提交 50 个(标签之后的 50 个提交,标签自身被排除)
--shallow-since 与 --shallow-exclude 按“到哪里为止”定义深度,而不是层数。