惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
H
Hacker News: Front Page
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
Cisco Blogs
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
AWS News Blog
AWS News Blog
Spread Privacy
Spread Privacy
S
Schneier on Security
The Hacker News
The Hacker News
Cyberwarzone
Cyberwarzone
T
Tenable Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Tailwind CSS Blog
S
Secure Thoughts
N
Netflix TechBlog - Medium
T
The Exploit Database - CXSecurity.com
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Help Net Security
Help Net Security
K
Kaspersky official blog
Google Online Security Blog
Google Online Security Blog
L
LangChain Blog
Martin Fowler
Martin Fowler
L
LINUX DO - 热门话题
Hacker News: Ask HN
Hacker News: Ask HN
www.infosecurity-magazine.com
www.infosecurity-magazine.com
有赞技术团队
有赞技术团队
P
Privacy International News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Recent Announcements
Recent Announcements
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The Register - Security
The Register - Security
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News
阮一峰的网络日志
阮一峰的网络日志
WordPress大学
WordPress大学
Recorded Future
Recorded Future
The Last Watchdog
The Last Watchdog
G
Google Developers Blog
T
Threatpost
小众软件
小众软件
S
Securelist
Recent Commits to openclaw:main
Recent Commits to openclaw:main
O
OpenAI News

Yesterday17's Blog

2026 新年解密红包 / Melody Flag | Yesterday17's Blog 谈谈 Iori 的设计思路(二):如何实现一个 Showroom 录制工具? | Yesterday17's Blog 谈谈 Iori 的设计思路(一):从 Nico Timeshift 说起 | Yesterday17's Blog Iori Minyami 0.1.0 发布 | Yesterday17's Blog 2025 新年解密红包 / Melody Flag | Yesterday17's Blog 使用 Cloudflare Warp 解决罗森票务的海外登录问题 | Yesterday17's Blog How To Blog 04: The Astro v5 Era | Yesterday17's Blog 谈谈 tokio::select! 的公平性 | Yesterday17's Blog Learning Pingora 05 - Connect with TLS | Yesterday17's Blog Leaving Bytedance | Yesterday17's Blog 大橋彩香 AsiaTour「Reflection」上海公演 个人向记录 & Repo | Yesterday17's Blog Recoving from burnout - What happened? | Yesterday17 Yubikey 重建手册 | Yesterday17's Blog How To Blog 03: Heimus | Yesterday17's Blog 🪧 Blog Migration Accouncement | Yesterday17's Blog Learn Your IDE - VSCode 是如何仅重启插件的? | Yesterday17's Blog How To Blog 02: Astro❤️Password | Yesterday17's Blog How To Blog 01: Why, How, and the Future | Yesterday17's Blog Learning Pingora 04 - Establish L4 Connection | Yesterday17's Blog Learning Pingora 03 - Upstreams and Peers | Yesterday17's Blog Learning Pingora 02 - A Simple HTTP Server | Yesterday17's Blog Learning Pingora 01 - Getting Started | Yesterday17's Blog 2024 新年解密红包 / Melody Flag | Yesterday17's Blog 向新的一年飞驰——记录 2023 | Yesterday17's Blog 「サクラノ刻」对话选摘(2) | Yesterday17's Blog PGP Key Revocation 注销声明 | Yesterday17's Blog 「サクラノ刻」对话选摘(1) | Yesterday17's Blog 2023 新年解密红包 / Melody Flag | Yesterday17's Blog 『蒼の彼方のフォーリズム』通关感想 | Yesterday17's Blog 单显卡直通教程 | Yesterday17's Blog 对博客与笔记的思考 | Yesterday17's Blog Project Anni 之旅(3)自动化 Flutter 应用 CI/CD 上架流程 | Yesterday17's Blog AsobiStage 直接播放链接 | Yesterday17 如何在后分P时代进行投稿——sswa使用详解 | Yesterday17's Blog JSON RPC 与 LSP 协议基础 | Yesterday17's Blog Grajapa Shueisha / BookEnd 加密方式调查 | Yesterday17's Blog 【2022篇+WriteUp】如何再收一个新年红包? | Yesterday17's Blog 如何将良心云的良心功能清理干净 | Yesterday17's Blog 【油猴脚本】bilibili 投稿页面返回旧版+旧版页面强制允许分P上传 | Yesterday17's Blog Cloudr1v1 授权方式分析 | Yesterday17 Typora 1.0.2 逆向实录 | Yesterday17's Blog Project Anni 之旅(2)ValueAfterTable——toml-rs的实现与限制 | Yesterday17's Blog IPv4透明代理+IPv6 Passthrough——树莓派单臂软路由折腾记 | Yesterday17's Blog Chaos; Child 汉化补丁 神秘编码探索 | Yesterday17's Blog 镣铐与舞蹈——个性与共性之迷思 | Yesterday17's Blog Go 学习笔记 02 - 找准 io 之道 | Yesterday17's Blog NAT Slipstreaming v1 原理浅析 | Yesterday17's Blog 绕过「9-nine-」的 CDKEY 验证——KrkrPlugin 正(?)向实录 | Yesterday17's Blog 静流的青春纪念册——「サクラノ刻 -櫻の森の下を歩む-」体验版感言 | Yesterday17's Blog Project Anni 之旅 01 - 从 clap-builder 到 derive | Yesterday17's Blog [Google CTF 2021] CPP WriteUp | Yesterday17's Blog 获取 アソビステージ 的实际播放链接 | Yesterday17's Blog 90 行 Rust 代码实现 AsyncTeeReader | Yesterday17's Blog 或许还算有价值一读的文章列表 | Yesterday17's Blog 从零开始的 Seedbox 之旅 | Yesterday17's Blog [随笔]技术型博客行文迷思(1) | Yesterday17's Blog 『ソーサレス*アライヴ! ~the World's End Fallen Star~』通关感想" | Yesterday17's Blog Rust std::fmt 格式语法简述 | Yesterday17's Blog 日亚修改居住国的解决方案 | Yesterday17's Blog [Windows/Linux] GC553 的 Switch 完美采集之路 | Yesterday17's Blog 【翻译】Subtyping and Variance / 子类型与变型 | Yesterday17's Blog Berd's Red Envelope 2021 WriteUp | Yesterday17's Blog 【中英对照】ALSA 音频 API 使用教程/A Tutorial on Using the ALSA Audio API | Yesterday17's Blog 从 cue_scanner.l 看 CUE Sheet 的词法单元 | Yesterday17's Blog Postman 历史记录导出的解决方案 | Yesterday17's Blog 《恋爱绮谭 不存在的夏天》通关感想 | Yesterday17's Blog [微机实验/TD-PITE] 微机接口综合实验 | Yesterday17's Blog [微机实验/TD-PITE] 键盘扫描及数码管显示实验 | Yesterday17's Blog [微机实验/TD-PITE] 数码管显示实验 | Yesterday17's Blog Airsonic Advanced+Google Drive+Caddy 部署纪实 | Yesterday17's Blog X-NUCA 2020 - hellowasm 题解 | Yesterday17's Blog [微机实验/TD-PITE] 8251 串行接口实验 | Yesterday17's Blog Node.js child_process.fork 与 env 污染 RCE | Yesterday17's Blog EP.01 「夜の向日葵」 | Yesterday17's Blog [微机实验/TD-PITE] 8254 定时/计数器实验+选做实验 | Yesterday17's Blog [JLU CTF/2020] babywasm WriteUp | Yesterday17's Blog PHP 反序列化与经典利用 | Yesterday17's Blog WebAssembly 逆向简述 | Yesterday17's Blog 『彼女、お借りします』一期完结点评 | Yesterday17's Blog [微机实验/TD-PITE] D/A 转换实验+选做实验 | Yesterday17's Blog [微机实验/TD-PITE] A/D 转换实验+选做实验 | Yesterday17's Blog 开源项目申请 JetBrains Open Source License 简单流程 | Yesterday17's Blog 微软拼音与 JetBrains 搜索快捷键冲突的解决方案 | Yesterday17's Blog [微机实验/TD-PITE] 8259 中断优先级实验+选做实验 | Yesterday17's Blog IFTTT 测试(续) | Yesterday17 IFTTT 测试 | Yesterday17's Blog [微机实验/TD-PITE] 存储器扩展实验+选做实验 | Yesterday17's Blog 新版 GCC 针对 -fdump-translation-unit 的替代方案 | Yesterday17's Blog 一次 HSTS 策略配置的排错之旅 | Yesterday17's Blog YukiNative 踩坑记——Windows 的消息队列 | Yesterday17's Blog 我是我自己——论获取 HTTPS 证书时的验证步骤 | Yesterday17's Blog 【设计文档】对 PUG 的大规模设计修订(1.1) | Yesterday17's Blog GS65 折腾记(2)加装固态,分区,Grub2 引导 Manjaro LiveCD | Yesterday17's Blog 「さくら、もゆ。」的空白字体列表——一次逆向问题定位过程实录 | Yesterday17's Blog GSuite 探索篇(1)使用 Service Account 向 Google Drive 传输文件 | Yesterday17's Blog 『サクラノ詩 -櫻の森の上を舞う-』通关感想 | Yesterday17's Blog 《ATRI -My Dear Moments-》通关感想 | Yesterday17's Blog [工具][VSCode 扩展] AegiKit——方便 Aegisub 使用的工具箱 | Yesterday17's Blog 贝塞尔曲线、字体矢量化与曲线运算 | Yesterday17's Blog NAT 类型初探 | Yesterday17's Blog
浅谈 git fetch 的工作方式 | Yesterday17's Blog
Yesterday17 · 2021-03-17 · via Yesterday17's Blog

最近写 Anni 的时候遇到一个问题。Anni元数据仓库是以 Git 仓库的形式存在的,但对客户端而言这种形式并不方便交互。

如果客户端只是利用 GitHubAPI 下载 HEAD 的压缩文件,那么仓库和 GitHub 就有了强关联;但如果想要 clone 的话,直接使用 git 命令显然是行不通的,需要一个合适的 Git 实现。

由于客户端需要,我们不仅需要 Anni 编写使用语言的 Rust 版本,还需要一个 Dart 的版本,甚至一个 Web 版本,而后二者目前存在的实现相比 Rust 的实现功能更少,完全不能满足使用需要。

Git 对于很多人,尤其是我,一直都是一个大黑箱。于是正好借着这个机会,我花了一天时间简单地了解了一下这个黑箱中的 fetch 部分,其结果就是这篇文章了。

ToC

  • 环境准备
  • Wireshark 抓包
  • 1
    • 请求
    • 返回
  • 2
    • 请求
    • 返回
  • 3
    • 请求
    • 返回
  • 日志输出
  • 结语

环境准备

我们以 gitea 作为实验环境,通过 pacman -S gitea 直接安装即可,安装完成后通过 systemctl start gitea 启动。

默认监听的端口是 3000,我们可用通过 localhost:3000 进行访问。

我们任意新建一个仓库。方便起见,我们直接使用网页自带自带的初始化。初始化完成后如下图所示:

基于对 Git 的简单认识,我们知道这个仓库目前存在 3Object:一个 COMMIT、一个 TREE 和一个 BLOB

Wireshark 抓包

我们对仓库进行 clone

git clone http://localhost:3000/yesterday17/Test.git --depth=1

同时打开 wiresharkLoopback: lo 进行抓包:

可以看到,总共有三个 HTTP 请求,我们一个一个来看。

1

请求

我们的请求如下:

其中值得注意的是 GETPathGit-Protocol 头。在第一个请求中,我们请求 /info/refs?service=git-upload-pack,并且设置 Git-Protocol 的值为 version=2

返回

该请求的返回如下:

返回的 body 明文如下:

001e# service=git-upload-pack

0000000eversion 2

0015agent=git/2.30.1

000cls-refs

0012fetch=shallow

0012server-option

0017object-format=sha1

0000

可以看到,返回的格式如下:

// 为方便观察,手动增加了部分空格

// 之后的 body 均如此

001e # service=git-upload-pack // 固定

0000 // 此处不存在换行,仅为表示需要

// 0000 表示结束

0015 agent=git/2.30.1 // 服务端 Git 版本

000c ls-refs // 表示服务端支持 ls-refs

0012 fetch=shallow // 表示服务端支持的操作

0012 server-option // 表示可以接收任意数量的 server option

0017 object-format=sha1 // 服务端使用的 hash 算法

0000

在每一行之前,有四个有趣的 16 进制数字,表示的是之后的字节数量。比如第一行是 001e,表示之后有 30 个字节,而:

# service=git-upload-pack

0000

正好是 30 个字符。

2

在获取了服务端的信息之后,我们就可以开始正式请求了。

请求

这里我们发现,首先是 method 变成了 POST;其次是请求的 path 发生了变化,变成了 /git-upload-pack;最后是 Content-Type,为 application/x-git-upload-pack-request。让我们来看看 body

0014 command=ls-refs # 命令

0014 agent=git/2.30.1 # 本地 Git 版本

0016 object-format=sha1 # 使用的 hash 算法

0001

0009 peel

000c symrefs

0014 ref-prefix HEAD # 获取 HEAD

001b ref-prefix refs/heads/ # 获取 ref/heads

001a ref-prefix refs/tags/ # 获取 ref/tags

0000

返回

返回结果如下:

0052 869bda16bc91c702d812e18fd9a2653dd8c9f461 HEAD symref-target:refs/heads/master

003f 869bda16bc91c702d812e18fd9a2653dd8c9f461 refs/heads/master

0000

可以看到,从这次请求中,我们得到了 ref/heads/masterhash 数据。

3

在得到了 commithash 之后,我们就可以向服务器请求数据了。

请求

请求的 body 如下:

0016 object-format=sha1

0011 command=fetch # 命令为 fetch

0014 agent=git/2.30.1

0001

000d thin-pack

000f include-tag

000d ofs-delta # Pack 使用 OFS_DELTA(下一篇博客详述)

000c deepen 1 # 只 clone 1 层

0032 want 869bda16bc91c702d812e18fd9a2653dd8c9f461

0032 want 869bda16bc91c702d812e18fd9a2653dd8c9f461

0009 done

0000

这里的 ofs-deltaGit 的打包有关,下一篇博客再述。其他的部分都比较直观。

返回

返回的 body 如下:

0011 shallow-info

0034 shallow 869bda16bc91c702d812e18fd9a2653dd8c9f461

0001

000d packfile

0021.枚举对象中: 3, 完成.

007e.对象计数中: 33% (1/3)

对象计数中: 66% (2/3)

对象计数中: 100% (3/3)

对象计数中: 100% (3/3), 完成.

0043.总共 3(差异 0),复用 0(差异 0),包复用 0

00dc.PACK........Պxܕ˻

B1..󜢻A6ϼAĖcl̆..ؖﶆ<ET3Ó.2ִʥ.򹕐.z.X\!ʖ奱mѻC.}̉;ߕgŝF8˴뢨ï`õ).ץ8`B4󡒵տL涪ک ߛ|.1G1Υ.xܳ40031Q.rut񵕋Ma蘙.v:wc󲝓9ޱ𜻻T..݁..8xܓV.I-.ᢂ. 7...%..ïۭ̊؍6諊

0006.&

0000

可以发现其中有一些乱码和点(.),这就是实际的二进制部分了。这部分的结构也很简单,在 packfile 之后 就是实际的 Pack 部分了,分为三种类型。

第一种类型就是实际的 Pack 内容,为 0x01

第二种类型为进度消息,由服务端发送以告知客户端 fetch 的进度,类型为 0x02

最后一种是错误信息,当服务端出现致命错误时发出,类型为 0x03

类型信息存储在四位 16 进制数之后的一个字节中,也就是上文中诸如 0021007e00dc 后面的点(.)。

日志输出

Git 提供了调试日志,我们可以来看一下:

❯ GIT_TRACE=1 GIT_TRACE_PACKET=1 git clone http://localhost:3000/yesterday17/Test.git --depth 1

11:45:42.031851 git.c:444 trace: built-in: git clone http://localhost:3000/yesterday17/Test.git --depth 1

正克隆到 'Test'...

11:45:42.039795 run-command.c:664 trace: run_command: git remote-http origin http://localhost:3000/yesterday17/Test.git

11:45:42.040655 git.c:730 trace: exec: git-remote-http origin http://localhost:3000/yesterday17/Test.git

11:45:42.040693 run-command.c:664 trace: run_command: git-remote-http origin http://localhost:3000/yesterday17/Test.git

11:45:42.047808 pkt-line.c:80 packet: git< # service=git-upload-pack

11:45:42.047858 pkt-line.c:80 packet: git< 0000

11:45:42.047861 pkt-line.c:80 packet: git< version 2

11:45:42.047863 pkt-line.c:80 packet: git< agent=git/2.30.1

11:45:42.047865 pkt-line.c:80 packet: git< ls-refs

11:45:42.047867 pkt-line.c:80 packet: git< fetch=shallow

11:45:42.047869 pkt-line.c:80 packet: git< server-option

11:45:42.047871 pkt-line.c:80 packet: git< object-format=sha1

11:45:42.047873 pkt-line.c:80 packet: git< 0000

11:45:42.047946 pkt-line.c:80 packet: clone< version 2

11:45:42.047957 pkt-line.c:80 packet: clone< agent=git/2.30.1

11:45:42.047964 pkt-line.c:80 packet: clone< ls-refs

11:45:42.047969 pkt-line.c:80 packet: clone< fetch=shallow

11:45:42.047974 pkt-line.c:80 packet: clone< server-option

11:45:42.047980 pkt-line.c:80 packet: clone< object-format=sha1

11:45:42.047985 pkt-line.c:80 packet: clone< 0000

11:45:42.047989 pkt-line.c:80 packet: clone> command=ls-refs

11:45:42.047997 pkt-line.c:80 packet: clone> agent=git/2.30.1

11:45:42.047998 pkt-line.c:80 packet: git< command=ls-refs

11:45:42.048001 pkt-line.c:80 packet: clone> object-format=sha1

11:45:42.048004 pkt-line.c:80 packet: git< agent=git/2.30.1

11:45:42.048006 pkt-line.c:80 packet: clone> 0001

11:45:42.048028 pkt-line.c:80 packet: git< object-format=sha1

11:45:42.048029 pkt-line.c:80 packet: clone> peel

11:45:42.048030 pkt-line.c:80 packet: git< 0001

11:45:42.048034 pkt-line.c:80 packet: clone> symrefs

11:45:42.048035 pkt-line.c:80 packet: git< peel

11:45:42.048039 pkt-line.c:80 packet: clone> ref-prefix HEAD

11:45:42.048040 pkt-line.c:80 packet: git< symrefs

11:45:42.048044 pkt-line.c:80 packet: git< ref-prefix HEAD

11:45:42.048044 pkt-line.c:80 packet: clone> ref-prefix refs/heads/

11:45:42.048050 pkt-line.c:80 packet: clone> ref-prefix refs/tags/

11:45:42.048053 pkt-line.c:80 packet: git< ref-prefix refs/heads/

11:45:42.048054 pkt-line.c:80 packet: clone> 0000

11:45:42.048057 pkt-line.c:80 packet: git< ref-prefix refs/tags/

11:45:42.048061 pkt-line.c:80 packet: git< 0000

11:45:42.050613 pkt-line.c:80 packet: clone< 869bda16bc91c702d812e18fd9a2653dd8c9f461 HEAD symref-target:refs/heads/master

11:45:42.050613 pkt-line.c:80 packet: git> 0002

11:45:42.050624 pkt-line.c:80 packet: clone< 869bda16bc91c702d812e18fd9a2653dd8c9f461 refs/heads/master

11:45:42.050628 pkt-line.c:80 packet: clone< 0000

11:45:42.050637 pkt-line.c:80 packet: clone< 0002

11:45:42.051301 pkt-line.c:80 packet: clone> command=fetch

11:45:42.051308 pkt-line.c:80 packet: clone> agent=git/2.30.1

11:45:42.051311 pkt-line.c:80 packet: clone> object-format=sha1

11:45:42.051317 pkt-line.c:80 packet: clone> 0001

11:45:42.051320 pkt-line.c:80 packet: clone> thin-pack

11:45:42.051321 pkt-line.c:80 packet: git< object-format=sha1

11:45:42.051323 pkt-line.c:80 packet: clone> include-tag

11:45:42.051327 pkt-line.c:80 packet: clone> ofs-delta

11:45:42.051335 pkt-line.c:80 packet: clone> deepen 1

11:45:42.051339 pkt-line.c:80 packet: clone> want 869bda16bc91c702d812e18fd9a2653dd8c9f461

11:45:42.051342 pkt-line.c:80 packet: clone> want 869bda16bc91c702d812e18fd9a2653dd8c9f461

11:45:42.051344 pkt-line.c:80 packet: clone> done

11:45:42.051347 pkt-line.c:80 packet: clone> 0000

11:45:42.051354 pkt-line.c:80 packet: git< command=fetch

11:45:42.051359 pkt-line.c:80 packet: git< agent=git/2.30.1

11:45:42.051362 pkt-line.c:80 packet: git< 0001

11:45:42.051365 pkt-line.c:80 packet: git< thin-pack

11:45:42.051369 pkt-line.c:80 packet: git< include-tag

11:45:42.051372 pkt-line.c:80 packet: git< ofs-delta

11:45:42.051375 pkt-line.c:80 packet: git< deepen 1

11:45:42.051379 pkt-line.c:80 packet: git< want 869bda16bc91c702d812e18fd9a2653dd8c9f461

11:45:42.051383 pkt-line.c:80 packet: git< want 869bda16bc91c702d812e18fd9a2653dd8c9f461

11:45:42.051386 pkt-line.c:80 packet: git< done

11:45:42.051389 pkt-line.c:80 packet: git< 0000

11:45:42.057628 pkt-line.c:80 packet: git> 0002

11:45:42.057664 pkt-line.c:80 packet: clone< shallow-info

11:45:42.057672 pkt-line.c:80 packet: clone< shallow 869bda16bc91c702d812e18fd9a2653dd8c9f461

11:45:42.057676 pkt-line.c:80 packet: clone< 0001

11:45:42.057754 pkt-line.c:80 packet: clone< packfile

11:45:42.057798 run-command.c:664 trace: run_command: git --shallow-file /home/yesterday17/Test/.git/shallow.lock index-pack --stdin -v --fix-thin '--keep=fetch-pack 97854 on Yesterday17-PC'

11:45:42.057820 pkt-line.c:80 packet: sideband< \2\37777777746\37777777636\37777777632\37777777744\37777777670\37777777676\37777777745\37777777657\37777777671\37777777750\37777777661\37777777641\37777777744\37777777670\37777777655: 3, \37777777745\37777777656\37777777614\37777777746\37777777610\37777777620.

remote: 枚举对象中: 3, 完成.

11:45:42.057976 pkt-line.c:80 packet: sideband< \2\37777777745\37777777657\37777777671\37777777750\37777777661\37777777641\37777777750\37777777656\37777777641\37777777746\37777777625\37777777660\37777777744\37777777670\37777777655: 33% (1/3)\15\37777777745\37777777657\37777777671\37777777750\37777777661\37777777641\37777777750\37777777656\37777777641\37777777746\37777777625\37777777660\37777777744\37777777670\37777777655: 66% (2/3)\15\37777777745\37777777657\37777777671\37777777750\37777777661\37777777641\37777777750\37777777656\37777777641\37777777746\37777777625\37777777660\37777777744\37777777670\37777777655: 100% (3/3)\15\37777777745\37777777657\37777777671\37777777750\37777777661\37777777641\37777777750\37777777656\37777777641\37777777746\37777777625\37777777660\37777777744\37777777670\37777777655: 100% (3/3), \37777777745\37777777656\37777777614\37777777746\37777777610\37777777620.

remote: 对象计数中: 100% (3/3), 完成.

11:45:42.058028 pkt-line.c:80 packet: sideband< \2\37777777746\37777777600\37777777673\37777777745\37777777605\37777777661 3\37777777757\37777777674\37777777610\37777777745\37777777667\37777777656\37777777745\37777777674\37777777602 0\37777777757\37777777674\37777777611\37777777757\37777777674\37777777614\37777777745\37777777644\37777777615\37777777747\37777777624\37777777650 0\37777777757\37777777674\37777777610\37777777745\37777777667\37777777656\37777777745\37777777674\37777777602 0\37777777757\37777777674\37777777611\37777777757\37777777674\37777777614\37777777745\37777777614\37777777605\37777777745\37777777644\37777777615\37777777747\37777777624\37777777650 0

remote: 总共 3(差异 0),复用 0(差异 0),包复用 0

11:45:42.058041 pkt-line.c:80 packet: sideband< PACK ...

11:45:42.058065 pkt-line.c:80 packet: sideband< 0000

11:45:42.058688 git.c:444 trace: built-in: git index-pack --stdin -v --fix-thin '--keep=fetch-pack 97854 on Yesterday17-PC'

接收对象中: 100% (3/3), 完成.

11:45:42.077544 pkt-line.c:80 packet: clone< 0002

11:45:42.078348 run-command.c:664 trace: run_command: git rev-list --objects --stdin --not --all --quiet --alternate-refs '--progress=正在检查连通性'

11:45:42.079311 git.c:444 trace: built-in: git rev-list --objects --stdin --not --all --quiet --alternate-refs '--progress=正在检查连通性'

过程很清晰,这里就不多赘述了。

结语

这篇文章诞生之初想讲的是 git clone 的流程,但众所周知 git clone 包括 git initgit fetchgit checkout 三个步骤,内容还是有点太多了。况且目前的应用场景也不需要了解太多,于是就只研究了最核心的 git fetch

Git 的文档只能说是「存在」,其中说得不清楚的地方太多,个人实现的过程中也是磕磕绊绊的。这篇文章也算是艰难 Debug 路上的慰藉吧(笑