04_Git底层原理解析

 教程目录

01_版本控制概述

02_Git概述

03_Git下载和安装

04_Git底层原理解析

05_Git命令详解 - CRUD

06_Git命令详解 - 分支

07_Git命令详解 - 后悔药

08_Git远程仓库

、Git三大分区

Git三大分区概念

        Git 本地数据管理,大概可以分为三个区,工作区,暂存区和版本库。

04_Git底层原理解析_第1张图片

         我们先看一张图

04_Git底层原理解析_第2张图片

工作区(Working Directory)

         直接编辑的地方,肉眼可见,直接操作。

        GitTest是我本地的一个仓库, 其中GitTest目录就是我们的工作区,但不包括.git这个目录

暂存区/缓存区(Stage 或 Index)

        数据暂时存放的区域。

        缓存区是一个看不见的区域,个人觉得这是Git的一个安全机制吧,就是说你提交代码的时候有可能会提交一些错误的代码,所以这个区域就是为了让你有机会反悔提交的代码。

        在项目目录下使用指令“git add fileName”就可以把代码存入缓存区,这里的fileName是指的要往缓存区添加的文件名,当然也可以使用“.”代表当前的整个目录。

        “git status”可以查看缓存区有什么文件。

版本库(commit History)

        存放已经提交的数据,push 的时候,就是把这个区的数据 push 到远程git仓库了。

        而.git这个目录就是本地仓库区,你commit的一切更改都会存储到本地仓库中

远程仓库(remote)

        简单来说,就是我们工作过程中不能将主项目放到某一个人的本地电脑上,这时就需要有一个地方存储主项目,这个地方就是我们的git远程仓库。

Git本地库与远程库

  • git本地库,适合个人在本地使用版本管理工具来管理自己的资料

  • git远程库,适合团队的协作,成员之间共享资料

04_Git底层原理解析_第3张图片

Git 远程库与远程库之间

  • fork: 看到别人优秀的项目,光看不过瘾,手痒也想写点东西,但是又没有机会加入核心开发团队,那么就可以fork下别人代码,到自己的远程库中。其实就是clone一份。自己就可以完全掌控代码库了。

  • pull request:当自己将别人fork过来的代码库,修改了一部分功能,又觉得自己的工作成果不想浪费,就想共享给原作者,那么就可以对原作者的代码库发起一个pull request。

04_Git底层原理解析_第4张图片

Git 三种状态

  • 已提交(committed):已提交表示数据已经安全的保存在本地数据库中。

  • 已修改(modified):已修改表示修改了文件,但还没保存到数据库中。

  • 已暂存(staged): 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。

04_Git底层原理解析_第5张图片

二、Git底层命令

基础的 linux 命令

目录命令

查询目录中的内容

ls [选项] [文件或目录]

选项:

  • -a:显示所有文件,包括隐藏文件

  • -l:显示详细信息

  • -d:查看目录属性

  • -i:显示inode

04_Git底层原理解析_第6张图片

         注:ls -l可以简写为ll

建立目录

mkdir [-p] [目录名]

选项:

  • -p:递归创建

04_Git底层原理解析_第7张图片

切换所在目录

cd [目录]

# 返回上次的目录
cd -  

# 进入上一级目录
cd ..  

# 进入当前目录
cd .

04_Git底层原理解析_第8张图片

显示当前所在目录

# 显示当前所在目录
pwd

04_Git底层原理解析_第9张图片

删除空目录

# 删除空白目录,若包含文件则不能删除
rmdir [目录]

04_Git底层原理解析_第10张图片

文件处理命令

创建空文件

touch [文件名]

04_Git底层原理解析_第11张图片

删除文件或目录

rm [文件或目录]

选项:

  • -r:删除目录

  • -f:强制

注意:rm -rf / 强制删除根目录下的所有(相当于自杀,绝对不可以使用)

04_Git底层原理解析_第12张图片

复制文件或目录

cp [选项] [原文件或目录] [目标目录]

选项:

  • -r:复制目录

  • -p:连带文件属性复制

  • -d:若原文件是链接文件,则复制链接属性

  • -a:相当于都复制 同-pdr

04_Git底层原理解析_第13张图片

剪切或改名

mv [原文件或目录] [目标目录]

04_Git底层原理解析_第14张图片

搜索文件

find  path  [-option]

选项:

  • -name:文件名称

  • -iname:不区分大小写

可以使用通配符:

  • * 匹配任意内容

  • ? 匹配任意一个字符

  • [] 匹配任意一个中括号内的字符

# 将对应clea铺在控制台 
find ./

# 将目前目录及其子目录下所有延伸档名是 c 的文件列出来。
find ./ -name "*.c"

# 将目前目录其其下子目录中所有一般文件列出
# find 目录名 -type f :将对应目录下的文件平铺在控制台 
find ./ -type f

# 将目前目录及其子目录下所有最近 20 天内更新过的文件列出
find ./ -ctime -20

# 查找指定区域内名称为“good”的文件!
find ./demo -name  "good.txt"  

# 增加i是指不区分大小写
find ./demo -iname  "good.txt"  

文件查看和编辑

查看

# 从第一个字节开始正向查看
cat 文件名

# 从最后一行开始反向查看
tac 文件名

# 查看一个文件的前n行,n为指定行数
head -n 文件名

# 查看一个文件的后n行,n为指定行数
tail -n 文件名

编辑

vim 文件名

        vi/vim 共分为三种模式,分别是命令模式,输入模式和底线命令模式。

        1、命令模式

                启动vi/vim,便进入了命令模式。此状态下敲击键盘动作会被vim识别为命令,而非输入字符。

        常用命令:

命令 说明
Ctrl + f 屏幕向下移动一页,相当于Page Down键
Ctrl + b 屏幕向上移动一页,相当于Page Up键
0或Home键 移动到这一行的最前面字符处
$或End键 移动到这一行的最后面字符处
gg 移动到这个档案的第一行
G 移动到这个档案的最后一行
数字 + Enter键 向下移动指定行数
x(小写) 向后删除一个字符,相当于del键
X(大写) 向前删除一个字符,相当于backspace键
dd 删除光标所在的那一行
yy 复制光标所在的那一行
p(小写) 将已复制的数据,粘贴在光标下一行
P(大写) 将已复制的数据,粘贴在光标上一行
u 复原前一个动作,撤销
. 重复前一个动作

        3、输入模式

        进入输入模式的命令:

命令 说明
i 在目前光标所在处输入
I 在光标所在行的第一个非空格符处开始输入
a 在目前光标所在的下一个字符处开始输入
A 在光标所在行的最后一个字符处开始输入
o 在光标所在的下一行输入新的一行
O 在光标所在的上一行输入新的一行
Esc 退出输入模式,回到命令模式

        3、底线命令模式

        在命令模式下按下:(英文冒号)就进入了底线命令模式。

命令 说明
:w 将编辑的数据写入硬盘档案中
:wq 存档并离开
ZZ 存档并离开
:q! 不存档并离开
:set number/nu 显示行号

其他命令

清除屏幕

clear :清除屏幕 

控制台输出

# 往控制台输出信息
echo 'test content'

# 往文件中输出信息
echo 'test content' > test.txt 

初始化新仓库

        什么是版本库呢?

        版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

        所以,创建一个版本库非常简单,进入工作区,右键打开git base here命令行;

        命令:git init

        解析:要对现有的某个项目开始用 Git 管理,只需到此项目所在的目录,执行:git init

        作用:初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目录,但我们还没有开始跟踪管理项目中的任何一个文件。

Git目录

04_Git底层原理解析_第15张图片

hooks 目录

        hooks 目录包含客户端或服务端的钩子脚本;

        这是一个有趣的特性。Git 提供了一套脚本,可以在每个有意义的 Git 阶段自动运行。这些被称为钩子的脚本可以在提交 (commit)、变基 (rebase)、拉取 ( pull ) 操作的前后运行。脚本命名预示着它的执行时机。如我们可以编写 pre-push 的作为钩子,进行推送代码前的检查。

info 目录

        info 包含一个全局性排除文件

        你可以将不想被 git 管理的文件记录到 .gitignore 文件中。

        排除文件的意思是不想共享这个文件。例如你不想共享你的 IDE 自定义配置,将其添加到 .gitignore 文件中即可。

logs 目录

        logs 保存日志信息

        保存所有更新的引用记录。打开logs文件夹可以看到其中有两个文件,refs文件夹和HEAD文件。

        1、refs文件夹中有两个文件夹:

        heads文件里面存储的是本地分支的对象,每个对象的文件名就是本地的一个分支名。我们使用git branch查看本地所有分支时,查询出的分支就是heads文件夹下所有文件的名称,这些分支文件中存储的是对应分支下的操作记录

04_Git底层原理解析_第16张图片

         remotes文件夹里存储的是远程的所有分支对象,每个对象的文件名称就是远程的一个分支名称。这些分支文件中保存了远程仓库对应分支所有操作

        2、logs下的HEAD文件保存的是所有的操作记录,使用git reflog查询的结果就是从这个文件来的。

objects 目录

        objects 目录存储所有数据内容;

        存放所有的 git 对象,哈希值一共40位,前 2 位作为文件夹名称,后 38 位作为对象文件名。

refs目录

        refs目录存储指向数据的提交对象的指针(分支)

        refs下有三个文件夹。

        heads:里面包含所有的本地分支,每个分支都是文件,文件中存储了分支当前指向的commit;打开refs\heads\master文件查看,可以看到其中保存的确实是最近一次提交的commit id。

04_Git底层原理解析_第17张图片

         tags 叫做里程碑,或者版本发布用或记录重要版本。

        remotes,远程仓库信息

        其中\refs\remotes\origin\HEAD:指向远程仓库当前分支。

config 文件

        config 文件包含项目特有的配置选项

description 文件

        description 用来显示对仓库的描述信息

HEAD 文件

        HEAD 文件指示目前被检出的分支

index 文件

        index 文件保存暂存区信息,一个二进制文件

git对象

git对内容版本控制

        Git的核心部分是一个简单的键值对数据库。你可以向该数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索该内容。

向数据库写入内容并返回对应键值

        命令:

echo 'test content' | git hash-object -w --stdin

        说明:

        -w选项指示 hash-object 命令存储数据对象;若不指定此选项,则该命令仅返回对应的键值

        --stdin(standard input)选项则指示该命令从标准输入读取内容;若不指定此选项,则须在命令尾部给出待存储文件的路径

        返回:

        该命令输出一个长度为 40个字符的校验和。这是一个 SHA-1哈希值

        示例:从标准输入流读取内容,生成对应的哈希值;但是没有真正存储

04_Git底层原理解析_第18张图片

        示例:从标准输入流读取内容存储在本地库,并生成对应的哈希值;  

04_Git底层原理解析_第19张图片

查看Git是如何存储数据的

        命令:

find .git/objects -type f

        返回:

.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4

        这就是开始是Git存储内容的方式:一个哈希对应一条内容。前两个字符用于命名子目录,余下的 38个字符则用作文件名。

04_Git底层原理解析_第20张图片

根据键值拉取数据

        命令:

git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4  

        -p选项可指示该命令自动判断内容的类型,并为我们显示格式友好的内容

        返回:对应文件的内容

        注:cat查看内容是文本存储压缩后的内容;

git对文件版本控制

创建一个新文件并将其内容存入数据库

        命令:

# 存文件
git hash-object -w文件路径

# 返回对应文件的键值
git hash-object文件路径

        示例:

# 命令:
# 把内容写入文件中
echo 'version 1' > test.txt
# 存储文件
git hash-object -w test.txt

# 返回:
83baae61804e65cc73a7201a7252750c76066a30

04_Git底层原理解析_第21张图片

 git在执行git add命令时出现以下警告:

warning: LF will be replaced by CRLF in test.txt. The file will have its original line endings in working directory

因为Git的换行符检查功能。LF是linux下的换行符,而CRLF是enter + 换行。

 向文件里写入新内容,并再次将其存入数据库

# 命令:
echo 'version 2' > test.txt
git hash-object -w test.txt

# 返回:
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

04_Git底层原理解析_第22张图片

 查看数据库内容

# 命令:
find .git/objects -type f
git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30
git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

# 利用 cat-file -t命令,可以让  Git告诉我们其内部存储的任何对象类型
git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
# 返回:
blob

04_Git底层原理解析_第23张图片

问题总结

1.记住文件的每一个版本所对应的 SHA-1值并不现实    (希望记录的是项目版本/快照)

2.在Git中,文件名并没有被保存——我们仅保存了文件的内容

解决方案:树对象

注意:当前的操作都是在对本地数据库进行操作不涉及暂存区。

树对象

        树对象(tree object),它能解决文件名保存的问题,也允许我们将多个文件组织到一起。Git以一种类似于 UNIX文件系统的方式存储内容。

        所有内容均以树对象和数据对象(git对象)的形式存储,其中树对象对应了 UNIX中的目录项,数据对象(git对象)则大致上对应文件内容。一个树对象包含了一条或多条记录(每条记录含有一个指向 git对象或者子树对象的 SHA-1指针,以及相应的模式、类型、文件名信息)。

        一个树对象也可以包含另一个树对象。

构建树对象

        我们可以通过 update-index;write-tree;read-tree等命令来构建树对象并塞入到暂存区。

        假设我们做了一系列操作之后得到一个树对像。

操作一:

        利用 update-index命令为test.txt文件的首个版本【在本地库中存储】——创建一个暂存区。并通过 write-tree命令生成树对像。

        test.txt文件的首个版本

# 创建test.txt文件,并将其内容存储在本地库
git init
echo 'test content' > test.txt
git hash-object -w ./test.txt

# 返回:test.txt内容在本地库存储对应的key	->git对象
d670460b4b4aece5915caf5c68d12f560a9fe3e4

04_Git底层原理解析_第24张图片

 把test.txt放到暂存区,并生成树对象

# 把test.txt放到暂存区:
git update-index --add --cacheinfo 100644 \
d670460b4b4aece5915caf5c68d12f560a9fe3e4 test.txt

# 生成树对象-》为暂存区生成快照并保存到objects
git write-tree

查看暂存区

git ls-files -s        

说明:

        文件模式为

                100644,表明这是一个普通文件

                100755,表示一个可执行文件;

                120000,表示一个符号链接。

        --add选项:因为此前该文件并不在暂存区中首次需要--add

        --cacheinfo选项:因为将要添加的文件位于Git本地库中,而不是位于当前目录下所有需要--cacheinfo。

04_Git底层原理解析_第25张图片

         注:树对象就是我们的项目版本快照!

        80865964295ae2f11d27383e5f9c0b58a8ef21da    项目的第一个版本快照(树对象)

 操作二:

        新增 new.txt将 new.txt和 test.txt文件的第二个个版本塞入暂存区。并通过 write-tree命令生成树对像。

        新增 new.txt将 new.txt和 test.txt文件的第二个个版本存入本地库

# 生成new.txt
echo 'new file' > new.txt

# 编辑修改test.txt的内容
vim test.txt

# new.txt和test.txt第二个版本存入本地库
git hash-object -w ./new.txt	# 返回fa49b077972391ad58037050f2a75f74e3671e92

git hash-object -w ./test.txt	# 返回c46f9d567f02066137fe1786bf1aa7873b510b67

04_Git底层原理解析_第26张图片

 目前本地库里面的数据

# 查看
find .git/objects -type f

.git/objects/80/865964295ae2f11d27383e5f9c0b58a8ef21da	项目的第一个版本快照(树对象)
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4	test.txt文件的第一个版本(git对象)

.git/objects/c4/6f9d567f02066137fe1786bf1aa7873b510b67	test.txt文件的第二个版本(git对象)
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92	new.txt文件的第一个版本(git对象)

将 new.txt和 test.txt文件的第二个个版本塞入暂存区,并生成树对象

# 把test.txt塞入暂存区
git update-index --cacheinfo 100644 \
c46f9d567f02066137fe1786bf1aa7873b510b67 test.txt

# 把new.txt塞入暂存区
git update-index --add --cacheinfo 100644 \
fa49b077972391ad58037050f2a75f74e3671e92 new.txt

git write-tree
# 第二份版本 95c70f16d00402c9530d4313d4463a3d60c74f8b

04_Git底层原理解析_第27张图片

 操作三:

         将第一个树对象加入第二个树对象,使其成为新的树对象

# 命令:read-tree命令,可以把树对象读入暂存区
git read-tree \
--prefix=bak 80865964295ae2f11d27383e5f9c0b58a8ef21da

git write-tree

图示(最后的树对象)

04_Git底层原理解析_第28张图片

         问题:现在有三个树对象(执行了三次 write-tree),分别代表了我们想要跟踪的不同项目快照。然而问题依旧:若想重用这些快照,你必须记住所有三个SHA-1哈希值。并且,你也完全不知道是谁保存了这些快照,在什么时刻保存的,以及为什么保存这些快照。而以上这些,正是提交对象(commit object)能为你保存的基本信息。

查看树对象

git cat-file -p 95c70f16d00402c9530d4313d4463a3d60c74f8b

100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b	simplegit.rb

04_Git底层原理解析_第29张图片

解析树对象

        Git根据某一时刻暂存区(即 index区域)所表示的状态创建并记录一个对应的树对象,如此重复便可依次记录(某个时间段内)一系列的树对象。

        其实树对象是对暂存区内操作的抽象,这颗树对象相对于就是快照。当我们的工作区有任何更改同步到暂存区时。便会调用write-tree命令通过 write-tree命令向暂存区内容写入一个树对象。它会根据当前暂存区状态自动创建一个新的树对象。即每一次同步都产生一颗树对象。且该命令会返回一个hash指向树对象。在Git中每一个文件(数据)都对应一个hash(类型 blob)每一个树对象都对应一个hash(类型 tree)

        总结:我们可以认为树对象就是我们项目的快照

提交对象

        我们可以通过调用 commit-tree命令创建一个提交对象,为此需要指定一个树对象的 SHA-1值,以及该提交的父提交对象(如果有的话第一次将暂存区做快照就没有父对象)

创建提交对象

# 创建提交对象,并添加注释
echo 'first commit' | git commit-tree 80865964295ae2f11d27383e5f9c0b58a8ef21da

# 返回:提交对象的hash
dcb30ef1a7385d63dbf99e20a8bc485ef9f4e636

# 查看提交对象类型
git cat-file -t dcb30ef1a7385d63dbf99e20a8bc485ef9f4e636

# 返回
commit

查看提交对象

# 查看提交对象内容
git cat-file -p dcb30ef1a7385d63dbf99e20a8bc485ef9f4e636

# 返回:包含树对象、作者、提交者、注释
tree 80865964295ae2f11d27383e5f9c0b58a8ef21da
author jingerbo  1598001199 +0800
committer jingerbo  1598001199 +0800

first commit

提交对象的格式

       提交对象的格式很简单:它先指定一个顶层树对象,代表当前项目快照;然后是作者 /提交者信息(依据你的user.name和user.email配置来设定,外加一个时间戳);留空一行,最后是提交注释。接着,我们将创建另两个提交对象,它们分别引用各自的上一个提交(作为其父提交对象):

# 第二次提交,包含第一个提交对象
echo 'second commit' | git commit-tree 95c70f16d0 -p dcb30ef1a738

# 返回第二个提交对象的hash
175c6b1226168f74ed039a0c72ec547ae35dba97

# 查看
git cat-file -p 175c6b1226168f7

# 返回
tree 95c70f16d00402c9530d4313d4463a3d60c74f8b
parent dcb30ef1a7385d63dbf99e20a8bc485ef9f4e636
author jingerbo  1598001561 +0800
committer jingerbo  1598001561 +0800

second commit

        第三次类似...

图示

04_Git底层原理解析_第30张图片

注意:git commit-tree不但生成提交对象而且会将对应的快照(树对象)提交到本地库中

你可能感兴趣的:(Git从入门到精通,git,1024程序员节)