bigywater.tech
Git · 63

浅克隆 --depth

--depth 1 的 clone 又快又小,代价是什么?.git/shallow 里记着什么?什么命令会因为历史不完整而出问题?这篇在 2000 个提交的仓库上实测不同深度的对象数与体积,再看加深、恢复,以及按时间或引用截断。

52 vs 6,000
--depth 1 与完整克隆的对象数
96 vs 4197 KB
--depth 1 与完整克隆的 pack 大小
^ 边界
git blame 在浅克隆里追到边界就停,行首显示 ^

本地路径会忽略,shallow 文件

实测 · 本机执行输出
  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 按“到哪里为止”定义深度,而不是层数。

分步演示

浅克隆
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动。仓库由 git fast-import 生成(2000 个提交,每次改 50 个文件之一);“对象数”是 count-objects 的 count + in-pack;pack 大小是 .git/objects/pack/*.pack 文件大小之和;克隆都走 file://;耗时太小没有引用
    • 没有验证:浅克隆之后 push 的限制;浅克隆与子模块;--depth 对多个分支的影响(--no-single-branch);shallow.lock 与并发;浅克隆上 git gc 的行为;partial clone(--filter)是另一种减小克隆的方法,见下一篇
    京ICP备2026067502号-1京公网安备11010502063487号