GIT 后端(幕后)

2025-06-09

GIT 后端(幕后)

标题图片

介绍

我一直在深入研究 Git 的内部机制,力图真正理解这个工具的本质,并深入了解 Git 是如何实现其标志性的功能——版本控制的。在本文中,我打算用尽可能通俗易懂的语言,向大家分享我的发现,以便所有 Git 用户都能理解这个神奇工具背后的核心概念。

重要提示:本文并非 Git 入门,而是旨在向您展示 Git 如何跟踪项目文件的不同版本。如果您是 Git 新手,请参考Git 文档或下载progit获取 Git 基础知识的详尽讲解,之后您可以回来阅读本文,深入了解 Git。

GIT 前端(您已经知道的内容)

您现在肯定知道,Git 的核心是一个版本控制系统 (VCS),供人们使用(现在我没有说开发人员,任何人都可以使用 git,例如,我使用 git 来跟踪本文的各个版本,因为我在发布之前编写并进行了更改)来跟踪他们的项目文件(文档)的不同版本/迭代。

在 Git 出现之前,已经存在许多 VCS,例如 CVS、subversion、perforce,它们都归类于中央版本控制系统 (CVCS),这意味着所有版本控制文件都存储在一个数据库中,并且每个与该项目合作的人(客户)都必须连接到中央数据库(所有提交都进入这个单一数据库)。

另一方面,Git 属于分布式版本控制系统 (DVCS),这意味着它没有一个中央数据库供人们推送或保存文件。每台 Git 用户计算机都充当文件的数据库,这意味着每个参与项目的人都可以在本地计算机上保存项目的完整历史记录,就像他们克隆项目时一样。那么这个数据库在你的电脑里放在哪里呢?我敢肯定,如果你使用 Git 检查你的项目目录,你只会看到项目的子目录和文件。我会在……中公开这个 Git 魔法。

GIT 内部原理

Git 对象

项目目录之所以成为 Git 仓库,是因为其中隐藏着一些内容。.git.git directory文件夹包含 Git对象数据库,Git 的魔法就在这里发生。此文件夹特意隐藏,是为了避免一些触动人心的事件(文件突变、错误),并维护 Git 的完整性。如果您是 Windows 用户,可以通过启用show hidden files功能来查看此文件夹;如果您不知道如何操作,请按照本教程操作;如果您是 Mac 用户,请点击此处链接

.git 目录包含一些其他目录和文件,如下所示:

- .git/
  - hooks
  - info/
    - exclude
  - objects/
    - info
    - pack
  - refs/
    - heads
    - tags
  - config
  - description
  - HEAD

Git 能够有效地管理项目的不同版本,因为它旨在跟踪文件内容并将跟踪的内容作为对象存储在对象数据库中。

对象目录

Git 作为一个内容可寻址文件系统,这意味着 Git 使用对格式存储其文件(对象)key:value,其中键是 Git 存储内容(称为对象)的地址,即值。

Git 使用SHA-1(安全哈希算法)为每个存储的对象生成一个唯一的密钥。SHA-1 会生成一个长度为 40 个字符的字母数字字符,用于引用存储在对象数据库中的对象,例如f7de3a39b026386f8f826bc230a112ae792ec035

这个对象目录是 Git 存储每个对象的位置,即对象目录是对象数据库

Git 对象

Git 在对象数据库(对象目录)中存储了三种主要的对象类型,它们是:

  • Blob 对象- Git 将文件内容存储为 Blob。需要注意的是,Blob 不包含文件名或文件模式,而是严格意义上 Git 版本控制文件中的内容。SHA-1 值用作 Blob 内容(值)的文件名(键)。

  • 树对象- 树对象类似于目录,包含 blob 对象和其他树对象。树对象是上次提交时工作目录的快照。

  • 提交对象- 提交对象基本上存储当前检出的分支的最后一次提交的详细信息(这被称为父级)、提交的当前树对象的指针、提交者的名称以及实际的提交消息。

Git 对象类型组织在 Git 尝试存储对象时自动创建的子目录中。子目录使用 SHA-1 的前两个字符命名,对象存储在使用其余 38 个哈希键命名的文件中。

  Objects/
    f7/
      de3a39b026386f8f826bc230a112ae792ec035

GIT 实际应用

在本节中,我们将进行一些版本控制,并亲自了解 Git 对象是如何创建并存储在对象数据库中的,我相信在本节结束时一切都会变得更有意义。

我将使用git bash进行演示。

  • 打开终端/cmd,然后 Cd 到你选择的任意目录,输入以下命令$ git init创建一个空的 Git 仓库。就我而言,我收到了这条消息,Initialized empty Git repository in C:/Users/opara prosper/Desktop/GitHub Projects/Git-Backend/.git/你肯定会收到一条类似的消息,但目录不同。

  • 打开一个新终端并将 cd 放入项目目录并将两个终端并排放置,这将使我们能够跟踪 .git 目录内发生的情况。

打开终端

  • 创建一个新文件example.txt来包含Hello world其内容 - 您可以从终端输入以下命令来执行此操作,cat > example.txt然后在新行中输入内容,使用CTRL D保存并退出

  • 在打开的第二个终端上输入此命令,find .git/objects -type f您将不会得到任何响应,因为对象目录中没有任何内容,但这很快就会改变。

  • 现在在我们最初打开的第一个终端上,输入git add example.txt将文件移动到暂存区的命令。

  • 回到你打开的第二个终端并输入find .git/objects -type f。现在你会得到类似这样的响应,但.git/objects/70/c379b63ffa0795fdbfbc128e5a2818397b7ef8你的响应会有所不同,因为 SHA-1 会为 git 对象创建随机密钥。这意味着一个对象已添加到 Git 的对象数据库中,但我们还不知道它是什么。如果你输入 ,git cat-file -p 70c379b63ffa0795fdbfbc128e5a2818397b7ef8你会注意到返回了hello world,但找不到文件名 example.txt ,这进一步证实了我对 Git 是一个内容跟踪器的说法。

运行该git add example.txt命令时,Git 会抓取 eample.txt 中的内容,并将其压缩到 git 对象数据库中,并使用 SHA-1 算法赋予其唯一标识符70c379b63ffa0795fdbfbc128e5a2818397b7ef8。要检查对象的类型,请输入以下命令。在本例中,返回git cat-file -t 70c379b63ffa0795fdbfbc128e5a2818397b7ef8一个blob ,这意味着新创建的对象是一个 blob 对象。

  • 回到我们的第一个终端,键入以下命令git commit -m "I created an example.txt file"来提交您的更改(即将您的更改从暂存区移动到 .git 目录)。

  • 返回第二个终端并输入find .git/objects -type f,这次您会注意到,就我的情况而言,对象目录中添加了两个新对象;

.git/objects/70/c379b63ffa0795fdbfbc128e5a2818397b7ef8
.git/objects/7a/68edc879868601bf427c8c2238bbc8752c33b3
.git/objects/f2/9f2741b30ecc1529da7dbae4aff9974b69e271

让我们检查一下这些对象,抓取任意一个对象并输入git cat-file -t 7a68edc879868601bf427c8c2238bbc8752c33b3。在我的情况下commit,返回了 ,这意味着这个对象是一个提交对象。要查看这个对象,请将-t上一个命令中的标志更改为 ,-pgit cat-file -p 7a68edc879868601bf427c8c2238bbc8752c33b3您将收到如下响应:

tree f29f2741b30ecc1529da7dbae4aff9974b69e271
author OPARA-PROSPER <oparaprosper79@gmail.com> 1541583214 +0100
committer OPARA-PROSPER <oparaprosper79@gmail.com> 1541583214 +0100

I created an example.txt file

让我们仔细看看这意味着什么。第一行tree f29f2741b30ecc1529da7dbae4aff9974b69e271是一个指向上次提交时创建的第二个对象的指针(这个对象始终是一个树对象),第二行和第三行包含提交者的信息,最后一行包含提交消息。

  • 现在我们将使用 检查与提交一起创建的第二个对象git cat-file -t f29f2741b30ecc1529da7dbae4aff9974b69e271tree返回一个响应,这意味着该对象是一棵树,我们可以通过将-t标志更改为-pie来查看此树对象的内容git cat-file -p f29f2741b30ecc1529da7dbae4aff9974b69e271,这将返回如下所示的响应;
100644 blob 70c379b63ffa0795fdbfbc128e5a2818397b7ef8    example.txt

回想一下我之前解释过的内容,树对象就像包含 blob 和其他树的目录,它们也是你上次提交时工作目录当前状态的快照。上次提交时,工作目录中唯一的文件是example.txt文件,这就是我们的树对象所包含的内容,但这次,它实际上是在运行 git add 命令暂存更改时创建的 blob 对象。这是因为 Git 跟踪的不是文件,而是内容。

我们的树对象包含有关文件模式(100644 表示这是一个普通文件)、对象类型、SHA-1 密钥以及从中提取内容的文件名的信息。

包起来

您所做的更改、暂存和提交越多,对象数据库中创建和引用的新 blob、树和提交对象就越多。

总之,这实际上就是 Git 在底层的工作方式,以跟踪我们对项目文件所做的更改。

鏂囩珷鏉ユ簮锛�https://dev.to/kodekage/git-backend-behind-the-scene-12l5
PREV
初学者脚本
NEXT
如何从 GitHub 永久删除泄露的 .env 文件...