前言
做技术一定要知其然知其所以然,意思就是:知道它是这样的,更知道它为什么是这样的。我主要通过4块内容来简单介绍 Git 原理是什么样的。这4块内容如下:
- Git 存储目录结构介绍
- Git 是如何存储的
- Git 的对象
- Git引用
当然 Git 原理不仅仅包含这些,想要更深入了解请查看官方教程 https://git-scm.com/book/zh/v2/。
本文内容是我在 GitChat 分享关于Git 的Chat 《Git实用操作手册》 摘抄一个章节,关于《Git实用操作手册》其他内容请访问 https://gitbook.cn/gitchat/activity/5cb46e9dd877c443a183f9d4。
Git 存储目录结构介绍
首先我们先从 Git 存储目录说起,通过 git init 创建一个空的 Git 仓库,具体操作如下图:
创建完成后进入 .git 目录,如下图所示:
- hooks 该目录用于配置 客户端执提交操作用于触发服务端的脚本配置,一般用于自动化部署使用
- info 该目录用于配置一些不希望被 Git 管控的文件。
- objects 该目录用于存储所有数据对象内容,这些数据内容类型有 commit tree blob tag
- refs 该目录用于存储 Git 本地以及远程分支的引用,当然还有一种特殊的引用标签引用
- config 该文件包含项目特有的配置选项,并且该配置仅对该 Git 仓库有效
- description 该文件仅供 GitWeb 程序使用
- HEAD 该文件表示当前 Git 仓库处于哪个分支
- index 该文件保存暂存区信息 (空仓库下该文件不会显示一旦执行 git add 操作该文件就会出现)
通过 git config --local 查看 config 文件的变化
我们通过 git config --local 配置仅对于 gitLearn 项目有效,用户名和邮箱配置如下图所示:
查看 config 文件,会发现该文件新增用户信息配置。
Git 是如何存储的
Git 是一个内容寻址文件系统
其核心部分是一个简单的键值对数据库(key-value data store)。 你可以向该数据库插入任意类型的内容,它会返回一个 40 位字符串键,通过该 40 位字符串键可以在任意时刻再次检索(retrieve)该内容。
什么是内容寻址?
每次我们进行提交会通过 SHA-1 算法生成一个长度为 40 个字符的校验和(checksum hash)
(也就是我们的 key)然后根据校验和
去获取我们文件的内容。这种通过唯一标识的 key(也可以理解为内容的地址)去获取我们的内容的操作就是内容寻址
。
Git 的对象
在 Git 中有四种对象分别为:
- blob 是具体的文件对象
- tree 是某个时刻提交目录的内容
- commit 执行一次 commit 就会产生一个 commit 对象
- tag 可以理解成 commit 的别名,一个 tag 对应一个 commit
了解 Git 的对象需要使用如下命令进行查看:
- git cat-file -p 对象 hash 值 查看对象的内容
- git cat-file -t 对象 hash值 查看对象的类型
- git ls-files --stage 查看 index 文件内容
- git hash-object 查看文件的 hash 值
查看 commit tree blob 三个对象演示
我们创建一个 first.txt 文件,并将其提交到暂存区中。
进入 .git 文件夹下会发现新增了一个 index 文件。
我们可以通过 git ls-files --stage 查看 index 文件的内容。
进入 objects 目录发现 9c 文件夹名称+文件名称 和 index 文件中的一段字符串内容相同。
我们通过 git cat-file -t 9c59e24b8393179a5d712de4f990178df5734d99 我查看该表示对象类型 如下图所示表示该标识对象类型是 blob。
执行 git commit -m 将 first.txt 文件提交到本地仓库中。
执行 git log 查看我们的提交记录。
如下图所示我们通过 git cat-file -p commitId 查看我们提交的内容。如下图所示:我们最新一次提交包含了一个 59b06 开头的 tree 对象。
在 ./git/objects 目录中可以找到我们对应的文件。
我们通过 git cat-file -p tree对象哈希值,查看该 tree 对象的内容 。如下图所示显示就是我们 git add参生的 blob对象。
在通过 git cat-file -p blob对象哈希值,查看我们 blob对象内容,如下图所示 blob对象 内容就是我们 first.txt 文件的内容。
我们将 first.txt 文件提交到本地仓库 会产生一个 commit 一个 tree 和一个 blob 对象。
tag 对象原理演示
首先我们通过 git log 查看最新的提交是 add a.txt 注释的 commit 如下图所示。
通过 git tag -a v1.1 -m ‘add a.txt tag’ 为该 commit 创建一个附注标签。
在我们的 .git/refs/tags/ 目录下会新增 v1.1 文件。
v1.1 内容如下:
看到这个你肯定想到了这是一个 Git 对象,我们通过 git cat-file -t 查看这个哈希值对象类型。如下图所示它是一个 tag。
然后通过 git cat-file -p 查看它的内容,如下图所示,该tag包含了commit 对象和 标注的信息。
Git 引用
什么是 Git 的引用?
这里的引用我们可以理解成一个书签,你在阅读一本书的时候可以为你读到的部分打上标签。然后你可以通过这个书签快速找到你之前阅读的位置。在 Git 中我们 创建新的 Commit 或者创建分支都会进行一次打标签的操作。我们各个不同 commit 之间的切换或分支的切换其实就是标签的切换。
Git 引用的三种类型
在 Git 中有三种类型的的引用 分别是:
- HEAD 引用:用来为我们的本地仓库打上标签使用。
- Tag 引用:用来为我们的 Git 仓库 tag 标签使用。
- 远程引用:用来为我们的远程仓库打标签使用。
HEAD 引用
HEAD 引用原理 我的个人理解是一个 Head 头指针+当前分支 指向当前最新提交的 commit 对象。我们也可以通过 git reset --hard 来切换我们commit 记录,切换后的 commit 以前的 commit 记录就没有了,不过我们可以通过 git reflog 查询操作记录将以前 commit 找回。
为了更好的理解 HEAD,引用建议大家访问 http://onlywei.github.io/explain-git-with-d3/#zen 执行 Git 相关的操作来理解什么是 HEAD引用。
如下图所示,我们的 Head头指针分支 指向 master分支 同时指向最新的 commit e137e。
我们执行一次 commit Head头指针分支 + master分支 就指向的是最新的提交 896ee 上。
通过 git branch b1 创建新的分支名称为b1,如下图所示,我们发现 Head 头指针还是指向 master 。
当执行 git checkout b1 的时候,如下图所示,此时 Head 指针指向分支 b1。
我们在分支 b1 上执行 commit Head 指针指向了在该分支下的最新提交 3c7acb。
当执行 git checkout master 是 Head 指针指向了 master 上最新的 commit 896ee。
当执行 git merge b1 的时候,会将 b1分支最新 commit 合并到 master,我们 Head指针 + master 同时指向最新的 commit 3c7ba 上。
通过上面的动态演示,这回对 Head 应用如何切换 commit 切换分支有了一定的理解。
接下来我们来通过查看 .git 目录来介绍 Head 指针是如何实现的。进入 .git 目录你会发现有一个 HEAD 文件,它的内容是 ref: refs/heads/master,如下图所示。
refs/heads/master 是具体文件路径,我们查 refs/heads 目录下的 master 文件内容,如下图所示,master 文件内容就是我们最新的 commit。
我们执行 git chekcout demoBranch3。
此时我们的 HEAD 文件内容变成了 ref: refs/heads/demoBranch3。
查看 refs/heads/ 路径下的 demoBranch3 文件,如下图所示 demoBranch3 文件记录是demoBranch3 分支下最新的 commit。
标签引用
标签引用是一个特殊的引用,它不像 HEAD 引用可以通过 git checkout 来更改引用的指针指向。标签引用不会移动它永远会指向一个 Commit 对象。
标签引用的演示请参看 Tag 对象原理演示部分。
远程引用
-
远程引用是只读的我们不能通过切换到远程引用执行 commit 将提交更新到远程仓库中
-
我们在本地分支执行 push 操作,Git 都会帮我们记录 push 到远程分支的最新 comit,当执行push 的时候,如果发现远程分支最新 commit 和我们本地仓库记录最后一次 push 的 commit 不同会报 Note about fast-forwards 异常,如下图所示:
出现 Note about fast-forwards 我们需要执行 git pull 将远程最新的 commit 拉取下来 然后再执行 git push 操作 或者 直接执行 git puhs -f。这里强调一下不建议执行 git puhs -f 操作因为会强制将本地的历史记录覆盖到远程仓库的历史记录。
通过查看 .git 目录理解远程引用
在 .git/refs/remotes/origin 文件夹中 HEAD 文件内容表示内容表示远程分支处于哪个分支。
在 .git/refs/remotes/origin 文件夹中 master 文件内容,表示最后一次 push 到远程仓库的commit,如下图所示:
在 .git/refs/remotes/origin 文件夹中 demoBranch1 文件表示,最后一次 push 到 demoBranch1 分支提交的 commitId。
参考文献
Git 官方教程
本文链接:https://my.lmcjl.com/post/13419.html
4 评论