30.1 考点:仓库
Git 仓库是一个存储文件和目录元数据的磁盘数据结构,位于项目目录的 .git/ 文件夹中。每次提交数据时,这些数据都会存储在这里,而 .git/ 文件夹则包含所有提交记录。其基本结构如下:
.git/
objects/
refs/
30.2 对象
Git 本质上是一个键值存储系统。当你向 Git 添加数据时,它会构建一个对象,并使用对象内容的 SHA-1 哈希值作为键。因此,Git 中的任何内容都可以通过其哈希值查找:
git cat-file -p 4bb6f98
Git 中有四种类型的对象:
- blob:表示文件内容。
- tree:表示目录结构。
- commit:表示提交记录。
- tag:表示标签。
30.3 HEAD 引用
HEAD 是一个特殊的引用,始终指向当前对象。你可以通过查看 .git/HEAD 文件来查看它当前指向的位置:
$ cat .git/HEAD
ref: refs/heads/mainline
通常,HEAD 指向另一个引用,但也可以直接指向一个对象:
$ cat .git/HEAD
4bb6f98a223abc9345a0cef9200562333
这就是所谓的“悬挂 HEAD”——因为 HEAD 不再指向任何引用,而是直接指向一个对象。
30.4 引用
引用本质上是一个指针,它指向一个对象。例如:
"mainline" --> 4bb6f98...
它们以纯文本文件的形式存储在 .git/refs/heads/ 中:
$ cat .git/refs/heads/mainline
4bb6f98a223abc9345a0cef9200562333
这些通常被称为分支。然而,在 Git 中,实际上并不存在“分支”这一概念,只有引用。虽然你可以通过直接跳转到不同对象的哈希值来浏览 Git,但这会非常不方便。引用为你提供了一个方便的名称来引用对象,而不是通过哈希值。
30.5 提交对象
提交对象可能是 Git 用户最熟悉的对象类型,因为它通常是通过 git commit 命令创建的。然而,提交对象并不直接包含任何更改的文件或数据,而是主要包含元数据和指向其他包含实际提交内容的对象的指针。 提交对象包含以下内容:
- 一个树对象的哈希值
- 一个父提交对象的哈希值
- 作者和提交者的姓名和电子邮件
- 提交信息 你可以通过以下方式查看任何提交的内容:
$ git cat-file commit 5bac93
tree 04d1daef...
parent b7850ef5...
author Geddy Lee <glee@rush.com>
committer Neil Peart <npeart@rush.com>
First commit!
重要说明:树对象存储了项目中的每个文件,并且存储的是整个文件,而不是差异。这意味着每个提交都包含项目的一个快照。
父提交:父提交行包含另一个提交对象的哈希值,可以看作是一个“父指针”,指向“前一个提交”。这隐式地形成了一个称为提交图的提交图,具体来说,这是一个有向无环图(DAG)。
30.6 树对象
树对象基本上表示传统文件系统中的一个文件夹:文件或文件夹的嵌套容器。 树对象包含:
- 0个或多个 blob 对象
- 0个或多个树对象 就像你可以使用
ls或dir列出文件夹的内容一样,你也可以列出树对象的内容:
$ git cat-file -p 07b1a631
100644 blob b91bba1b .gitignore
100644 blob cc0956f1 Makefile
040000 tree 92e1ca7e src
...
你可以通过首先找到提交中的树的哈希值,然后查看该树来查找提交中的文件:
$ git cat-file commit 4bb6f93a
tree 07b1a631
parent ...
author ...
committer ...
$ git cat-file -p 07b1a631
100644 blob b91bba1b .gitignore
100644 blob cc0956f1 Makefile
040000 tree 92e1ca7e src
...
30.7 blob 对象
Blob 对象包含任意二进制文件内容。通常,它会是原始文本,如源代码或博客文章。但也可以是 PNG 文件的字节或其他任何内容。 如果你有 blob 的哈希值,你可以查看其内容:
$ git cat-file -p d429810
package com.example.project
class Foo {
...
}
...
例如,你可以像上面那样浏览树对象,然后查看其中的一个 blob:
$ git cat-file -p 07b1a631
100644 blob b91bba1b .gitignore
100644 blob cc0956f1 Makefile
040000 tree 92e1ca7e src
100644 blob cae391ff Readme.txt
$ git cat-file -p cae391ff
Welcome to my project! This is the readme file
...
30.8 创建新提交
git commit 命令执行以下操作:
- 创建表示项目目录的 blob 和树对象,并存储在
.git/objects中。 - 创建一个新的提交对象,包含你的作者信息、提交信息以及步骤 1 中的根树,也存储在
.git/objects中。 - 更新
.git/HEAD中的 HEAD 引用来指向新创建的提交的哈希值。 这将导致 Git 中新增一个项目快照,并与之前的提交状态连接起来。
30.9 移动 HEAD
当你运行 git checkout 命令指向一个提交(通过哈希值或引用指定)时,你告诉 Git 使你的工作目录看起来像快照被创建时的样子。
- 更新工作目录中的文件以匹配提交内的树。
- 更新 HEAD 以指向指定的哈希值或引用。
30.10 移动引用
运行 git reset --hard 会将引用移动到指定的哈希值或引用。 将 MyBranch 移动到 b8dc53:
$ git checkout MyBranch # 将 HEAD 移动到 MyBranch
$ git reset --hard b8dc53 # 使 MyBranch 指向 b8dc53
30.11 创建新引用
运行 git checkout -b <refname> 会创建一个新的引用,指向当前提交。
$ cat .git/HEAD
1f324a
$ git checkout -b TestBranch
$ cat .git/refs/heads/TestBranch
1f324a