没有网络怎么传一个 git 仓库?git bundle 把若干个 ref 和对应的对象打成一个文件。这篇拆开这个文件看它到底由什么组成,再看增量 bundle 的前置条件、clone 与 fetch 的用法。
$ git bundle create /work/l67full.bundle --all 2>&1 | tail -1; ls -l /work/l67full.bundle | awk '{print $5, "字节"}'
2457 字节
文件开头的文本部分(PACK 之前的字节):
# v2 git bundle
<sha40> refs/heads/dev
<sha40> refs/heads/main
<sha40> refs/tags/v1
<sha40> HEAD
(最后有一个空行:文本头与 pack 的分隔)
PACK 头:版本 2,对象数 30;源仓库 git rev-list --objects --all 数出 30 个对象——pack 里装的就是全部可达对象bundle 文件开头是文本:# v2 git bundle,然后每行 hash ref名,增量 bundle 还有以 - 开头的前置条件行;一个空行之后就是标准的 packfile。用 Python 逐字节读出来,对象数与源仓库的可达对象数一致。
$ git bundle verify /work/l67full.bundle 2>&1 | tail -2 | sed -E 's/[0-9a-f]{40}/<sha40>/g'
The bundle records a complete history.
The bundle uses this hash algorithm: sha1
$ git bundle list-heads /work/l67full.bundle | sed -E 's/[0-9a-f]{40}/<sha40>/g'
<sha40> refs/heads/dev
<sha40> refs/heads/main
<sha40> refs/tags/v1
<sha40> HEAD
$ git log --oneline | wc -l | sed "s/^ */clone 出来的提交数: /"; git branch -a | tr "\n" " "; echo; git tag | tr "\n" " "; echo; git config --get remote.origin.url | sed "s#.*/#…/#"
clone 出来的提交数: 10
* main remotes/origin/HEAD -> origin/main remotes/origin/dev remotes/origin/main
v1
…/l67full.bundlegit bundle verify 检查完整性与前置条件,list-heads 列出其中的 ref;git 可以把 bundle 当只读的远程直接 git clone x.bundle。
$ git bundle create /work/l67inc.bundle main ^92579846b3c90c197a3c05ee36e59ad88cc8fbce 2>&1 | tail -1; ls -l /work/l67inc.bundle | awk '{print $5, "字节"}'
855 字节
增量 bundle 的文本部分:
# v2 git bundle
-<sha40> 提交 10
<sha40> refs/heads/main
pack 里的对象数 9(完整 bundle 是 30),文件大小 855 字节(完整 bundle 2,457 字节)
在缺少前置提交的仓库里验证:
$ git bundle verify /work/l67inc.bundle 2>&1 | head -3 | sed -E 's/[0-9a-f]{40}/<sha40>/g'
error: Repository lacks these prerequisite commits:
error: <sha40>
在已经有这些提交的仓库(上面 clone 出来的 l67c)里验证:
$ git bundle verify /work/l67inc.bundle 2>&1 | head -4 | sed -E "s/[0-9a-f]{40}/<sha40>/g"
/work/l67inc.bundle is okay
The bundle contains this ref:
<sha40> refs/heads/main
The bundle requires this ref:git bundle create inc.bundle main ^<已有的提交> 只打包新对象,在文本头里记录前置条件。接收方缺少前置提交时 verify 报 Repository lacks these prerequisite commits。
$ git fetch /work/l67inc.bundle main:refs/remotes/bundle/main 2>&1 | tail -2 | sed -E "s/[0-9a-f]{7}\.\.[0-9a-f]{7}/<旧>..<新>/; s#From .*#From <bundle>#"; git log --oneline refs/remotes/bundle/main | wc -l | sed "s#^ *#bundle/main 的提交数: #"
From <bundle>
* [new branch] main -> bundle/main
bundle/main 的提交数: 13git fetch x.bundle main:refs/remotes/… 和从普通远程 fetch 一样更新引用,适合用 U 盘、邮件同步仓库。
git pack-objects 直接打包全部对象: 3,099 字节;bundle 里 PACK 部分: 2,894 字节;文本头 230 字节
bundle 没有额外的格式负担:pack 部分的大小和 git pack-objects 同级,再加几百字节的文本头。