git - Git如何存储分支?

标签 git

假设我有两个分支:master 和 dev。第一个包含名为 1.txt 的文件有内容

Hello, world
第二个包含文件 1.txt有内容
Goodbye, world!!
git 将在何处以及如何存储文件的不同副本 1.txt ?我的意思是,在 .git 的确切位置文件夹?

最佳答案

Git 并不完全存储文件。 Git 存储的是对象。

分支也不包含文件。分支名称,如 masterdev ,存储提交哈希 ID。

理解这一点的关键是有点循环:只有当你理解它时,你才真正理解它。 :-) 但是首先,将 Git 视为存储提交对象并以提交的概念为中心。

提交是这些 Git 对象之一。有四种对象:提交、树、blob 和标签。树和 blob 用于构建提交。标签对象用于带注释的标签,但不要担心这些。

所以 Git 是关于存储提交的,而提交最终会为你保存你的文件(通过那些树和 blob 对象)。但是提交不是文件本身:它更像是一个包装器。提交的内容是:您的姓名(作为作者)、您的电子邮件地址以及您提交的时间;提交的父提交的哈希 ID;您的提交日志消息;以及记住哪些文件进入提交的树对象的哈希 ID。

所以你可能认为树对象保存你的文件——但它也不是!相反,树对象保存文件的名称,以及 blob 对象的哈希 ID。正是这些 blob 对象保存了您的文件。

所有 Git 对象都由一个丑陋的大哈希 ID 命名

提交的名称或任何其他 Git 对象的名称写为 40 个字符的哈希 ID,如 d35688db19c9ea97e9e2ce751dc7b47aee21636b 。您可能已经看到它们,例如在 git log 输出中,或者在您运行其他 Git 命令时显示的缩短版本。

人类不可能以任何实际方式使用这些哈希 ID,因此 Git 提供了一种将简短而有意义的名称转换为大而丑陋的哈希 ID 的方法。这些名称有多种形式,但您使用的第一个是分支名称。

这意味着如果您有两个分支名称 masterdev ,它们实际上存储了哈希 ID。

Git 使用哈希 ID 来查找提交对象。每个提交对象然后存储一个树 ID。 Git 使用它来查找树对象。树对象包含(以及其他内容)一个名称,例如与 blob 哈希 ID 配对的 1.txt。 Git 使用 blob 哈希 ID 查找 blob 对象,blob 对象存储文件的完整内容。

所以这就是 Git 存储文件的方式

Where and how git will store different copies of file one? I mean, where exactly in .git folder?



当您运行 git add 1.txt 然后提交它时,Git 会生成一个 blob 来保存 1.txt 中的任何内容。新的 blob 有一些哈希 ID。假设它以 1234567... 开头。 Git 将实际内容以压缩形式存储在 .git/objects/12/34567... 中,以及一些将对象类型标识为 blob 的前期位。

如果您随后再次更改 1.txtgit addgit commit,您将获得一个带有新 ID 的新 blob。假设它以 fedcba9... 开头。该对象进入 .git/objects/fe/dcba9...

当然,为了存储这些 blob,Git 也必须编写树对象和提交对象。如果您在 dev 分支上,当 Git 写出新提交时,Git 将更改名称 dev 以存储新的提交哈希 ID。

提交形成一个链

为了找到所有这一切之前在 dev 上的提交,Git 使用先前的 dev 提示提交 ID 作为其父级写入新提交。

假设我们给每个提交一个字母,而不是大的丑陋的哈希 ID,从 A 开始并计数。这更容易绘制,当然我们在仅仅 26 次提交后就会用完字母。 :-)

让我们从一个只有一次提交的存储库开始:
A   <-- master

分支名称 master 存储 A 以便我们知道提交名为 A

这不是很有趣,所以让我们做一个新的提交 B :
A <-B   <-- master

现在名称 master 存储字母 B 。提交本身,即 B 对象,在其中包含提交 A 的 ID。

为了在 master 上进行另一个新提交,我们为它分配一个新的散列 C ,使用适当的日志消息和树等编写提交对象,并使 C 的父项成为 B :
A <-B <-C

然后我们将 C 写入 master :
A <-B <-C   <-- master

这意味着分支名称,如 master ,只是指向分支的提示提交。从某种意义上说,分支本身就是从最新开始向后工作的提交链。

请注意,Git 的内部箭头都指向后。 Git 始终从最新的开始向后运行所有内容。

我们可以通过创建一个新分支 dev 来使这更有趣。最初, dev 指向与 master 相同的提交:
A--B--C   <-- dev (HEAD), master

我们添加了这个有趣的符号 (HEAD) 来记住我们使用的分支名称。

现在让我们像往常一样进行新的提交。新提交一如既往地获取其作者和日志消息,并将当前提交的哈希 ID 存储为 C 作为其父级,但现在我们必须更新分支名称以指向 D 。我们应该更新哪个分支名称?这就是 HEAD 的用武之地:它告诉我们要更新哪一个!
A--B--C   <-- master
       \
        D   <-- dev (HEAD)

所以现在 dev 标识提交 D ,而 master 仍然标识 C

这就是 Twig 生长的方式

这是理解 Git 的第一个主要 secret 。 Git 不存储文件,它存储提交。提交形成链。这些链是 Git 存储库中的历史记录。

Git 使用分支名称来记住最新或提示提交。这些提示提交让我们可以找到较旧的提交。如果我们向 E 添加一个新的提交 master 我们得到:
A--B--C--E   <-- master
       \
        D   <-- dev

我们现在可以直观地看到 masterdev 在提交 C 时连接起来。

运行 git checkout <branch-name> 告诉 Git 提取分支尖端的提交,使用提交查找树以查找 blob 以获取所有文件。然后,作为分支名称的 git checkout 的最后一步,Git 将 HEAD 附加到该分支名称,以便在我们添加新提交时知道要更新哪个分支名称。

关于git - Git如何存储分支?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46892079/

相关文章:

Gitflow 安装问题

ruby - 无权访问 Heroku 应用程序

git diff 查看 merge 会引入什么

Git - 如何在不丢失这些更改的情况下压缩对忽略文件的更改?

node.js - 如何将git hash添加到Vue.js组件

linux - git clone 使用 vcsrepo 模块 puppet 抛出错误

git - Gerrit 与 Github

git - 如何在 git 上使用 CRLF 提交文件?

git - git 在哪里存储它的 reflog?

windows - 在所有子目录中调用 git init 的 Powershell 脚本