深入Git-中篇

简介: 前言上篇文章深入Git-上篇我们介绍了Git的目录结构,对于仓库的核心实现objects我们选择先跳过了。本篇文章就主要讲讲objects,通过本篇文章可以了解Git的仓库存储,其版本的实现逻辑等。

深入Git-中篇

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战

前言

上篇文章深入Git-上篇我们介绍了Git的目录结构,对于仓库的核心实现objects我们选择先跳过了。本篇文章就主要讲讲objects,通过本篇文章可以了解Git的仓库存储,其版本的实现逻辑等。


Git的三个分区


在开始学习前,我们先简单了解下Git的三个分区。

49.png


  • 工作区


顾名思义我们进行编辑操作的就是工作区的内容


  • 暂存区(索引)


可以理解为暂存着下一次commit的版本仓库


  • 版本库


存放所有版本内容的仓库,不同版本的文件及内容都可以在其中找到


objects结构

objects
├── info
└── pack
复制代码


初始化时objects下面仅包含两个目录,info和pack,其中pack将存储ojects下其它文件的打包压缩后的结果。


仓库文件类型


刚刚初始化的仓库objects下面是没有任何东西的,下面我们将通过添加文件等操作,为其添加不同类型的文件。


在工作区添加文件及内容


echo 'hello world' > a.txt
复制代码

blob

此刻,我们的objects还是为空的,我们将其添加到暂存区


git add a.txt
复制代码


我们再通过tree打印下objects目录,可以发现多了文件夹3b,下面有文件18e512dba79e4c8300dd08aeb37f8e728b8dad。文件名和我们平常使用的commitId类似。


├── 3b
│   └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
├── info
└── pack
复制代码


实际上文件名是根据文件内容通过SHA1哈希算法生成的,完整值为3b18e512dba79e4c8300dd08aeb37f8e728b8dad,存储在objects的时候会将前两位字符提取用于分桶存储,而文件内容则是个二进制文件。

00000000: 7801  4bca  c94f  5230
00000008: 3462  c848  cdc9  c957
00000010: 28cf  2fca  49e1  0200
00000018: 4411  0689
复制代码


为了更加清楚地展示其内容,我们再介绍一个命令

git cat-file -t <fileId> # 文件类型
git cat-file -p <fileId> # 文件内容
复制代码


我们来看看刚才在objects下生成的文件

git cat-file -t 3b18e512dba79e4c8300dd08aeb37f8e728b8dad # blob
git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad # hello world
复制代码

41.png


tree

接着我们将文件添加到版本库中


git commit -m firstcommit
复制代码


可以发现objects下面生成了两个文件

5ce458a111f86b77eb9399931b0391d27f75cfacebaa691b5554f29ac9d4f37811a1da6f24d376a1

.
├── 3b
│   └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
├── 5c
│   └── e458a111f86b77eb9399931b0391d27f75cfac
├── eb
│   └── aa691b5554f29ac9d4f37811a1da6f24d376a1
├── info
└── pack
复制代码


我们先看看ebaa691b5554f29ac9d4f37811a1da6f24d376a1的内容

git cat-file -t ebaa691 # tree
复制代码

因为SHA1可以通过前7位确定寻找到唯一值,所以我们平常工作中都可以使用简短ID即前7位


区别于前面的blob类型,这边新出现一个tree类型(实际其内容仍然是通过二进制保存)

git cat-file -p ebaa691 
# 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad  a.txt
复制代码


tree文件实际对应当前commit操作时的暂存区快照。因为我们只添加了一个文件,所以只会输出一行内容,也就是a.txt相关信息。从左至右依次为文件权限-文件类型-文件哈希-文件名

值得注意的是tree文件保存了文件的文件名而blob仅仅保存了文件内容。这样做的好处是,在通常情况下文件的内容是比目录结构大得多的情况下。当文件名修改时,我们仅仅需要生成新的tree文件,而不用因为文件名的修改而更新blob。


42.png

commit

我们接着看另外一个文件


git cat-file -t 5ce458a # commit
复制代码


出现新的文件类型commit

git cat-file -p 5ce458a
复制代码


其文件内容包括刚才生成的treeID,提交用户相关信息及提交信息。因为我们这是第一次提交,所以不会有parentId。在之后的commit文件中则会在parentId中保存上次的commitId。

tree ebaa691b5554f29ac9d4f37811a1da6f24d376a1
author xxx <xxx@qq.com> 1643509265 +0800
committer xxx <xxx@qq.com> 1643509265 +0800
firstcommit
复制代码


43.png

小结

通过上面的实践,我们认识了Git版本库中三种文件类型

类型 存储信息
blob 文件内容
tree 目录快照(包括文件权限,文件类型,文件ID,文件名)
commit 版本信息(包括提交用户信息,treeID,parentID,提交信息)

以上文件的相同点在于



  1. 存储在objects,通过前两位哈希值进行分桶

  2. 文件实际类型都为blob文件

  3. 文件名都是通过SHA1哈希得到

版本控制


前面我们分析了在版本库中不同文件类型及其保存的内容。在其基础上,我们可以分析出Git版本控制的大致流程。我们通过实例来分析


  1. 在工作区添加内容

echo 'a' > a.txt
echo 'b' > b.txt
复制代码

44.png

  1. 将工作区内容添加到暂存区,在版本库中生成blob文件
git add .
复制代码

45.png

  1. 对暂存区生成快照tree文件
git commit -m first
复制代码

46.png

  1. 生成第一个版本commit文件,其文件名则是我们平常使用的commitID

47.png


  1. 我们更新a.txt
echo a2 > a.txt
复制代码

48.png


  1. 将更新同步到暂存区
git add a.txt
复制代码

49.png


  1. 生成新的版本
git commit -m second
复制代码

50.png

抛出几个思考


  1. 会不会有重复的commitID


在我们上面分析版本实现的逻辑上,实际可以发现版本库实际实现了一颗哈希树。在哈希树中,其哈希值通过子节点进行哈希算法得到且不会重复。


  1. 我们能否篡改某个commit的内容而不被发现?


实际Git是提供了命令让我们回退到中途某个版本进行修改的,但是我们修改内容后没法不被发现。同样因为哈希树的实现,当我们修改某个commit版本的时候,其comnitId肯定是会更改的,此时前后面对应的commit文件中保存的parentId会改变,所以此时参与哈希计算的内容(parentId)实际会改变导致后面版本的commitID都会跟着改变。


  1. 对于修改内容,版本中是保存增量还是全量数据?


通过上文的分析,实际已经可以知道答案。Git中保存了每次更新后的全量文件,这也是为什么我们切换版本或者切换分支会非常快的原因,省去了遍历不同版本应用patch的操作。当然每次保存更新后的全量文件会使我们的仓库变得非常大,而Git相应的会对仓库下的文件进行打包压缩来减小仓库体积,打包后的结果放在前文所说的pack文件夹下面。


结语


本篇文章主要分析了Git仓库的存储机制,其是如何保存不同版本的内容


参考



相关文章
|
8月前
|
安全 Linux 开发工具
轻松掌握Git开发(一)基本概念的介绍
轻松掌握Git开发(一)基本概念的介绍
|
12月前
|
开发工具 git
软件开发入门教程网之 Git 分支管理
几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。
|
开发工具 git
深入Git-下篇
前言 在前面的几篇文章主要介绍了一些原理性的东西。本篇文章作为收尾,将介绍一些在日常工作中比较有用的命令以及解决一些经常出现的问题场景。大家可以结合git常用命令(熟练了就是高手)进行学习。
|
数据可视化 Shell 开发工具
深入Git-上篇
前言 作为一个开发者,想必大家都清楚Git。无论大家在工作中是使用命令行还是可视化工具来操作Git,应该都已经熟悉和掌握其基本的使用。而本系列文章的目的在于向大家介绍Git背后的基本原理,例如其如何实现不同版本的代码保存及版本切换。此外,还会向大家介绍一些好用的命令,解决在平常使用中的困惑。
|
PHP 开发工具 git
软件开发入门教程网之Git 分支管理
几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。 有人把 Git 的分支模型称为必杀技特性 ,而正是因为它,将 Git 从版本控制系统家族里区分出来。
软件开发入门教程网之Git 分支管理
|
监控 开发工具 git
Git 版本控制,看这篇就够了 (二)基础篇
Git 版本控制,看这篇就够了 (二)基础篇
Git 版本控制,看这篇就够了 (二)基础篇
|
Unix Linux Shell
Git 版本控制,看这篇就够了 (一)
Git 版本控制,看这篇就够了 (一)
Git 版本控制,看这篇就够了 (一)
|
存储 开发工具 git
GIT详细基础笔记(1)
GIT详细基础笔记
138 0
GIT详细基础笔记(1)
|
开发工具 git
Git简明教程
入门 使用Git前,需要先建立一个仓库(repository)。您可以使用一个已经存在的目录作为Git仓库或创建一个空目录。 使用您当前目录作为Git仓库,我们只需使它初始化。
81 0

相关实验场景

更多