乐趣区

关于git:深入浅出git一数据模型

自 2005 年诞生以来,git 曾经在开源世界中大受欢迎,咱们中的许多人也在咱们的工作岗位上应用它。它是一个很棒的 VCS 工具,具备很多长处,但易于学习并不是其中之一。对于 git 如果只会死记硬背命令那么要不了多久你就会遗记, 而后一而再而三的背诵, 无疑让人很受打击, 在我看来,相熟应用 git 甚至开始喜爱它的惟一办法是理解它如何在外部工作。

git 命令只是对数据存储的形象, 如果不理解 git 的工作原理,无论咱们在笔记中记忆或存储了多少 git 命令或技巧咱们依然会对 git 的应用感到困惑. 而 git 则是通过形象的命令来裸露它的数据结构的应用办法.

所以这边文章咱们更多的要关注 git 的外部关系 - 数据模型, 当然这篇文章不会波及到 git 的源码.

筹备工作

初始化仓库

为了解说数据模型, 咱们首先要在本人的工作目录下初始化一个空的 git 仓库

git init

git 会告知咱们曾经在以后的目录下创立了一个 .git 目录, 咱们来看看这个 .git 长什么样子.

$ tree .git/
.git
|-- HEAD
|-- config
|-- description
|-- hooks
|   |-- applypatch-msg.sample
|   |-- commit-msg.sample
|   |-- fsmonitor-watchman.sample
|   |-- post-update.sample
|   |-- pre-applypatch.sample
|   |-- pre-commit.sample
|   |-- pre-push.sample
|   |-- pre-rebase.sample
|   |-- pre-receive.sample
|   |-- prepare-commit-msg.sample
|   |-- update.sample
|-- info
|   |-- exclude
|-- objects
|   |-- info
|   |-- pack
|-- refs
    |-- heads
    |-- tags

8 directories, 15 files

其中一些文件和目录是不是看着有些相熟, 当初咱们次要还是看 objects 这个目录, 当初它是空的, 然而一会儿咱们就会扭转它.

提交文件

首先咱们创立一个 Main.java 文件

touch Main.java

而后输出一部分内容

public class Main {public static void main(String[] args) {System.out.println("Hello World");
    }
}

而后以同样的形式在筹备一个 README.md 文件

touch README.md

向文件中输出以下内容

this is my first java project!

当初 add 并且 commit 他们到仓库

git add .
git commit -m 'Initial Commit'

模型的创立

当初看上去没啥非凡的, 当初咱们回过头来在看看 .git/objects 目录下曾经存在了一些子文件夹以及文件了

.git/objects
|-- 84
|   -- 705622ee44f2afbb21087ca7d81fda01fccded
|-- 95
|   -- fc1236534b6f73930367f02895467040f47d4a
|-- b0
|   -- 81e51f448387e72a3e3551ba8610eedc172e60
|-- f1
|   -- a8b89f50a2fd8287578daa2b0374adf3cad8aa
|-- info
|-- pack
6 directories, 4 files

须要留神的是在你的电脑上目录和文件名称和我这里是不一样的.

blob object 的创立

.git/objects 下咱们留神到每个目录的名称只有 2 个字符长度,Git 为每个对象生成一个 40 个字符的校验和(SHA-1)哈希,该校验和的前两个字符用作目录名,另外 38 个字符用作文件(对象)名。
当咱们提交一些文件时,git 创立的第一类对象是 blob object, 在咱们的例子中是两个, 每一个blob object 对应咱们提交的每一个文件:

blob object 蕴含文件的快照以及领有文件校验和.

tree object 的创立

git 创立的另外一种对象是tree object, 在咱们的例子中只有一个, 它蕴含咱们我的项目中所有文件的列表, 其中蕴含调配给它们的 blob object 的指针(这就是 git 如何将文件与 blob object 相关联)

commit object 的创立

最初 git 还创立了一个 commit object, 该对象具备指向它的 tree object 的指针(以及一些其余信息)


)

这个时候在来看以下 objects 目录下的构造就清晰多了

.git/objects
|-- 84
|   -- 705622ee44f2afbb21087ca7d81fda01fccded
|-- 95
|   -- fc1236534b6f73930367f02895467040f47d4a
|-- b0
|   -- 81e51f448387e72a3e3551ba8610eedc172e60
|-- f1
|   -- a8b89f50a2fd8287578daa2b0374adf3cad8aa
|-- info
|-- pack

验证模型的准确性

下面画出了模型图, 然而你认为我这个模型是本人猜的吗? 我又是如何确定哪个是 blob object? 哪个是 tree object? 哪个是 commit object 的呢? 接下来就是见证奇观的时刻了.

应用 git log 命令咱们能够查看咱们的提交历史

commit f1a8b89f50a2fd8287578daa2b0374adf3cad8aa (HEAD -> master)
Author: zhu.yang <zhu.yang@xxx.com>
Date:   Tue Jan 8 10:12:06 2019 +0800
    Initial Commit

依据咱们后面说的命名约定, 咱们能够在 objects 中发现 f1a8b89f50a2fd8287578daa2b0374adf3cad8aa 这个对象.
想要查看文件内容咱们不能简略的应用 cat 命令, 因为这些不是纯文本文件, 然而好在 git 给咱们提供了一个 cat-file 命令

git cat-file commit f1a8b89f50a2fd8287578daa2b0374adf3cad8aa

能够通过它获取到 commit object 中的内容

tree 95fc1236534b6f73930367f02895467040f47d4a
author zhu.yang <zhu.yang@xxx.com> 1546913526 +0800
committer zhu.yang <zhu.yang@xxx.com> 1546913526 +0800
Initial Commit

从下面能够看到 commit 指向 tree object 并且咱们能够应用 git ls-tree 命令来查看下其中的内容

git ls-tree 95fc1236534b6f73930367f02895467040f47d4a

正如咱们说意料的一样, 其中蕴含了指向 blob object 的文件列表

100644 blob 84705622ee44f2afbb21087ca7d81fda01fccded    Main.java
100644 blob b081e51f448387e72a3e3551ba8610eedc172e60    README.md

如果想要查看 Main.java 中的内容则应用 cat-file 命令即可

git cat-file blob 84705622ee44f2afbb21087ca7d81fda01fccded

咱们能够看到其中返回了 Main.java 文件的内容

public class Main {public static void main(String[] args) {System.out.println("Hello World");
        }
}

下面就是当咱们创立并提交了一些文件的时候就会产生的事件. 同时也验证了咱们模型的准确性.

批改文件时模型的扭转

当初咱们批改一下 main.java 而后从新提交一下

正如咱们看到的一样,git 以快照的形式为 Main.java 新建了一个 blob object, 因为 README.md 没有被批改, 因而不会为其创立新的 blob object. 而且git 会重用现有的 blob object.

当初,当 git 创立一个 tree object 时,调配给 Main.java 的 blob 指针会被更新,并且调配给 README.md 的 blob 指针将放弃与前一个提交树中的雷同。


)

在最初,git 创立一个 commit object 并指向它的 tree object. 同时还有一个指向它的父提交对象的指针(每个提交除了第一个提交至多还有一个父提交)

到当初为止咱们曾经晓得了 git 是如何解决文件的新增以及编辑的, 惟一还遗留的就是如何解决删除了, 咱们先删除 Main.java:

请留神上图中红色的连线, 咱们发现删除同样也是非常简单, 只须要删除 tree object 指向 blob object 的指针即可. 在这种状况下咱们在新的提交中删除了 Main.java, 因而咱们的提交的树对象不再具备指向示意 Main.java 的 blob object 的指针.

模型对文件夹的解决

咱们提供的这个数据模型还有一个附加性能 -tree object 是能够被嵌套的(它们能够指向其余树对象), 你能够这样想: 每个 blob object 代表一个文件, 每个树对象代表一个目录, 所以如果咱们有嵌套目录, 咱们就有嵌套的 tree object.

因为下面的图曾经是提交屡次后果画进去的了, 再在下面的根底上画构造就不是那么清晰了, 这次我从新初始化一个仓库来演示, 当初该仓库下存在存在的数据如下:

|-- README.md
`-- app
    `-- user.json

而后提交, 最初能够看到如下的数据模型

Git 应用 blob object 以及 tree object 来重现我的项目的文件夹构造. 到这里我置信你必定对 git 的数据模型有了较为深刻的理解, 它真的是很简略, 我置信基于它再去学习 Git 肯定会是事倍功半.

总结

  1. 创立一个提交的时候 git 会新增 blob object,tree object,commit object 并会造成链路图
  2. 嵌套的 tree object 用来示意文件夹
  3. git 从复用 blob object
  4. 除了第一个提交之外, 每一个提交都有一个父提交
退出移动版