git的底层原理
对象 Objects
git使用 Object 存储所有类型的内容,也称为git对象,全部存储在 .git/objects/ 目录下
git使用40位的SHA-1值作为 object 文件的唯一标识符,前两位 SHA-1作为目录名称,后38位SHA-1作为文件名. git会把所有object进行zlib压缩.
object可以分为头部信息和存储内容两部分
- 头部信息: 类型+空格+内容字节数+\0
- 存储内容
git有四种 object
- Blob object 二进制对象
- 对于普通文件,git使用 blob object (二进制对象) 存储文件内容.
- Tree object 树对象
- 对于目录文件,git使用一个 tree object (树对象) 存储一系列 tree entry, 每个 tree entry 存储一个 blob object (普通文件) 或一个 tree object (目录下的目录).
- Commit object 提交对象
- tree object和blob object用于表示版本快照,commit object用于表示版本索引和版本关系
- commit object的存储内容包括:
- 对应的根 tree object 对应的SHA-1
- 0个,1个或多个父级commit object对应的SHA-1(第一次commit没有父节点,出现分支合并时会出现多个父级commit object)
- commit的内容,包括:author,committer,encoding,commit message等
- Tag object 标签对象
可以使用git cat-file 命令查看object的存储内容
1 | |
引用 refs
如果我们对某一个commit感兴趣,直接记忆SHA-1值并不方便,因此git提供了"别名"来代替SHA-1值,即 references (refs) 引用
git支持三种引用类型,存储在 .git/refs/ 中
HEAD引用
- 通常指向当前所在的分支(而并不是指向某一个commit对象)
- 在特殊情况下(例如checkout一个commit导致detached head状态时),HEAD文件可能会包含一个git object的SHA-1值
- 可以通过
cat .git/HEAD查看HEAD文件
标签引用 (tag reference)
包含两种类型:轻量标签 和 附注标签
轻量标签可以使用如下命令创建
1
git update-ref refs/tags/v1.0 a9f2652cb992f300c0a251d3607bdabfe8901bb2这条命令会创建一个以标签名命名的文本文件,文件内容位其所引用的commit object的SHA-1值
附注标签可以使用如下命令创建
1
git tag -a v1.1 a9f2652cb992f300c0a251d3607bdabfe8901bb2 -m "test tag"这条命令会创建一个 tag object, 存储在
.git/objects/下tag object的存储内容包括:所引用object的SHA-1值
远程引用
- 如果添加了远程仓库并执行过
git push操作,git会记录最近一次push操作时每一个分支所对应的值,保存在.git/refs/remotes/目录下
- 如果添加了远程仓库并执行过
包文件 pack
每当我们对文件进行修改,git就会创建一个新的blob object(文件内容),如果文件很大,只修改其中的极小一部分内容,会导致严重的磁盘空间浪费。
git的解决方法是:不定时对blob对象打包并移除,生成两个文件
- pack file (包文件): 采用原始内容+增量内容的形式存储
- index file (索引文件): 存储各个pack file中object的大小/偏移/类型等数据,从而便于重建文件快照和object的关系
包文件存储在 .git/objects/pack/ 中
可以使用 git verify-pack 查看
当 git push, git pull 等命令时git会自动进行打包,也可以手动使用 git gc 命令来进行打包
Commit
当执行 git commit 时,git会创建一个 commit object,包含指向顶层tree的指针,parent commit的哈希值,和作者/提交者信息/commit message等
工作区 索引 仓库
工作区即我们能看到和编辑的文件
索引(index, staging area) 是一个暂存区域,里面的代码会在下一次commit时被提交到git repo。当 git add xxx 时,该文件会被放在暂存区,同时新增一个 blob.
仓库 (git repository)即 .git 隐藏文件夹里的内容
分支
git分支,本质上只是一个指向commit对象的可变指针