首先需要了解 Git 存储库的目录结构,Git 存储库分为常规存储库和 Bare (裸)存储库,普通用户从远程克隆下来的存储库,或者本地初始化的存储库大多是常规存储库,这类存储库和特定的工作区相关联;另一类是没有工作区的存储库,就是裸存储库,在代码托管平台的服务器上,存储库几乎都是以裸存储库的方式存储的。对于常规存储库而言,其存储库真正的路径是工作区根目录下的 .git 文件夹,或者 .git 文件指向的目录,后者通常用于 Git 子模块。
知道了 Git 存储库的位置,就可以查看存储库的目录结构,下面是一个查看存储库的截图。
不同的目录具备不同的作用,大致如下:
路径 属性 作用 备注HEAD R 存储当前检出的引用或者提交 ID 在远程服务器上用于展示默认分支
config R 存储库配置 存储库配置优先级高于用户配置,用户配置优先级高于系统配置
branches D deprecated
description R depracated
hooks D Git 钩子目录,包括服务端钩子和客户端钩子 当设置了 core.hooksPath 时,则会从设置的钩子目录查找钩子
info D 存储库信息 dump 协议依赖,但目前 dump 协议已无人问津
objects D 存储库对象存储目录
refs D 存储库引用存储目录
packed-refs R 存储库打包引用存储文件 该文件可能不存在,运行 git pack-refs 或者 git gc 后出现
在这些目录或者文件中,最重要的是 objects 和 refs ,只需要两个目录的数据就可以重建存储库了。在 objects 目录下,Git 对象可能以松散对象也可能以打包对象的形式存储:
路径 描述objects/[0-9a-f][0-9a-f] 松散对象存储目录,最多有 256 个这样的子目录
objects/pack 打包对象目录,除了打包对象,还有打包对象索引,多包索引等
objects/info 存储存储库扩展信息
objects/info/packs 哑协议依赖
objects/info/alternates 存储库对象借用技术
objects/info/http-alternates 存储库对象借用,用于 HTTP fetch
Git 在实现其复杂功能的时候还会创建一些其他目录,更详细的细节可以查阅:Git Repository Layout。
Git 对象的存储Git 的对象可以按照松散对象的格式存储,也可以按照打包对象的格式存储,用户将文件纳入版本控制时,Git 会将文件的类型标记为 blob,将文件长度和 \x00 以及文件内容合并在一起计算 SHA1 哈希值后,使用 Deflate 压缩,存储到存储库的 objects 目录下,路径匹配正则为 objects\/[0-9a-f]{2}\/[0-9a-f]{38}$,当然如果使用 SHA256 则应该匹配 objects\/[0-9a-f]{2}\/[0-9a-f]{62}$,松散对象的空间布局如下:
Git 使用的 Deflate 是 Phil Katz 为 PKZIP 创建的压缩算法,也是使用最广泛的压缩算法之一,其变体 GZIP 也被广泛用于 POSIX 文件压缩和 HTTP 压缩。Git 命令行,libgit2 目前依赖 zlib 提供 deflate 算法,jgit 则使用 Java 提供的 deflate 实现,Golang 则在 compress/zlib 包中提供 deflate 支持,但算法实现在 compress/flate,严格来说 Git 使用的是 deflate 的 zlib 包装,比如我们使用 zlib 创建 zip 压缩包时会使用 -15 作为 WindowBits,而在创建 GZIP 时会使用 31 作为 WindowBits,在 Git 中,则会使用 15 作为 WindowBits。