04.Git学习笔记–Git底层原理
Git 存储对象(HashMap)
Git 是一个内容寻址文件系统,其核心部分是一个简单的键值对数据库(key-value data store),你可以向数据库中插入任意内容,它会返回一个用于取回该值的 Hash 键。
# git 键值库中插入数据
echo 'learn git' | git hash-object -w --stdin
#返回:96d9d2f539e7b7c1865cb02bcb9bc6f075707e9d
#基于键获取指定内容
git cat-file -p 96d9d2f539e7b7c1865cb02bcb9bc6f075707e9d
#返回:learn git
Git 基于该功能把每个文件的版本中内容都保存在数据库中,当要进行版本回滚的时候,就通过其中一个键将其取回并替换。
模拟演示 Git 写入与回滚过程
git init yangxu3
cd yangxu3
#查找所有的git对象
find .git/objects/ -type f
# 写入版本1
echo 'version1' > README.MD; git hash-object -w README.MD;
#返回:5bdcfc19f119febc749eef9a9551bc335cb965e2
#查找所有的git对象
find .git/objects/ -type f
#返回:.git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
#根据指定的 Hash 键查看内容
git cat-file -p 5bdcfc19f
#返回:version1
# 写入版本2
echo 'version2' > README.MD; git hash-object -w README.MD;
#返回:df7af2c382e49245443687973ceb711b2b74cb4a
#查找所有的git对象
find .git/objects/ -type f
#返回:
# .git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
# .git/objects/df/7af2c382e49245443687973ceb711b2b74cb4a
# 写入版本3
echo 'version3' > README.MD; git hash-object -w README.MD;
#返回:777d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
cat README.MD
#返回:version3
# 回滚指定版本
git cat-file -p 5bdcfc19f119febc749eef9a9551bc335cb965e2 > README.MD
cat README.MD
#返回:version1
#查看对象类型
git cat-file -t 777d3c2b51
#返回:blob
echo 'hello yangxu' > yangxu.txt
#查找所有的git对象
find .git/objects/ -type f
#返回:
#version1
# .git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
#version3
# .git/objects/77/7d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
#version2
# .git/objects/df/7af2c382e49245443687973ceb711b2b74cb4a
git status
#Untracked files:
# README.MD
# yangxu.txt
git add yangxu.txt
#查找所有的git对象
find .git/objects/ -type f
#返回:
# .git/objects/4c/f2efc0e9fb0ab868c0e4bb01ab384d27212501
# .git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
# .git/objects/77/7d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
# .git/objects/df/7af2c382e49245443687973ceb711b2b74cb4a
git add README.MD
#查找所有的git对象
find .git/objects/ -type f
#返回:
# .git/objects/4c/f2efc0e9fb0ab868c0e4bb01ab384d27212501
# .git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
# .git/objects/77/7d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
# .git/objects/df/7af2c382e49245443687973ceb711b2b74cb4a
#git add 就相当于执行了git hash-object -w
所以 git add 其实就是把修改之后的内容插入到键值库中。当执行 git add README.MD 等同于执行了 git hash-object -w README.MD 把文件写到数据库中。
解决了存储的问题,但其只能存储内容,并没有存储文件名,如果要进行回滚,怎么知道哪个内容对应哪个文件呢?
树对象解决了文件名存储的问题。
Git 树对象
树对象解决了文件名的问题,它的目的是将多个文件名组织在一起,其中包含多个文件名称与其对应的 Key 和其它树对象的引用,可以简单理解为操作系统中的文件夹,一个文件夹包含多个文件和多个其它文件夹。
每一个分支当中都关联了一个树对象,存储了当前分支下所有的文件名及对应的 Key。
通过以下命令即可查看
#查看分支树
#语法表示master分支上最新的提交所指向的树对象
git cat-file -p master^{tree}
# 100644 blob 5bdcfc19f119febc749eef9a9551bc335cb965e2 README.MD
# 100644 blob 4cf2efc0e9fb0ab868c0e4bb01ab384d27212501 yangxu.txt
Git 提交对象
一次提交即为当前版本的一个快照,该快照就是通过提交对象保存,其存储的内容为:一个顶级树对象、上一次提交的对象啥希、提交者用户名及邮箱、提交时间戳、提交评论。
git cat-file -p 47743f
#tree 8841349a4199ff9188227cab93b45ae0e5037e62
#parent f89ba79925b3b11ff6c96700fd75be1ab081be2e
#author 阳旭 <yangxu@example.com> 1597897008 +0800
#committer 阳旭 <yangxu@example.com> 1597897008 +0800
#第二次提交 添加了hello.java
从修改一个文件到提交的过程总共生成了三个对象:
- 一个内容对象 –> 存储了文件内容
- 一个树对象 –> 存储了文件名及内容对象的 Key
- 一个提交对象 –> 存储了树对象的 Key 及提交评论
Git 引用
当执行 git branch {branchName} 时创建了一个分支,其本质就是 Git 基于指定提交创建了一个引用文件,保存在 .git\refs\heads\ 下。
git branch dev
cat .git/refs/heads/dev
#29a762c133e18aa211bedfa9d6b46f2dfa34939d
Git 总共 有三种类型的引用:
- 分支引用
- 远程分支引用
- 标签引用
#查询比较两个版本
git log master..dev
#版本提交历史网络
git log --pretty=format:'%h %s' --graph
# * 29a762c 第三次提交,添加src/main/hi.java
# * 47743f8 第二次提交 添加了hello.java
# * f89ba79 第一次提交
#查看分支树
git cat-file -p master^{tree}
#100644 blob 5bdcfc19f119febc749eef9a9551bc335cb965e2 README.MD
#040000 tree 9ab1341c6eef75e8a815723ba0538a767dce43cd src
#100644 blob 4cf2efc0e9fb0ab868c0e4bb01ab384d27212501 yangxu.txt
实例
接“模拟演示 Git 写入与回滚过程”的操作过程。
#查找所有的git对象
find .git/objects/ -type f
#返回:
# .git/objects/4c/f2efc0e9fb0ab868c0e4bb01ab384d27212501
# .git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
# .git/objects/77/7d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
# .git/objects/df/7af2c382e49245443687973ceb711b2b74cb4a
git commit -am '第一次提交';
#查找所有的git对象
find .git/objects/ -type f
#返回:
# .git/objects/4c/f2efc0e9fb0ab868c0e4bb01ab384d27212501
# .git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
# .git/objects/77/7d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
# .git/objects/ba/3f3367b5d7b3d849a5616805cbef52d28ce6fd
# .git/objects/df/7af2c382e49245443687973ceb711b2b74cb4a
# .git/objects/f8/9ba79925b3b11ff6c96700fd75be1ab081be2e
#master^{tree} 语法表示 master 分支上最新的提交所指向的树对象
git cat-file -p master^{tree}
# 100644 blob 5bdcfc19f119febc749eef9a9551bc335cb965e2 README.MD
# 100644 blob 4cf2efc0e9fb0ab868c0e4bb01ab384d27212501 yangxu.txt
git log
#commit f89ba79925b3b11ff6c96700fd75be1ab081be2e (HEAD -> master)
#Author: 阳旭
#Date: Wed Aug 19 21:08:13 2020 +0800
#第一次提交
git cat-file -p f89ba79925b3b
#tree ba3f3367b5d7b3d849a5616805cbef52d28ce6fd
#author 阳旭 1597842493 +0800
#committer 阳旭 1597842493 +0800
#第一次提交
git cat-file -p ba3f3367b5
#100644 blob 5bdcfc19f119febc749eef9a9551bc335cb965e2 README.MD
#100644 blob 4cf2efc0e9fb0ab868c0e4bb01ab384d27212501 yangxu.txt
#回滚时基于文件的名称找到对应的 Key
#再根据 Key 找到对应的文件内容
git cat-file -p 5bdcfc19f119f
#version1
git cat-file -p 4cf2efc0e9fb0
#hello yangxu
mkdir -p src/main/java/com/
echo 'hello java' > src/main/java/com/hello.java
git add -A; git commit -am '第二次提交 添加了hello.java';
#查找所有的git对象
find .git/objects/ -type f
# .git/objects/0d/5fd5e3f28eeb99f12200f6763863dc8ff454a0
#(tree) com
# .git/objects/4c/f2efc0e9fb0ab868c0e4bb01ab384d27212501
#hello yangxu
# .git/objects/63/49162fdf1fc358aa6c38b5c414fdb67f4fce62
#(tree) java
# .git/objects/79/72b3cab0fc6556ded9686a420124d6bd3c1a71
#(tree) main
# .git/objects/92/6d8f6502d1546c36b0284016853ea43800cd13
#(blob) hello.java
# .git/objects/a1/58e71c23418e55639ca153a8728594147a40c5
#hello java
# .git/objects/f8/9ba79925b3b11ff6c96700fd75be1ab081be2e
#tree ba3f3367b5d7b3d849a5616805cbef52d28ce6fd
#author 阳旭 <yangxu@example.com> 1597842493 +0800
#committer 阳旭 <yangxu@example.com> 1597842493 +0800
#第一次提交
# .git/objects/ba/3f3367b5d7b3d849a5616805cbef52d28ce6fd
#100644 blob 5bdcfc19f119febc749eef9a9551bc335cb965e2 README.MD
#100644 blob 4cf2efc0e9fb0ab868c0e4bb01ab384d27212501 yangxu.txt
# .git/objects/47/743f824c9dd8568544951bdfb641a0744066e8
#tree 8841349a4199ff9188227cab93b45ae0e5037e62
#parent f89ba79925b3b11ff6c96700fd75be1ab081be2e
#author 阳旭
#committer 阳旭
#第二次提交 添加了hello.java
# .git/objects/77/7d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
#version3
# .git/objects/df/7af2c382e49245443687973ceb711b2b74cb4a
#version2
# .git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
#version1
# .git/objects/88/41349a4199ff9188227cab93b45ae0e5037e62
#100644 blob 5bdcfc19f119febc749eef9a9551bc335cb965e2 README.MD
#040000 tree 7972b3cab0fc6556ded9686a420124d6bd3c1a71 src
#100644 blob 4cf2efc0e9fb0ab868c0e4bb01ab384d27212501 yangxu.txt
在 main 目录下添加一个 hi.java
echo 'hello java' > src/main/hi.java
git add -A; git commit -am '第三次提交,添加src/main/hi.java';
#查找所有的git对象
find .git/objects/ -type f
# .git/objects/0d/5fd5e3f28eeb99f12200f6763863dc8ff454a0
#tree 926d8f com
# .git/objects/29/a762c133e18aa211bedfa9d6b46f2dfa34939d
#tree 8ac8ff9b39b6100544778bd809d406720aff47c2
#parent 47743f824c9dd8568544951bdfb641a0744066e8
#author 阳旭
#committer 阳旭
#第三次提交,添加src/main/hi.java
# .git/objects/47/743f824c9dd8568544951bdfb641a0744066e8
#tree 8841349a4199ff9188227cab93b45ae0e5037e62
#parent f89ba79925b3b11ff6c96700fd75be1ab081be2e
#author 阳旭
#committer 阳旭
#第二次提交 添加了hello.java
# .git/objects/4c/f2efc0e9fb0ab868c0e4bb01ab384d27212501
#hello yangxu
# .git/objects/53/bb87a2f653cb79d2acef6c9eef5ef076eaf1d4
#blob a158e7 hi.java
#tree 0d5fd5 java
# .git/objects/5b/dcfc19f119febc749eef9a9551bc335cb965e2
#version1
# .git/objects/63/49162fdf1fc358aa6c38b5c414fdb67f4fce62
#tree 0d5fd5 java
# .git/objects/77/7d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
#version3
# .git/objects/79/72b3cab0fc6556ded9686a420124d6bd3c1a71
#tree 634916 main
# .git/objects/88/41349a4199ff9188227cab93b45ae0e5037e62
#blob 5bdcfc README.MD
#tree 7972b3 src
#blob 4cf2ef yangxu.txt
# .git/objects/8a/c8ff9b39b6100544778bd809d406720aff47c2
#blob 5bdcfc README.MD
#tree 9ab134 src
#blob 4cf2ef yangxu.txt
# .git/objects/92/6d8f6502d1546c36b0284016853ea43800cd13
#blob a158e7 hello.java
# .git/objects/9a/b1341c6eef75e8a815723ba0538a767dce43cd
#tree 53bb87 main
# .git/objects/a1/58e71c23418e55639ca153a8728594147a40c5
#hello java
# .git/objects/ba/3f3367b5d7b3d849a5616805cbef52d28ce6fd
#blob 5bdcfc README.MD
#blob 4cf2ef yangxu.txt
# .git/objects/df/7af2c382e49245443687973ceb711b2b74cb4a
#version2
# .git/objects/f8/9ba79925b3b11ff6c96700fd75be1ab081be2e
#tree ba3f3367b5d7b3d849a5616805cbef52d28ce6fd
#author 阳旭 <yangxu@example.com> 1597842493 +0800
#committer 阳旭 <yangxu@example.com> 1597842493 +0800
#第一次提交
由于 main 目录下的内容发生了改变,所以部分 Key 发生了变化。