15分钟了解Git对象和引用(2)

简介: 15分钟了解Git对象和引用

普通Tree对象

Root Tree是一种逻辑含义上特殊的 tree 对象, 因为它是由 Git 自动生成的对某个目录的映射或索引. 但是在物理含义上, 它与普通的tree对象并没有本质差别, 我们尝试通过新增目录的方式, 来进一步了解 tree 对象:

 mkdir src docs

 echo "abc" > src/main.c

 echo "efg" > docs/project.txt

 git add src docs

 git commit -s

[master 87902f9] COMMIT B

2 files changed, 2 insertions(+)

create mode 100644 src/main.c

create mode 100644 src/project.txt

 git cat-file -p master

tree 2e6cdc032db0ecfa4e3e898b8f8551acce10db11

parent 3de31e208ae26c6b44d9e6c4a3f0adb32d0c68b6

author Dyrone Teng <tenglong.tl@alibaba-inc.com> 1633681481 +0800

committer Dyrone Teng <tenglong.tl@alibaba-inc.com> 1633681627 +0800

 

COMMIT B

 

Signed-off-by: Dyrone Teng <tenglong.tl@alibaba-inc.com>

 git cat-file -p 2e6cdc032db0ecfa4e3e898b8f8551acce10db11

100644 blob 8178c76d627cade75005b40711b92f4177bc6cfc    README.md

040000 tree 99b20b290a90e1137e0487f955620db05b229abe    docs

040000 tree 8db636fa1dbc4d704179895bdb6e4322f32cbda6    src

 git cat-file -p 99b20b290a90e1137e0487f955620db05b229abe

100644 blob 1505b408c2ef47655f603b9045464e642ab28d97    project.txt

 git cat-file -p 8db636fa1dbc4d704179895bdb6e4322f32cbda6

100644 blob 8baef1b4abc478178b004d62031cf7fe6db6f903    main.c

我们在工作空间中, 新创建了两个文本文件为 main.c 和 project.txt 并分别存放在新的目录 src 和 docs 下, 随后对当前工作空间的更改进行提交。

通过 git cat-file -p master 来查看 master 分支 (Git自动为我们生成的默认分支名) 上最近提交的root-tree 对象, 随后进一步通过该子命令进行递归查看. 我们可以从输出结果分析出以下绩点(仅分析 tree 和 blob 对象):

· 创建 blob 对象 1505b408c2ef47655f603b9045464e642ab28d97 用于存放 project.txt 文件的文本内容;

· 创建 blob 对象 8baef1b4abc478178b004d62031cf7fe6db6f903 用于存放 main.c 文件的文本内容;

· 创建 tree 对象 8db636fa1dbc4d704179895bdb6e4322f32cbda6 用于存放 src 目录索引信息, 其中包含了 main.c 文件的名称、类型和模式等信息;

· 创建 tree 对象 99b20b290a90e1137e0487f955620db05b229abe 用于存放 docs 目录索引信息, 其中包含了 project.txt 文件的名称、类型和模式等信息;

· 创建 tree 对象 (root-tree) 2e6cdc032db0ecfa4e3e898b8f8551acce10db11 用于存放当前仓库目录的索引信息, 其中包含了 src 以及 docs 目录以及 README.md 文件的名称、类型和模式等信息;

大家是否注意到, 当我们使用 cat-file 查看一个tree对象时, 其中不仅包含了文件名、OID和对象类型, 还输出了类似 100644 以及 040000 等信息, 这些信息则代表着文件的 类型 和 模式 .。

文件的类型(type)与模式(mode)

文件类型 (https://en.wikipedia.org/wiki/Unix_file_types) 和 模式 (https://en.wikipedia.org/wiki/File-system_permissions), 是源于Unix文件系统中的设计, 而在Git中则进行了选择性的沿用, 可以认为是一个Unix文件结构设计的迷你版。

Git采用32位长存储文件模式,从高位到低位依次:

· 4位: 保存对象类型, 二进制的有效值为 1000(常规文件)、1010(符号链接) 和 1110 (gitlinks)

· 3位: 保留

· 9位: unix 权限格式, 只有 0755 和 0644 对常规文件有效, 符号链接和 gitlinks 在该字段中的值为0.

目前的模式有以下几种:

· 0100000000000000 (040000): 目录;

· 1000000110100100 (100644): 普通文件(非可执行);

· 1000000110110100 (100664): 普通文件(非可执行、group可写);

· 1000000111101101 (100755): 普通文件(可执行);

· 1010000000000000 (120000): 符号链接(Symbolic link);

· 1110000000000000 (160000): Gitlink(用于Git Submodule);

Commit

通过上面的介绍, 我们已经大体了解了blob和tree的用途, 其中treee区别了不同状态下仓库内容的快照, 但光有这些还是不够的, 因为这时候还缺少如下的信息:

· 无法追溯 : 仅有快照, 相当于有了 What . 但快照和快照之间的联系没有建立起来, 如前面介绍它们只是松散存储的Tree对象, 没有形成历史结构存储下来。

· Who : 不知道快照的参与者是谁。

· Why/How : 不知道当初创建快照的原因。

· When : 不知道什么时间创建了快照。

提交 (commit) 对象用来解决这些问题

· 关联快照 : commit引用了一个 _root-tree_对象, 即仓库顶层目录的快照, 从而将提交对象和树对象关联。

· Who : commit对象中记录了作者、提交人以及其他参与者的信息, 例如可以通过 git commit 子命令的 -signoff 选项, 在提交末尾添加 Signed-off-by (授权提交) 信息. 诸如此类的还有:

o Reported-and-tested-by: reported and tested the issue (I assume);

o Acked-by: acknowledged by the owner of the code, “looks good to me” ;

o Reviewed-by: reviewed;

o Cc: was notified about the patch;

· When : 提交中通过时间戳记录了创建的时间以及提交的时间. 这是因为, Git的作者和提交者可以是不同的人, 对应的创作时间和提交时间自然就可能不同, 我们可以执行 git log --format=fuller 查看包含了上述全部信息的提交历史列表。

· Why/How : 提交中还保存了提交的描述信息, 其中可以记录本次提交的原因、背景和实现策略等. 一般包含三部分 (关于如何写好你的提交信息, 在后续的文章中我的同事赵鹏飞会为大家详细介绍):

o 一行标题;

o 一行空行;

o 可以包含多个段落的具体描述;

· 可追溯 : Git commit中可以存储包含指向父提交的引用(parent), 通过这种方式, 新的 commit 和旧的 commits 之间就构成了一个有向无环图 (DAG), 我们可以从图中搜索 commit 并递归追溯其所有的祖先提交 (ancestors)。 通常, 一个提交可以有0个或n个父提交, 情况分别为:

o 0个 : 当提交为根提交(root-commit) 或者 使用 git commit-tree 子命令(不加 -p 选项)直接创建的commit对象。

o 1个 : 非根提交, 或非合并提交的情况下

o 2个 : 合并提交(merged-commit)

· 一个较好的参考示例:

o https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=19be0eaffa3ac7d8eb6784ad9bdbc7d67ed8e619

为了很好的理解上面的几点, 我们可以在刚才创建的 test.git 中执行:

 git log --parents --pretty=fuller

 

commit 6ae8bbeeb4dba091d3c6295d0b3d0f9a3863d32f 3de31e208ae26c6b44d9e6c4a3f0adb32d0c68b6 (HEAD -> master)

Author:     Dyrone Teng <tenglong.tl@alibaba-inc.com>

AuthorDate: Fri Oct 8 16:24:41 2021 +0800

Commit:     Dyrone Teng <tenglong.tl@alibaba-inc.com>

CommitDate: Fri Oct 8 16:27:07 2021 +0800

 

   COMMIT B

 

   Signed-off-by: Dyrone Teng <tenglong.tl@alibaba-inc.com>

 

commit 3de31e208ae26c6b44d9e6c4a3f0adb32d0c68b6

Author:     Dyrone Teng <tenglong.tl@alibaba-inc.com>

AuthorDate: Fri Oct 8 15:45:01 2021 +0800

Commit:     Dyrone Teng <tenglong.tl@alibaba-inc.com>

CommitDate: Fri Oct 8 15:45:01 2021 +0800

 

   COMMIT A

 

   Signed-off-by: Dyrone Teng <tenglong.tl@alibaba-inc.com>

我们通过添加 --parents 选项, 让 git log 子命令输出 parents commit的相关信息, 同时还添加了 --pretty=fuller 选项, 让输出结果中同时展示作者, 提交者, 创作时间和提交时间。

小结

Git对于每一次的文件内容变化, 都会单独保存为一个zlib压缩过的blob对象(文件), 进一步通过commit和tree将文件和目录组织起来形成快照, 并将提交和快照关联起来, 建立提交之间的DAG。

这种设计, 让版本管理变得简单可靠, 但同时, 也不可避免的会造成 Git 仓库存储的 快速膨胀 。 Git可以通过将松散的对象打包为 pack 文件, 来解决这个问题。 除此以外, pack中还支持保存delta的方式(delta compression), 保存blobs之间的 delta 差异 (2进制则会失效)。

Tag

标签 (tags) 的概念并非Git独创, 通常用来记录被认为很重要的提交, 例如软件的发行版本。

轻量级标签 (Lightweight Tags)

我们可以创建一个不带有任何额外信息的tag, Git称这种类型的标签为 轻量级tag (lightweight)。 "签如其名", 轻量级tag看上去与一个 commit 并无太多差别, 事实也确实是这样, 因为其不会保存额外的信息, 只是一个commit的指针, 我们可以在 _test.git 中创建一个名为 v1.0 的轻量级tag:

 git tag v1.0

 git cat-file -t v1.0

commit

可以看到, 标签名也支持使用 git cat-file 子命令, 我们可以看到其对象类型为 commit。

附注标签 (Annotated Tags)

另外一类标签叫做 附注标签 (Annotated Tags) , 附注标签可以记录额外的信息。 例如: 如果一个 tag 被用来标记发行版本, 就可以通过附注的信息记录本次发布了什么内容。 此外, 附注标签还可以记录标签的 作者 (tagger) 、邮箱 (tagger email) 以及 时间戳 , 这些都可以理解为标签说被附注的 元数据(meta-data) 。 最后, 附注标签还支持使用默认配置的邮箱信息, 用于生成 GPG 签名信息并附注在tag中。

附注标签与轻量级标签不同的是, 附注标签会以tag对象类型进行保存:

 git tag -a v2.0 -m "create v2.0"

 git cat-file -t v2.0

tag

 git cat-file tag v2.0

object 6ae8bbeeb4dba091d3c6295d0b3d0f9a3863d32f

type commit

tag v2.0

tagger Dyrone Teng <tenglong.tl@alibaba-inc.com> 1633919318 +0800

 

create v2.0

如上所示, 我们创建了一个名为 v2.0 的附注标签, 其描述信息为 create v2.0 。我们可以使用 cat-file 子命令查看该标签会发现其对象类型为 tag 。 进一步, 如果我们希望查看tag的附注信息, 我们可以执行 git cat-file tag <tag_name> 子命令完成。


目录
相关文章
|
6月前
|
存储 Java 开发工具
聊聊Git中的引用
这个 SHA-1 值就指向 Git 仓库中的某个提交对象 ID。既然能用 SHA-1 值来区分不同的提交对象,为何要创建一个存放 SHA-1 值的文件呢?因为 SHA-1 值过于长,而且没有规律,如果利用简单的文件名来引用对原来的提交对象,就不用记住复杂且无规律的 SHA-1 值了。 因此在 Git 中,**像这种只含有 SHA-1 值的文件,就是 Git 的引用(Reference)。而分支,就是一个指向某一系列提交之首的指针或引用**。
79 0
聊聊Git中的引用
|
存储 算法 Unix
15分钟了解Git对象和引用(1)
15分钟了解Git对象和引用
114 0
|
6月前
|
存储 算法 开发工具
深入剖析Git对象底层原理
Git 在中间做了什么,它如何存储不同的文件和内容,以及如何区分不同分支下的文件版本呢?日常操作对这些自动的操作都是无感的。 但是如果哪天一旦上述操作中出现了错误,需要找回自己的代码时,如果不懂 Git 其内部存储原理,是没法找回的,因此为了避免这种情况,就有必要去了解其内部的存储——Git 对象的原理。
37 0
深入剖析Git对象底层原理
|
存储 运维 安全
15分钟了解Git对象和引用(3)
15分钟了解Git对象和引用
92 0
|
开发工具 git 索引
commit时Git都干了些啥?--- 提交对象
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点 提交对象 一般我们平时有了需要提交的文件,都是2步走:add,然后commit add操作 第一步:添加文件 //添加文件到暂存区 git add test.
1331 0
|
开发工具 数据库 git
Git是如何保存文件名和目录关系的---树对象
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点 树对象(tree)—— 保存文件名和目录关系 树对象主要解决2个问题,:文件名的保存和文件目录关系的保存 就像下面这样 下面我们就来模拟一下构建上面这颗树,也就是模拟保存这3个文件,其中的"bak"是一个目录,下面有一个文件 首先可以看到,我们一共需要保存的是3个文件,new.txt 、 内容为version 2的 test.txt 和内容为version 1的 test.txt。
800 0
|
数据库
Git.Framework 框架随手记--ORM查询返回实体对象
  使用ORM有一个优势,可以通过某种机制将数据库中的数据转化为自己想要的对象形式数据。本章记录一下如何使用Git.Framework返回实体对象     一. Git.Framework 中提供的方法     在Git.
1022 0
|
开发工具 Android开发 git
mac AndroidStudio git 引用失效
mac AndroidStudio git 引用失效
2768 0
|
7天前
|
缓存 Java Shell
[Git]入门及其常用命令
本文介绍了 Git 的基本概念和常用命令,包括配置、分支管理、日志查看、版本回退等。特别讲解了如何部分拉取代码、暂存代码、删除日志等特殊需求的操作。通过实例和图解,帮助读者更好地理解和使用 Git。文章强调了 Git 的细节和注意事项,适合初学者和有一定基础的开发者参考。
22 1
[Git]入门及其常用命令