Git 内部原理.md
Git 内部原理
了解 Git object,如blob、tee、 commit等?
知道 git reset –soft、git reset、 git reset –hard 的区别?
听说过 git reflog ?
实验1
- linux系统中,使用
mkdir git-test
创建文件夹。 - cd git-test
- tree .git
1 | lautung@PC:~/git-test$ tree .git |
注意.git
目录下的objects
目录。
使用 echo 命令,输入添加文件
1
2lautung@PC:~/git-test$ echo 111111 > a.txt
lautung@PC:~/git-test$ echo 222222 > b.txt此时目录下存在文件
a.txt
和b.txt
。git add a.txt b.txt 添加到暂存区。
使用
tree .git
命令打印目录结构,如下:
1 | lautung@PC:~/git-test$ tree .git/ |
我们发现objects目录下,多了两个文件目录87和90,87目录下有一个文件6c799506a9f7f24febd177d22352477f10fab0,90目录下有一个文件d2950097fa1850b6f692ab1095ec9cdd3f7fae。
- 尝试使用
cat .git/objects/87/6c799506a9f7f24febd177d22352477f10fab0
打印其中一个文件。我们只是一段乱码,因为git对存储的内容进行了二进制的压缩。 cat
命令行不通。不过无须担心,git为我们提供了合适的命令,使用git cat-file [-t] [-p]
。使用命令,如下:注意文件名是1
2
3
4
5
6lautung@PC:~/git-test/.git/objects/87$ git cat-file -t 876c799506a9f7f24febd177d22352477f10fab0
blob
lautung@PC:~/git-test/.git/objects/87$ git cat-file -t 876c
blob
lautung@PC:~/git-test/.git/objects/87$ git cat-file -p 876c
222222文件目录名+文件名
或文件目录名+文件名简写
的形式,上述代码876c799506a9f7f24febd177d22352477f10fab0
和876c
指的是同一个文件。
除此之外,我们查看打印结果,blob和222222,blob我们姑且当做不知道,但是222222,细心的同学一定记得我们在步骤4中的文件b.txt的内容吧。
好,这个小实验到此为止,到底是为了证明什么呢?请往下看。
Git Object
什么是 Git object ?git object 我们可以理解为git存储信息的最小单元。我们上面小实验最后的blob其实就是 git object的类型,它表示文件存储具体内容,比如我们打印的222222。
所以,我们在小实验最开始,添加了两个文件,git就创建了两个object,类型都是blob,内容分别是111111和222222。
实验2
我们已经前面已经知道了 Git Object 和 blob类型,下面我们开始实验2:
- 在实验一的基础上,我们用
git commit -m "[+] init"
提交暂存区内容。 - 使用tree .git 观察文件目录,如下:
1 | lautung@PC:~/git-test$ tree .git |
我们能够发现,多了两个目录(48和fd)极其对应的文件。
- 使用
git cat-file [-t] [-p]
分别查看两个文件。
1 | lautung@PC:~/git-test/.git/objects/48$ git cat-file -t fd7e |
文件类型:tree,值有两个行,每行从左到右分别是:权限、文件类型、SHA1值、文件名。这个说起来像是快照,通过tree类型的文件,可以找到相对应的文件。
1 | lautung@PC:~/git-test/.git/objects/48$ git cat-file -t 48e2 |
文件类型:commit,文件内容:commit文件对应的快照指针(sha1值),作者信息,空行,提交的描述信息。
通过实验二,想必已经对commit、tree、blob各自的用途了解了。那么branch(分支)和tag存在哪里呢?
分支和Tag存在哪里?
git 其实把他两以明文的形式存储,以下:
1 | lautung@PC:~/git-test$ cat .git/HEAD |
48e24744cfdd74856b23de73122c702ad9b58b3c是实验二的那个commit类型文件。所以,HEAD、分支、普通的Tag可以简单的理解成是个指针,指向对应 commit的SHA1值。我们修改实验二的图,可得以下图形:
Q1:为什么要把文件的权限和文件名储存在
Tree object里面而不是 Blob object呢?
每次提交,如果有一个文件没有变更,Tree直接复用即可,能节省很多麻烦和内存。
Git的三个分区及变更历史的形成
在工作区,添加文件及其内容,只有工作区会发生变化,其它两区是没有变化的,如图:
第二步,我们执行git add .
的时候,首先,git将修改的或新的文件在git仓库创建一个blob文件,其次,将暂存区的索引指向那个新的blob文件。
第三部,我们执行git commit -m "commit massenge"
的时候,有如下三步:
- 新建一个tree object文件,记录包含的文件索引。
- 新建一个commit object文件,指向步骤1中的tree object文件。
- 将master指针(当前分支指针),指向最新的commit object。
Q2:每次 commit,Git储存的是全新的文件快照还是储存文件的变更部分?
全新的文件快照。虽然会比较占用空间,但是这是一个空间换时间的策略,时间复杂度O(1),如何存储变更,就需要大量的算法,在网络传输也不好用。
Q2:git 如何保证历史记录不被篡改?
Git和区块链的数据结构非常相似,两者都基于哈希树和分布式。,一旦修改所有都会修改。