2.Git 基础

2026-02-14 02:01:34

哑协议

如果你正在架设一个基于 HTTP 协议的只读版本库,一般而言这种情况下使用的就是哑协议。

这个协议之所以被称为“哑”协议,是因为在传输过程中,服务端不需要有针对 Git 特有的代码;抓取过程是一系列 HTTP 的 GET 请求,这种情况下,客户端可以推断出服务端 Git 仓库的布局。

Note

现在已经很少使用哑协议了。

使用哑协议的版本库很难保证安全性和私有化,所以大多数 Git 服务器宿主(包括云端和本地)都会拒绝使用它。

一般情况下都建议使用智能协议,我们会在后面进行介绍。

让我们通过 simplegit 版本库来看看 http-fetch 的过程:

$ git clone http://server/simplegit-progit.git

它做的第一件事就是拉取 info/refs 文件。

这个文件是通过 update-server-info 命令生成的,这也解释了在使用 HTTP 传输时,必须把它设置为 post-receive 钩子的原因:

=> GET info/refs

ca82a6dff817ec66f44342007202690a93763949 refs/heads/master

现在,你得到了一个远程引用和 SHA-1 值的列表。

接下来,你要确定 HEAD 引用是什么,这样你就知道在完成后应该被检出到工作目录的内容:

=> GET HEAD

ref: refs/heads/master

这说明在完成抓取后,你需要检出 master 分支。

这时,你就可以开始遍历处理了。

因为你是从 info/refs 文件中所提到的 ca82a6 提交对象开始的,所以你的首要操作是获取它:

=> GET objects/ca/82a6dff817ec66f44342007202690a93763949

(179 bytes of binary data)

你取回了一个对象——这是一个在服务端以松散格式保存的对象,是你通过使用静态 HTTP GET 请求获取的。

你可以使用 zlib 解压缩它,去除其头部,查看提交记录的内容:

$ git cat-file -p ca82a6dff817ec66f44342007202690a93763949

tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf

parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7

author Scott Chacon 1205815931 -0700

committer Scott Chacon 1240030591 -0700

changed the version number

接下来,你还要再获取两个对象,一个是树对象 cfda3b,它包含有我们刚刚获取的提交对象所指向的内容,另一个是它的父提交 085bb3:

=> GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7

(179 bytes of data)

这样就取得了你的下一个提交对象。

再抓取树对象:

=> GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf

(404 - Not Found)

噢——看起来这个树对象在服务端并不以松散格式对象存在,所以你得到了一个 404 响应,代表在 HTTP 服务端没有找到该对象。

这有好几个可能的原因——这个对象可能在替代版本库里面,或者在包文件里面。

Git 会首先检查所有列出的替代版本库:

=> GET objects/info/http-alternates

(empty file)

如果这返回了一个包含替代版本库 URL 的列表,那么 Git 就会去那些地址检查松散格式对象和文件——这是一种能让派生项目共享对象以节省磁盘的好方法。

然而,在这个例子中,没有列出可用的替代版本库。所以你所需要的对象肯定在某个包文件中。

要检查服务端有哪些可用的包文件,你需要获取 objects/info/packs 文件,这里面有一个包文件列表(它也是通过执行 update-server-info 所生成的):

=> GET objects/info/packs

P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack

服务端只有一个包文件,所以你要的对象显然就在里面。但是你要先检查它的索引文件以确认。

即使服务端有多个包文件,这也是很有用的,因为这样你就可以知道你所需要的对象是在哪一个包文件里面:

=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx

(4k of binary data)

现在你有这个包文件的索引,你可以查看你要的对象是否在里面——

因为索引文件列出了这个包文件所包含的所有对象的 SHA-1 值,和该对象存在于包文件中的偏移量。

你的对象就在这里,接下来就是获取整个包文件:

=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack

(13k of binary data)

现在你也有了你的树对象,你可以继续在提交记录上漫游。

它们全部都在这个你刚下载的包文件里面,所以你不用继续向服务端请求更多下载了。

Git 会将开始时下载的 HEAD 引用所指向的 master 分支检出到工作目录。

最新发表
友情链接