0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

探究Git基本原理(上)

jf_78858299 来源:分布式实验室 作者:分布式实验室 2023-05-12 15:20 次阅读

简单地说,Git 究竟是怎样的一个系统呢?请注意接下来的内容非常重要,若你理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余。

在学习 Git 时,请尽量理清你对其它版本管理系统已有的认识,如 CVS、Subversion 或 Perforce, 这样能帮助你使用工具时避免发生混淆。

尽管 Git 用起来与其它的版本控制系统非常相似, 但它在对信息的存储和认知方式上却有很大差异,理解这些差异将有助于避免使用中的困惑。

图片

Git 初始化代码仓库

执行完成了 git init 命令,究竟做了什么呢?

执行完成如下命令之后,我们可以得到下图所示的内容,右侧的就是 Git 为我们创建的代码仓库,其中包含了用于版本管理所需要的内容。

# 左边执行
$ mkdir git-demo
$ cd git-demo && git init
$ rm -rf .git/hooks/*.sample

# 右边执行
$ watch -n 1 -d find .

图片

我们这里可以一起看下生成的 .git 目录的结构如何:

➜ tree .git
.git
├── HEAD
├── config
├── description
├── hooks
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags

.git/config - 当前代码仓库本地的配置文件

  • 本地配置文件(.git/config)和全局配置文件(~/.gitconfig)
  • 通过执行如下命令,可以将用户配置记录到本地代码仓库的配置文件中去
  • git config user.name "demo"
  • git config user.email "demo@demo.com"
➜ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true

[user]
name = demo
email = demo@demo.com

.git/objects - 当前代码仓库代码的存储位置

  • blob 类型
  • commit 类型
  • tree 类型

# 均无内容
➜ ll .git/objects
total 0
drwxr-xr-x 2 escape staff 64B Nov 23 20:39 info
drwxr-xr-x 2 escape staff 64B Nov 23 20:39 pack

➜ ll .git/objects/info
➜ ll .git/objects/pack

.git/info - 当前仓库的排除等信息

➜ cat ./.git/info/exclude
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

.git/hooks - 当前代码仓库默认钩子脚本

./.git/hooks/commit-msg.sample
./.git/hooks/pre-rebase.sample
./.git/hooks/pre-commit.sample
./.git/hooks/applypatch-msg.sample
./.git/hooks/fsmonitor-watchman.sample
./.git/hooks/pre-receive.sample
./.git/hooks/prepare-commit-msg.sample
./.git/hooks/post-update.sample
./.git/hooks/pre-merge-commit.sample
./.git/hooks/pre-applypatch.sample
./.git/hooks/pre-push.sample
./.git/hooks/update.sample

.git/HEAD - 当前代码仓库的分支指针

➜ cat .git/HEAD
ref: refs/heads/master

.git/refs - 当前代码仓库的头指针

# 均无内容
➜ ll .git/refs
total 0
drwxr-xr-x 2 escape staff 64B Nov 23 20:39 heads
drwxr-xr-x 2 escape staff 64B Nov 23 20:39 tags

➜ ll .git/refs/heads
➜ ll .git/refs/tags

.git/description - 当前代码仓库的描述信息

➜ cat .git/description
Unnamed repository; edit this file 'description' to name the repository.

add 之后发生了什么

执行完成了 git add 命令,究竟做了什么呢?

执行完成如下命令之后,我们可以得到下图所示的内容,我们发现右侧新增了一个文件,但是 Git 目录里面的内容丝毫没有变化。这是因为,我们现在执行的修改默认是放在工作区的,而工作区里面的修改不归 Git 目录去管理。

而当我们执行 git status 命令的时候,Git 又可以识别出来现在工作区新增了一个文件,这里怎么做到的呢?—— 详见[理解 blob 对象和 SHA1]部分

而当我们执行 git add 命令让 Git 帮助我们管理文件的时候,发现右侧新增了一个目录和两个文件,分别是 8d 目录、index 和 0e41.. 文件。

# 左边执行
$ echo "hello git" > helle.txt
$ git status
$ git add hello.txt

# 右边执行
$ watch -n 1 -d find .

图片

图片

我们这里重点看下,生成的 8d 这个目录以及下面的文件。而其名称的由来是因为 Git 对其进行了一个叫做 SHA1 的 Hash 算法,用于将文件内容或者字符串变成这么一串加密的字符。

# 查看 objects 的文件类型
$ git cat-file -t 8d0e41
blob

# 查看 objects 的文件内容
$ git cat-file -p 8d0e41
hello git

# 查看 objects 的文件大小
$ git cat-file -s 8d0e41
10

# 拼装起来
blob 10\\0hello git

现在我们就知道了,执行 git add 命令将文件从工作区添加到暂存区里面,Git 会把帮助我们生成一些 Git 的对象,它存储的是文件的内容和文件类型并不存储文件名称。

微信搜索公众号:架构师指南,回复:架构师 领取资料

为了验证我们上述的说法,我们可以添加同样的内容到另一个文件,然后进行提交,来观察 .git 目录的变化。我们发现,右侧的 objects 目录并没有新增目录和文件。这就可以证明,blob 类型的 object 只存储的是文件的内容,如果两个文件的内容一致的话,则只需要存储一个 object 即可。

话说这里 object 为什么没有存储文件名称呢?这里因为 SHA1 的 Hash 算法计算哈希的时候,本身就不包括文件名称,所以取什么名称都是无所谓的。那问题来了,就是文件名的信息都存储到哪里去了呢?—— 详见[理解 blob 对象和 SHA1]部分

# 左边执行
$ echo "hello git" > tmp.txt
$ git add tmp.txt

# 右边执行
$ watch -n 1 -d find .

图片

理解 blob 对象和 SHA1

了解 Git 的 blob 对象和 SHA1 之前的关系和对应计算!

Hash 算法是把任意长度的输入通过散列算法变化成固定长度的输出,根据算法的不同,生成的长度也有所不同。

Hash 算法:

  • MD5 - 128bit - 不安全 - 文件校验
  • SHA1 - 160bit(40位) - 不安全 - Git 存储
  • SHA256 - 256bit- 安全 - Docker 镜像
  • SHA512 - 512bit - 安全

但是,当我们使用工具对上述文件内容进行 SHA1 计算的时候,会发现并没有我们在 .git 目录里面看到的那样,这是为什么呢?

echo "hello git" | shasum
d6a96ae3b442218a91512b9e1c57b9578b487a0b -

这里因为 Git 工具的计算方式,是使用类型 长度 \\0 内容的方式进行计算的。这里,我们算了下文件内容只有九位,但是这里是十位,这里因为内容里面有换行符的存在导致的。现在我们就可以使用 git cat-file 命令来拼装 Git 工具存储的完整内容了。

➜ ls -lh hello.txt
-rw-r--r-- 1 escape staff 10B Nov 23 21:12 hello.txt

echo "blob 10\\0hello git" | shasum
8d0e41234f24b6da002d962a26c2495ea16a425f -

# 拼装起来
blob 10\\0hello git

图片

当我们使用 cat 命令来查看 object 对象里面的内容的时候,发现看着像是一串乱码。其实这是 Git 工具将文件的原始内容进行一个压缩,然后再存储到 object 对象里面。奇怪的是,我们发现压缩之后的内容反而比原始内容还大!

这是因为其进行了压缩,存储了一些压缩相关的信息。上例所示的比原始文件大,是因为我们创建的内容实在是太小了。当我们常见一个比较大的文件时,就会看到压缩之后的文件大小远小于原始文件的。

➜ cat .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f
xKOR04`HWH,6A%

➜ ls -lh .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f
-r--r--r-- 1 escape staff 26B Nov 23 21:36 .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f

➜ file .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f
.git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f: VAX COFF executable not stripped - version 16694

其实,我们这里也是可以通过 Python 代码来获取二进制 object 对象的内容的。

import zlib

contents = open('0e41234f24b6da002d962a26c2495ea16a425f', 'rb').read()
zlib.decompress(contents)

图片

聊聊工作区和暂存区

聊聊工作区和暂存区,以及文件如何在工作区和缓存区之间同步的问题。

之前的章节我们也聊到了,当我们执行 git status 命令的时候,Git 工具怎么知道我们有一个文件没有追踪,以及文件名的信息都存储到哪里去了?

这一切的答案,都要从工作区和索引区讲起。Git 根据其存储的状态不同,将对应状态的“空间”分为工作区、暂存区(也可称为索引区)和版本区三类。具体示例,可以参考下图。

图片

而更加深层次的理解,就要从执行 git add 命令后生成相关的 object 对象,但是其存储的是文件的类容、大小和内容,并不包含文件名称的信息。而文件名称相关的信息就包含在生成的 index 文件(索引文件)里面。

当我们直接查看 index 文件里面的内容,发现使我们无法理解的乱码,但是通过基本的输出,我们可以看到其文件名称。要想查看 index 文件的内容,可以通过 Git 提供的相关命令进行查看。

# 左边执行
$ echo "file1" > file1.txt
$ git add file1.txt
$ cat .git/index

$ git ls-files # 列出当前暂存区的文件列表信息
$ git ls-files -s # 列出当前暂存区文件的详细信息

# 右边执行
$ watch -n 1 -d tree .git

图片

当添加文件的时候,文件或目录会从工作区流向暂存区,加之一些其他操作,会导致工作区和暂存区是会有一定差别的。这就会导致,当我们执行 git status 的结果就是两者的差别。

经过如下操作,会使工作区和暂存区和的内容不一致了,通过命令我们也是可以查看区别的。当我们使用 add 命令将新文件添加到暂存区的时候,会发现这下就一致了。

# 左边执行
$ git status
$ echo "file2" > file2.txt
$ git ls-files -s
$ git status
$ git add file2.txt
$ git ls-files -s
$ git status

# 右边执行
$ watch -n 1 -d tree .git

图片

如果我们这里去修改一个文件的话,很显然这个时候我们的工作区和暂存区又不一致了。当我们使用命令去查看文件状态的时候,发现一个文件被修改了,而 Git 是怎么知道的呢?咳咳,就是通过查找 index 文件的内容,找到对应文件名称以及其内部引用的 object 对象,与工作区的文件内容进行对比而来的。

# 左边执行
$ git ls-files -s
$ echo "file.txt" > file1.txt
$ git status

# 右边执行
$ watch -n 1 -d tree .git

图片

而这个时候,我们再使用 git add 命令将其修改内容保存至暂存区的话,会发现对应文件的 object 的 blob 对象的引用值发生改变了。这时可以发现,objects 目录下面有三个对象了,其中 file1.txt 占了两个,但是文件却只有两个。通过命令查看对应 blob 对象的内容,发现各有不同。

# 左边执行
$ git ls-files -s
$ git add file1.txt
$ git ls-files -s

# 右边执行
$ watch -n 1 -d tree .git

图片

理解 commit 提交原理

执行完成了 git commit 命令,究竟做了什么呢?

Git 仓库中的提交记录保存的是你的目录下所有文件的快照,就像是把整个目录复制,然后再粘贴一样,但比复制粘贴优雅许多!Git 希望提交记录尽可能地轻量,因此在你每次进行提交时,它并不会盲目地复制整个目录。条件允许的情况下,它会将当前版本与仓库中的上一个版本进行对比,并把所有的差异打包到一起作为一个提交记录。Git 还保存了提交的历史记录。这也是为什么大多数提交记录的上面都有父节点的原因。

当我们使用 add 命令将工作区提交到暂存区,而暂存区其实保存的是当前文件的一个状态,其中包括有哪些目录和文件,以及其对应的大小和内容等信息。但是我们最终是需要将其提交到代码仓库(本地)的,而其命令就是 git commit 了。

图片

而当我们执行 git commit 命令的时候,究竟都发生了什么呢?可以看到当提交之后,.git 目录中生成了两个信息的 object 对象,其中 logs 和 refs 目录都有新的文件生成。通过如下操作,我们可以查看到其提交的类型和对应内容。

# 左边执行
$ git commit -m "1st commit"

$ git cat-file -t 6e4a700 # 查看 commit 对象的类型
$ git cat-file -p 6e4a700 # 查看 commit 对象的内容

$ git cat-file -t 64d6ef5 # 查看 tree 对象的类型
$ git cat-file -p 64d6ef5 # 查看 tree 对象的内容

# 右边执行
$ watch -n 1 -d tree .git

图片

这样我们就理解了,当我们执行 git commit 命令之后,会生成一个 commit 对象和一个 tree 对象。commit 对象内容里面包含了一个 tree 对象和相关提交信息,而 tree 对象里面则包含了这次我们提交版本里面的文件状态(文件名称和 blob 对象),这样我们就知道了这次提交的变动了。

图片

我们这次提交之后,处理 objects 目录发生变动之外,还有一些其他的变化。比如 logs 和 refs 的目录有所变化。我们查看 refs 目录里面的内容,发现其指向了 6e4a70 这个 commit 对象,即当前 master 分支上面最新的提交就是这个 6e4a70 了。

而这个 6e4a70 这个 commit 对象,有一个 HEAD 的指向,就是 .git 目录下的 HEAD 文件。其实质就是一个指针,其永远指向我们当前工作的分支,即这里我们工作在 master 分支上。当我们切换分支的时候,这个文件的指向也会随机改变的。

# 左边执行
$ cat .git/refs/heads/master
$ cat .git/HEAD

# 右边执行
$ watch -n 1 -d tree .git

图片

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • CVS
    CVS
    +关注

    关注

    0

    文章

    14

    浏览量

    10984
  • Git
    Git
    +关注

    关注

    0

    文章

    198

    浏览量

    15755
  • 版本管理
    +关注

    关注

    0

    文章

    7

    浏览量

    160
收藏 人收藏

    评论

    相关推荐

    步进电机基本原理

    本帖最后由 eehome 于 2013-1-5 09:48 编辑 步进电机基本原理
    发表于 08-16 16:17

    串联谐振逆变器的基本原理

    串联谐振通常伴有逆变器。该组合称为串联谐振逆变器。什么是基本原理?让我简要介绍串联谐振逆变器的一些基本原理。]首先给你看一张图片:
    发表于 11-07 10:21

    IC测试基本原理是什么?

    IC测试基本原理是什么?ATE测试向量是什么?
    发表于 05-07 06:43

    IC测试的基本原理是什么?

    本文详细介绍了芯片开发和生产过程中的IC测试基本原理
    发表于 05-08 07:33

    电机转动的基本原理是什么?

    电机转动的基本原理是什么?电机运动的基本原则有哪些?
    发表于 07-21 07:59

    线性电源的基本原理是什么

    多路线性电源 AC-DC稳压电源 低纹波电源 可调线性电源 原理图PCB目录多路线性电源 AC-DC稳压电源 低纹波电源 可调线性电源 原理图PCB基本原理芯片选型原理图&3D-PCB具体
    发表于 07-30 07:47

    SPWM的基本原理

    基本原理SPWM的全称是(Sinusoidal PWM),正弦脉冲宽度调制是一种非常成熟,使用非常广泛的技术;之前在PWM的文章中介绍过,基本原理就是面积等效原理,即冲量相等而形状不同的窄脉冲加在
    发表于 09-06 08:13

    无线充电的基本原理是什么

    一 、无线充电基本原理无线充电的基本原理就是我们平时常用的开关电源原理,区别在于没有磁介质耦合,那么我们需要利用磁共振的方式提高耦合效率,具体方法是在发送端和接收端线圈串并联电容,是发送线圈处理谐振
    发表于 09-15 06:01

    RAID技术的基本原理是什么

    RAID技术的基本原理是什么?RAID技术有哪几个优势?
    发表于 10-14 12:01

    IIC的基本原理是什么?

    IIC的基本原理是什么?
    发表于 11-25 08:46

    串口通信的基本原理是什么?

    同步通信和异步通信的区别是什么?串口通信的基本原理是什么?
    发表于 12-13 06:46

    步进马达基本原理

    步进马达基本原理步进马达基本原理步进马达基本原理
    发表于 11-30 11:55 8次下载

    探究Git基本原理(下)

    简单地说,Git 究竟是怎样的一个系统呢?请注意接下来的内容非常重要,若你理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余。 在学习 Git 时,请尽量理清你对其它版本管理
    的头像 发表于 05-12 15:20 598次阅读
    <b class='flag-5'>探究</b><b class='flag-5'>Git</b><b class='flag-5'>基本原理</b>(下)

    6.4.2.1 基本原理∈《碳化硅技术基本原理——生长、表征、器件和应用》

    6.4.2.1基本原理6.4.2n型和p型SiC的欧姆接触6.4金属化第6章碳化硅器件工艺《碳化硅技术基本原理——生长、表征、器件和应用》往期内容:6.4.1.2SiC的肖特基接触∈《碳化硅技术
    的头像 发表于 01-24 10:09 1652次阅读
    6.4.2.1 <b class='flag-5'>基本原理</b>∈《碳化硅技术<b class='flag-5'>基本原理</b>——生长、表征、器件和应用》

    密码学基本原理()

    电子发烧友网站提供《密码学基本原理().pdf》资料免费下载
    发表于 08-02 09:13 1次下载
    密码学<b class='flag-5'>基本原理</b>(<b class='flag-5'>上</b>)