Git

Git Learning Notes (04)

Git Underlying Principles

Posted by Chen Xingxu on September 3, 2020

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

从修改一个文件到提交的过程总共生成了三个对象:

  1. 一个内容对象 –> 存储了文件内容
  2. 一个树对象 –> 存储了文件名及内容对象的 Key
  3. 一个提交对象 –> 存储了树对象的 Key 及提交评论

Git 引用

当执行 git branch {branchName} 时创建了一个分支,其本质就是 Git 基于指定提交创建了一个引用文件,保存在 .git\refs\heads\ 下。

git branch dev 
cat .git/refs/heads/dev
#29a762c133e18aa211bedfa9d6b46f2dfa34939d

Git 总共 有三种类型的引用:

  1. 分支引用
  2. 远程分支引用
  3. 标签引用
#查询比较两个版本
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 发生了变化。