本地 git fetch
后,.git/objects/pack/
常突然变大——远端把对象打成 pack
传来,git index-pack
解包入库。理解传输在磁盘上的落点,能解释「只 fetch
一个分支为何 pack 很大」「浅克隆为何有 shallow
文件」。
本文讲 pack 落盘与 ref 更新;不展开 HTTP smart 协议路由或服务器实现(见 gitprotocol-pack 规范边界)。
一、fetch 概要
git fetch <remote>:
- 与远端协商(have/want SHAs)。
- 接收 pack 字节流(可能 thin pack)。
git index-pack写入objects/pack/pack-*.pack+.idx。- 更新
refs/remotes/<remote>/<branch>(或配置指定的 ref)。 - 写
FETCH_HEAD:本次抓取的 ref 与 SHA 列表。
对象格式与本地 commit 相同;pack 布局见 第 07 篇。
二、FETCH_HEAD
文本文件,每行:
<sha> <ref-description>
git pull 合并时读此文件决定合并目标。多次
fetch 会覆盖/追加(行为依版本与参数)。
三、refs/remotes
git clone 后:
.git/refs/remotes/origin/HEAD
.git/refs/remotes/origin/master # 示例
指向上次 fetch 见到的远端 tip。与本地
refs/heads/* 独立;git push
更新远端服务器上的 ref,本地 refs/remotes
在下次 fetch 才同步。
git clone --bare 无 remotes 目录,仅有
refs/heads(实测 bare 克隆清单含
packed-refs)。
四、浅克隆与 shallow 文件
git clone --depth 1 <url>
只拉最近历史;.git/shallow 列出边界
commit(仍视为 tip,但 parent 可能不在库中):
2b24b26a117ef5cfdbf88040e282ec86085e6b1f
git fetch --unshallow 补全历史后,实测
shallow 文件消失(Git 2.54.0 实验)。
浅仓库的 git rev-list 遇边界停止;对象仍存于
pack/松散对象。
五、push 的本地副作用
git push 主要改远端
ref;本机通常:
- 更新 remote-tracking ref(若配置
remote.<name>.mirror等例外)。 - 不复制新对象到本机(对象已在本地)。
若 push 被拒,本地无额外损坏;若用
git push --force,远端 ref 移动,与本机
objects 无直接关系。
六、本地协议实验
无需网络:
git init --bare /tmp/bare.git
cd /tmp/wt && git clone /tmp/bare.git . -q
echo hi > f && git add f && git commit -qm h && git push -q
cd /tmp/clone && git clone /tmp/bare.git clone -q
find clone/.git/objects/pack -type f
cat clone/.git/shallow 2>/dev/null || true
git clone --depth 1 file:///tmp/bare.git shallow -q && cat shallow/.git/shallow七、本文边界
- 不讨论 credential、ref advertisement 的 HTTP 细节。
- 不对比 SSH vs HTTPS 性能。
参考资料
- Git documentation, gitprotocol-pack
- Git documentation, gitrepository-layout
- 实验环境:Git 2.54.0
系列索引:Git 内部结构 · 上篇:merge/rebase · 下篇:worktree/submodule
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Git 内部】.git 目录全景:三棵树与仓库布局
git init 之后 .git 里每个路径干什么?对照 gitrepository-layout 与本地 find 清单,建立 bare/non-bare、三棵树、objects/refs/index 的磁盘级地图。
【Git 内部】松散对象:zlib 载荷与 SHA-1 路径
blob/tree/commit 在磁盘上如何编码?对象头 type size、zlib 压缩、objects/ab/cdef 路径命名,对照 git hash-object 与手工 SHA-1 验证。
【Git 内部】对象图:tree、commit、tag 的链式结构
一次提交在对象库里如何连成链?tree 条目 mode/name/hash、commit 的 tree/parent 字段、annotated tag,用 git cat-file -p 展开并画对象图。
【Git 内部】refs、HEAD 与 packed-refs
分支和标签在磁盘上只是一行 SHA?refs/heads、符号引用 HEAD、packed-refs 文件头与 peeled tag 的格式与生成时机。