你知道 Git 是如何作版本控制的吗?

做者:冯伟尧git

咱们在切换不一样分支,或者切换到指定历史提交记录时,Git 是怎么把文件的历史版本调出来的?也就是说:github

  1. Git 把数据存储在哪里?
  2. 怎么存储文件的不一样版本?
  3. 又是怎么让不一样版本和指定提交记录关联起来的?

在开始前,为了不部分歧义,先统一下对提交记录的认识:咱们都知道每一个提交记录都会有惟一对应的一个 40 位字符串,这是 Git 根据咱们的提交内容利用 SHA-1 算法计算出来的哈希值。这个 40 位的字符串,有人称 id 或 哈希值 或 校验和 或 SHA-1 值。由于 SHA-1 是一种哈希算法,这里咱们统一下,这个字符串就叫哈希值算法

Git 如何存储数据

Git 如何存储数据,答案主要就在 .git 文件夹的 objects 目录里。先不解释 objects 目录是什么,咱们先看看里面都有什么东西。如今你能够随意打开一个 Git 仓库,找到其中 .git 目录(默认状况下它多是隐藏的)下的 objects 文件夹。他应该是下面这样的:markdown

objects-dir

这里面是不少以 2 个字符命名的文件夹,文件夹里又是若干数量以一串 38 个字符命名的文件。若是你足够敏感的话,或许能猜到这是一个相似指定 commit 提交记录的哈希值。借助 git cat-file 命令能够查看这个文件里的内容。结果以下所示:oop

blob-example

这里的结果是我项目中 CHANGELOG.md 文件的内容。更确切的说,它是 CHANGELOG.md 文件在某一次提交时的完整内容。而这个 objects 文件夹,就是 Git 存放数据的地方。spa

Git 在每次提交时,会找到存在变动的文件,利用 SHA-1 算法根据内容计算出 40 位的字符串为文件命名,放在 objects 文件夹。 哈希值的前两个字符用于命名子目录,余下的 38 个字符则用做文件名。 也就是说,你的文件每次发生变动,都会有一个对应的快照,保存了该版本文件的全部内容。若是 Git 想恢复文件到某一次历史版本,只须要拿到这个文件该次版本对应的哈希值便可。指针

但是 Git 怎么知道某次提交时该文件对应版本的哈希值是什么呢?也就是说 Git 如何把文件不一样版本和提交记录关联起来的?code

实际上在 objects 文件夹里主要存放着三类信息,除了咱们上面提到的文件内容信息,还有文件路径信息以及提交信息。它们分别以 文件对象 blob object、树对象 tree object、提交对象 commit object 的形式存在。每个对象都是 objects 目录下的一个文件,和保存文件内容信息的方式相似,Git 会经过 SHA-1 算法根据对象内容计算哈希值,获得一串 40 位的字符串为文件命名,用于找到他们。 接下来咱们来看树对象和提交对象都包含了哪些内容。orm

当你提交时,Git 会把你当前的目录结构保存下来,对应的 tree object 结构以下:对象

tre-example

包含了文件的文件名和文件对象的哈希值,以及子文件夹名字和对应的树对象哈希值。也就是说,你只须要找到某次提交根目录的树对象哈希值,就能找到该次提交全部文件对应的文件名以及文件对象哈希值。 也就能获得文件的历史版本内容了。

那我如何找到这个树对象?

就是经过咱们最熟悉的提交对象。git log 命令能够找到对应提交的哈希值,咱们能够看到提交对象的结构以下:

commit-example

tree 指向的就是当前提交的树对象。parent 是上一次提交的提交对象。其余的是做者,提交人,时间和描述信息。

如今我就能够知道某次提交的某个文件的历史版本内容是什么了。由此咱们也大概了解了 Git 是怎么存储文件的历史版本,又是怎么把历史版本和提交记录联系起来的。

git-objects

当使用 git commit 进行提交操做时,Git 会先计算每个子目录的哈希值,而后在 Git 仓库中将这些校验和保存为树对象。 随后,Git 便会建立一个提交对象,它除了包含做者,时间和描述信息外,还包含指向这个树对象(项目根目录)的哈希值以及上一次提交的哈希值。如此一来,Git 就能够在须要的时候重现这次保存的快照。

git 的分支

如今咱们知道,只要拿到某次提交对象的哈希值,咱们就能获得该次提交对应的快照。若是咱们想要某个分支的文件内容,又是如何作到的?

分支只是一个可变的指针,指向分支最新的提交对象。

在 .git 里的 refs 文件夹中,保存了全部分支对应的最新提交对象的哈希值。目录结构以下:

refs

其中 heads 文件夹保存的是本地分支,remotes 文件夹保存的是远程分支。以本地分支 master 为例,利用 cat master 能够看到,该文件中保存了最新提交对象的哈希值。若是你如今进行一次新的提交,会发现该文件中的内容会变为新提交对象的哈希值。

refs

同时 Git 中有一个特殊的引用 HEAD,它老是指向当前检出的分支或提交对象。HEAD 指向的提交对象保存在 .git 里的 HEAD 文件中。 以下图,HEAD 指向了 master 分支。若是咱们手动检出 master 分支的上一次提交,会发现该文件指向了一个肯定的提交对象,即上一次提交的提交对象。

git-refs

最后,能够用一张图来归纳分支和提交对象之间的关系。

git-refs
相关文章
相关标签/搜索