揭开git的面纱--git 原理

一、Git对象

Git对象一共有4种:blob数据对象、tree树对象、commit提交对象、tag标签对象;

  1. blob对象会把二进制文件进行压缩存储,并输出一个(唯一的)40位hash值作为key
  2. git会把压缩内容存储在 objects 文件夹下,并以对应的key值40位的前两位hash值作为文件夹名,后38位作为文件名;
  3. git是全量快照存储,而非增量存储;
  4. blob对象的hash值与文件名无关; 故blob对象无法存储文件信息;
  5. tree对象可以解决文件名保存的问题, 并且使得项目的多个文件也组织到一起;
  6. tree对象虽然可以表示我们想要跟踪的快照,但是无法记录快照的信息,操作者信息;
  7. commit对象还可以指明它的父提交对象;从而形成一系列的log记录; 我们执行git log查看的就是一条条互相链接的提交对象。

二、Git 储存原理&&实操

1.创建新项目git-demo;cd git-demo && git init
mkdir git-demo
cd git-demo
git init // 初始化git仓库,项目中多出.git文件夹
2.在项目中add的2个文件
echo '111' > a.txt
echo '222' > b.txt
git add .

image.png

使用git cat-file来查看git object的信息

git cat-file -t 查看版本库对象类型(blob、tree、commit、tag)
git cat-file -p 查看版本库对象详细内容
git cat-file -s 查看版本库对象大小

前面git对象中第二点,我们有介绍到"git会把压缩内容存储在 objects 文件夹下,并以对应的key值40位的前两位hash值作为文件夹名,后38位作为文件名"。因此,我们这里要查看git object信息,需要使用hash值:即所在的文件夹名+文件名。

Git cat-file [-t] [-s] [-p] 58c9 
Git cat-file [-t] [-s] [-p] c200 
// 一般使用hash的前6位就可以定位到对象,我们这里文件少使用4位也可以的。

image.png
查看.git/objects中新增的2个对象信息,类型:blob对象、size: 4byte(算上了换行符)、内容: 文件具体内容。不难发现Blob是object的最小单位,储存文件的具体内容。

3.git commit暂存区的2个文件
git commit -m 'first commit'

image.png

同样通过git cat-file 查看新增2个对象信息

image.png
每次commit都会多出一个commit与一个tree对象。

commit对象详情内容中,可查看对应的tree对象、作者、提交信息以及上一次提交的commit(我们这里是第一次提交就没有显示了)。

tree对象的详情内容中,可查看到这次快照的目录结构、文件权限、文件名(即上一步执行git add,新增的 储存blob对象的文件信息)。

object之间的关联图(一)
4.修改a.txt文件并执行git add && git commit
echo '333' > a.txt  
git add a.txt
git commit –m "2nd commit"

会发现此时又多出3个对象,分别是blob、commit、tree对象。
blob对象,是修改a.txt文件新增的blob对象,修改前的a.txtd的blob对象无变动。
tree对象,关联修改后的a.txt对应blob对象以及未修改的b.txt对应的blob对象。证明git是全量快照存储,而非增量存储,每次快照都记录着,项目中当前git所追溯的所有文件。
commit对象,可查看对应的tree对象、作者、提交信息以及上一次提交的commit。

object之间的关联图(二)

image.png

5.打个标签git tag
git tag -a "V1.0.0" -m "V1.0.0"
// git tag –a –m 会生成一个tag objects,但直接git tag 不会生成

image.png

多出一个tag 对象,可查看对应commit、作者、tag标注,同时.git/refs/tags中也会多出一个文件

object之间的关联图(三)

image.png

6. 查看.git/HEAD文件
cat .git/HEAD 
// 文件内容:.git/refs/heads/master
cat .git/refs/heads/master
// 文件内容:最新commit记录

image.png

object之间的关联图(四)

image.png

HEAD、分支、普通的Tag可以简单的理解成是一个指针,指向对应commit的SHA1值

变更文件时工作区域变动

MTVideo.GIF

三、.git目录结构

文件夹类型文件夹里面的内容
objects文件夹这就是实际意义上的 git数据库, 存数据的地方; 并且存了所有的历史记录;
refs文件夹目录存储指向数据(分支、远程仓库和标签等)的提交对象的指针;
HEAD文件它文件通常是一个符号引用(symbolic reference),指向目前所在的分支。
hooks文件夹目录包含客户端或服务端的钩子脚本;我们最常用的就是pre-commit钩子了;
FETCH_HEAD文件git fetch将更新git remote 中所有的远程repo 所包含分支的最新commit-id, 将其记录到.git/FETCH_HEAD文件中
ORIG_HEAD文件当前分支,前一次commit-id
config文件包含项目特有的配置选项;
index文件文件保存暂存区信息;
description文件用来显示对仓库的描述信息,文件仅供 GitWeb 程序使用,我们无需关心;
info文件夹包含一个全局性排除文件;

FQA

  1. Git为啥是全量快照存储, 而非增量存储?
    Git做了取舍,以空间换时间,把复杂度从O(n)降为O(1)。
    全量快照存储:diff两次commit为O(1)复杂度,获取commit节点下的tree object,再把对应文件取出来做对比。
    增量存储:diff两次commit为O(n)复杂度,去到每个文件第一个commit,叠加之后的变更记录,才能做对比

  2. Blob对象的hash值为啥不保存文件名信息?
    假如git把文件名信息存放在blob对象中,我们仅仅修改文件名,git会全量快照创建一份blob对象,
    但是文件名储存在tree objects,git只要新建一份tree对象。大部分blob对象要比tree对象大很多,blob记录是文件内容,而tree对象只记录了文件同层信息。

  3. Git怎么保证历史记录不可篡改?
    Git和区块链的数据结构非常相似,都是基于哈希式和分布式,一个文件改动,都应的object、tree、commit都会改变。可以进行追溯。

参考链接:https://www.lzane.com/slide/git-under-the-hood/#/3/19

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值