自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 |-- tags8 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|-- pack6 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 95fc1236534b6f73930367f02895467040f47d4aauthor zhu.yang <zhu.yang@xxx.com> 1546913526 +0800committer zhu.yang <zhu.yang@xxx.com> 1546913526 +0800Initial Commit
从下面能够看到commit指向tree object并且咱们能够应用git ls-tree
命令来查看下其中的内容
git ls-tree 95fc1236534b6f73930367f02895467040f47d4a
正如咱们说意料的一样,其中蕴含了指向blob object的文件列表
100644 blob 84705622ee44f2afbb21087ca7d81fda01fccded Main.java100644 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肯定会是事倍功半.
总结
- 创立一个提交的时候git会新增blob object,tree object,commit object并会造成链路图
- 嵌套的tree object用来示意文件夹
- git从复用blob object
- 除了第一个提交之外,每一个提交都有一个父提交