Git 底层原理:传输协议分析(二)
目录
概要
上一篇文章讲了 git 传输协议的相关流程和要点,本文算上一篇文章的总结帖,涉及了传输协议格式、环境变量、子命令等,并附上了相关的源码说明。
Git 传输协议格式
pkt-line 格式
pkt-line 数据流用来描述引用信息,每一行的前四个字节代表这一行的十六进制编码的长度,包括这四个字节和数据在内。因为包括自身四个字节,前四个直接一定大于0004,所以 pkt-line 格式定义了3个特殊的编码:
- 0000 (
flush-pkt
):代表一段消息的结束。 - 0001 (
delim-pkt
):代表一段消息的分节符。 - 0002 (
response-end-pkt
):无状态会话时响应结束。
side-band 格式
side-band 格式用来传递 pack 包数据和进度的。前四个字节和 pkt-line 格式相同,代表这一行的数据长度。第五位用于标志消息类型,0x01
代表是packfile 数据,0x02
代表是进度消息,0x03
代表是错误信息。
引用发现数据格式
引用数据
由服务端发送给客户端。整体来讲,一个 引用数据格式 一般由如下几部分组成:
1 | PKT-LINE("# service=$servicename" LF) |
其中
PKT-LINE
代表这一行是 pkt-line 格式的。servicename
是服务类型,git clone、git fetch 是git-upload-pack
,git push 则是git-receive-pack
。ref_list
是引用信息列表,一定是按照引用名称排序的。
ref_list
第一个引用一定是 HEAD
, HEAD
后面一定有支持的功能说明( capability declarations )第一条引用信息格式为:
1 | PKT-LINE(obj-id SP name NUL cap_list LF) |
其中
cap_list
是支持的功能列表( capability list )。
后面的引用信息格式为:
1 | PKT-LINE(obj-id SP name LF) |
- 其中
name^{}
是 git revision 中定义的格式,表示递归该引用找到非 tag 类型的 object 。- 另外,如果该仓库没有引用时,那
ref_list
的内容则是:PKT-LINE(zero-id SP "capabilities^{}" NUL cap-list LF)
。
pkt-line 官方说明见:http-protocol.txt。
git-upload-pack 数据流格式
git-upload-pack 请求数据
由客户端发送给服务端,表示客户端需要( "want"
)哪些 commit-id
,同时也会说明自己有( "have"
)哪些 commit-id
。git-upload-pack 请求数据格式相对简单,请求数据一定会有个 "want"
,且第一条需要带上功能说明( capability declarations ):
1 | PKT-LINE("want" SP obj-id SP cap_list LF) |
_git-upload-pack 回复数据
_由服务端回复给客户端,
git-receive-pack 数据流格式
更多信息
git 支持 4 种交互协议
Git 客户端和服务端交互的协议支持 4 种:本地协议
、 http 协议
、 ssh 协议
、 git 协议
,在我们的日常开发过程中,接触最多的是 http 协议
和 ssh 协议
。
根据 URL 的前缀可以知道使用的是什么协议: file://
、 https://
、 ssh://
、 git://
。通过如下命令和服务器进行交互:
1 | # 本地协议 |
本地协议
一般用于共享目录的开发模式,不过需要有文件系统访问权限,安全性不能保障。git 协议
默认使用9418
端口,但是没有认证过程,数据传输过程也不加密,安全性也不能保障。
哑协议
上面使用 Wireshark 抓取到的协议叫智能( smart )协议,实际上 Git 1.6.6 之前的版本(2010年前)一直使用哑( Dumb )协议。使用哑协议的版本库很难保证安全性和私有化,而且只能架设只读版本库,目前已经很少使用了,哑协议的交互过程可以参考《Git Internals - Transfer Protocols》。
protocol v2
Git 目前已经有新的 protocol v2 协议,支持更高阶的特性,比如能力广播、断电续传等。
相关环境变量
除了 GIT_TRACE
用于查看 Git 运行日志,Git 还提供了几个非常有意思的环境变量用于查看和调试传输协议。
GIT_TRACE
GIT_TRACE_PACKET
GIT_TRACE_PACKET=true
:显示协议交互数据,不过不会显示 PACK 包内容。输出的信息是经过格式化的,并不是原始数据,不过这个比较容易理解和阅读。
GIT_TRACE_PACKFILE
GIT_TRACE_PACKFILE=<file-path>
:把协议交互中的 PACK 包保存到指定文件中,如果设置为 GIT_TRACE_PACKFILE=true
,那就显示在标准输出。
GIT_TRACE_CURL
显示 curl 交互信息,包括 TLS
+ HTTP
+ Git 协议
,该数据和使用 Wireshark 抓到的信息基本相同。
相关命令
git-update-server-info
该命令可以生成 info/refs
引用信息。
1 | `git --exec-path`/git-update-server-info && cat .git/info/refs |
相关源码
参考资料
- https://github.com/git/git/blob/master/Documentation/technical/http-protocol.txt
- https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols
- https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols
- https://wangdoc.com/ssh/client.html
- https://github.com/gcla/termshark
- Walkthrough: Decrypt SSL/TLS traffic (HTTPS and HTTP/2) in Wireshark