git fetch 怎么知道只需要下载哪些对象?客户端的 have 要发多少个?本地分支多的时候协商会不会很慢?这篇在 1000 个提交的历史上实测 consecutive、skipping、noop 三种协商算法,以及 thin pack 如何让小改动几乎零传输。
客户端发出的 have: 16 个;fetch 命令 1 次;want 1 个 $ git fetch -q 2>&1; git rev-list --count origin/main..origin/main; git log --oneline origin/main | wc -l | sed "s/^ */origin\/main 现在的提交数: /" 0 origin/main 现在的提交数: 1050
客户端用 want 说想要什么,用 have 说已经有什么;服务端回 ACK … common,找到共同点就只打包缺的对象。纯落后的客户端第一批发 16 个 have,一轮就结束。
fetch.negotiationAlgorithm=(未设置) 客户端发出 have 1008 个,fetch 命令 6 轮;对象库增加 15 KB fetch.negotiationAlgorithm=consecutive 客户端发出 have 1008 个,fetch 命令 6 轮;对象库增加 15 KB fetch.negotiationAlgorithm=skipping 客户端发出 have 16 个,fetch 命令 1 轮;对象库增加 15 KB fetch.negotiationAlgorithm=noop 客户端发出 have 0 个,fetch 命令 1 轮;对象库增加 350 KB (consecutive:沿着本地历史一个个往前发 have;skipping:跳着发,间隔越来越大,更快找到共同点;noop:一个 have 都不发——只适合 partial clone 之类的场合,会下载更多)
fetch.negotiationAlgorithm:默认(consecutive)沿着本地历史一个一个往前发 have,本地独有的提交越多越慢(500 个独有提交时发出 1008 个 have、6 轮);skipping 跳跃式发送,16 个 have、1 轮就找到共同点;noop 完全不发 have,适合 partial clone 等不需要协商的场景,代价是服务端不知道你有什么,会多发很多(本例多下载 350 KB)。
git fetch --negotiation-tip=origin/main:客户端发出 have 16 个,fetch 命令 1 轮
限制协商只从指定的提交出发,本地的其它分支(哪怕有几百个独有提交)不参与,协商变成 16 个 have、1 轮。
服务端打包的摘要: remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0) 模拟服务端要发的 pack(只含 v2 这一个提交的新对象):--thin(可以引用客户端已有的 v1 blob 做 delta)308 字节;不用 thin(对象必须自包含)136,125 字节;整个文件压缩后约 135,860 字节
服务端发的 pack 可以是 thin 的——引用客户端已有的对象做 delta。对 213 KB 文件改一行,thin pack 只有 308 字节,自包含的 pack 要 136,125 字节。客户端收到后会把 thin pack 补全。