优秀的编程知识分享平台

网站首页 > 技术文章 正文

深入了解Git

nanyue 2024-12-11 16:11:17 技术文章 8 ℃

Git内部如何工作以组织数据和历史记录

大多数开发人员都熟悉版本控制系统,而在大多数情况下git实际上是选择。 本文实际上是介绍git的工作原理,这将使开发人员可以更好地理解git。

Git是分布式版本控制,用于存储文件和目录更改的历史记录,以便开发人员可以穿越时间了解所有更改的发生方式。 最初,git是由Linux创建者创建的,用于在有多个开源开发人员时管理Linux内核开发。

在Linux手册页(man git)中,Git被标识为"愚蠢的内容跟踪器"。 通过本文,您可以确定为什么要这样调用git。


Git的"Porcelain"和"Plumbing"命令

开发人员工作流程中使用的大多数命令被称为"Porcelain瓷器命令"。 但是这些命令是通过称为"管道命令"的低级命令构造的。 让我们举个例子:

· Porcelain命令→git add,git commit,git push,git pull,git branch,git checkout,git tag,git merge,git rebase等…

· Plumbing命令→git cat-file,git has-object,git count-objects

了解哈希和SHA-1

即使大家都熟悉哈希,也需要总结一下才能使本文完整。 通过加密函数为某些输入生成哈希值。 Git使用SHA-1算法。

这个简单的函数返回哈希值,以git计算。 它在stdin输入上执行git hash-object。

echo 'aaa' | git hash-object --

stdin→ 72943a16fb2c8f38f9dde202b7a70ccc19c52f34

同样,哈希值包括:

"aaa" → 72943a16fb2c8f38f9dde202b7a70ccc19c52f34

"aab" → 6f27bcf7c99320f97e935dac870033e697bc5b11

"bbb" → f761ec192d9f0dca3329044b96ebdb12839dbff6

· 哈希值始终从任何输入生成40位十六进制数

· 相同的输入将始终生成相同的哈希值

· 输入的微小差异将完全改变哈希值

· 无法从哈希值识别输入,并且哈希函数不可逆

· 哈希值不是无限的,因为它限制为40位数字。 但是两个输入产生相同哈希的概率几乎为零,从而使哈希成为唯一的哈希。

Git目录

让我们使用git init初始化空的git仓库,您将看到在该位置创建了.git的隐藏目录。 让我们检查.git /:

branches/
config
description
HEAD
hooks/
info/
objects/
refs/

因为这些内部目录是刚刚启动的,所以大多数内部目录(例如branchs或objects /)将为空。 该.git将具有与git相关的文件,目录和其他信息的所有详细信息,稍后将进行讨论。

Git对象数据库

让我们检查一下哈希值如何存储在.git中的git数据库中。 要将其写入git数据库,我们可以使用更早的命令来计算哈希,但要使用-w。

echo 'aaa' | git hash-object --stdin -w

在这里,预期aaa以某种方式存储在git对象数据库中。

Git对象数据库实际上是持久化的键-值映射。 因此,对于任何元素,内容的哈希值将是关键,而值将是内容。

这里72943a16fb2c8f38f9dde202b7a70ccc19c52f34将是键,值将是aaa。 在编写此元素时,请检查将其放置在.git / objects中。 在哈希表72中将有一个包含前两个元素的目录,其中包含一个具有其余哈希值的文件:943a16fb2c8f38f9dde202b7a70ccc19c52f34。

user@ubuntu:~/git-internals/.git/objects/72$

ls 943a16fb2c8f38f9dde202b7a70ccc19c52f34

该文件的内容已加密,无法直接读取。 因此,有一个函数可用于从返回aaa的git数据库读取值。

git cat-file 72943a16fb2c8f38f9dde202b7a70ccc19c52f34 -p

这显示了如何从git objects数据库中写入和读取数据,以及如何将其组织为键-值映射,以及如何将其除以哈希值以提高性能。

Git对象

Git跟踪所有内容,并作为对象存储在git数据库中。 类型包括:

· Blob→将文件内容存储为原始二进制数据。

· Tree 树→已存储目录内容。

· Submit 提交→提交存储的内容。

· Tag 带注释的标签→已存储标签内容。

这些内容中的任何一个都可以用于计算哈希值,并且内容将存储在对象数据库中的哈希值中。 Git不会区分这些类型,而是将所有类型均等地存储在.git / objects中。 最好通过示例学习这些类型。

用于提交,文件和目录的Git对象

为了清楚地了解Git对象及其数据库,让我们创建一个文件并提交到git仓库中。

$ echo "aaa" >> a.txt

$ git add a.txt

$ git commit -m "Add a file"

通过git log标识最后一次提交的哈希值,然后从对象数据库中读取该值:git cat-file 239d1a0 -p。 (在大多数项目中,可使用7位数字标识git对象,称为短SHA)

tree 37057b2e8a9041ef88b805a5b7c4e0e668a03be4

author Thomas Shelby <user@gmail.com> 1588048341 +0530

committer Thomas Shelby <user@gmail.com> 1588048341 +0530

Add a file

用于提交的git对象包含作者,时间,消息和树。 这里的树值似乎是另一个哈希值,它表示提交时项目的状态。 让我们检查tree→git cat文件37057b2 -p的git对象。

100644 blob 72943a16fb2c8f38f9dde202b7a70ccc19c52f34 a.txt

这是指项目在基本目录中只有一个名为a.txt的blob(不是目录的文件)。 因此,可以从哈希值读取此文件的内容。

aaa

这将返回预期的a.txt内容。 在这里,通过commit我们可以通过从tree和blob对象导航来构造目录的全部内容。 因此,应该看到,从.git目录开始,所有信息都作为git对象从提交中构建工作目录。

在此初始提交之后,让我们检查git对象数据库:tree .git / objects

.git/objects/
├── 23
│   └── 9d1a0f75b596d7d67e23721f11066abf144982
├── 37
│   └── 057b2e8a9041ef88b805a5b7c4e0e668a03be4
├── 72
│   └── 943a16fb2c8f38f9dde202b7a70ccc19c52f34
├── info
└── pack

这具有引用提交,树和Blob git对象的所有3个对象。 git count-objects也可以用来标识git对象的数量。

添加顺序提交和嵌套目录/文件

它只有一个文件,没有目录。 让我们添加更多的Blob和树,以清楚地了解文件和目录在对象数据库中的存储方式。

$ mkdir files

$ echo "aaa" >> files/aaa.txt

$ echo "bbb" >> files/bbb.txt

$ git add .

$ git commit -m "Add files"

$ git log

故意为新文件添加了aaa内容。 让我们通过读取git对象→git cat文件5c45ebb -p来检查提交内容。

tree 21e6981939eae6277ad2128753e2984b552868cf

parent 239d1a0f75b596d7d67e23721f11066abf144982

重要的更改包括引用父提交的父属性。 然后观察由于文件和目录更改而树已更改。 让我们一步一步检查树和斑点的内容以构建文件和目录。

100644 blob 72943a16fb2c8f38f9dde202b7a70ccc19c52f34 a.txt

040000 tree 75d27669a0c4e9dd702c71c6ac3307d533493ba5 files

a.txt的Git对象与内容保持相同。 但是在父树对象中,存在另一棵树,该树表示其目录。 检查此嵌套树的内容时:git cat-file 75d2766 -p

100644 blob 72943a16fb2c8f38f9dde202b7a70ccc19c52f34 aaa.txt

100644 blob f761ec192d9f0dca3329044b96ebdb12839dbff6 bbb.txt

如果选中斑点,它将具有预期的内容。 请注意,这里的aaa.txt将具有与a.txt相同的对象,因为文件内容包含aaa。 但是,文件名更改并没有影响,因为它不是存储在blob本身中,而是存储在其包含的树中。

可以看出git如何在两次提交之间重用包含aaa的公共blob。 只有7个对象

.git/objects/
├── 21
│ └── e6981939eae6277ad2128753e2984b552868cf
├── 23
│ └── 9d1a0f75b596d7d67e23721f11066abf144982
├── 37
│ └── 057b2e8a9041ef88b805a5b7c4e0e668a03be4
├── 5c
│ └── 45ebb2052428ce037e2fbf760cb0ec9a18f6e2
├── 72
│ └── 943a16fb2c8f38f9dde202b7a70ccc19c52f34
├── 75
│ └── d27669a0c4e9dd702c71c6ac3307d533493ba5
├── f7
│ └── 61ec192d9f0dca3329044b96ebdb12839dbff6

从提交解释工作目录

当前有两个提交,用户可以检出这两个提交中的一个。 对于任何提交,其对文件和目录的观点可能有所不同。 对于提交,它将忽略连接的提交,并使用与其连接的树和Blob建立文件和目录。此处将显示两次提交的方式

编辑现有文件

先前的提交能够显示如何表示新添加的目录或文件。 在此提交中,它将显示文件编辑如何反映在git对象模型中:将根目录内容中的a.txt编辑为aab。

总结一下git对象,这些简单地引用如下:

· 树→表示目录。 包含包含树/斑点的文件结构

· Blob→表示文件。 包含加密的文件内容。

· 提交→表示提交。 包含作者和被引用的父树

· 标签→表示带注释的标签。 包含引用的提交。


git分支

默认情况下,git创建默认的master分支。 因此,此引用将使用.git存储在某个位置。 它实际上存储在.git / refs / heads / master中,而不是加密文件,因此可以读为cat .git / refs / heads / master。 这仅包含最后一次提交的哈希值。 因此,git分支只是对git commit hash值的引用。

让我们尝试添加分支并提交以检查引用的保留方式。

$ git checkout -b branch1 # create branch and switch to it$ cat .git/refs/heads/branch1 # refers to same commit as master$ nano a.txt # edit content of a.txt$ echo "ccc" >> c.txt # add new file of c.txt$ git add .$ git commit -m "branch1"

验证branch1引用已修改,并且现在正在引用新的提交。 当前分支存储在更少的.git / HEAD中,作为refs / heads / branch1。

让我们使用git checkout master签出回master分支,请注意,这将更改HEAD文件中的当前分支以及根据该提交构造文件和目录。 因此,请注意,删除了在新提交中添加的c.txt。

$ git checkout master # create branch and switch to it

$ nano a.txt # edit content of a.txt

$ git add .

$ git commit -m "master"

由于已经将其切换为master作为当前分支,因此不必在每次提交时都更新HEAD。 但是在每次提交时,对提交的master分支引用都会更新为新的提交。

用分支包起来:

· 分支→是提交的参考

· HEAD→是对分支的引用。 (如果将头部检出到提交的头部分离,则可以直接引用该提交。git checkout commit_id)

Git垃圾收集器

如图所示,git分支仅引用提交ID,因此可以更改git文件以进行提交而无需任何引用。 (注意:这是出于演示目的而进行的修改。)

cat .git/refs/heads/master > .git/refs/heads/branch1

这用主提交引用替换了branch1提交引用。

这里两个分支在3aefd7b中引用相同的提交,而在533503b中的提交未被分支,另一个提交或标记引用。

一段时间后,这些没有任何参考的git对象将被垃圾收集并删除。

正如"愚蠢的内容跟踪器"所建议的定义一样,可以看出git在项目的每个时间框架都跟踪文件和目录。 它具有在给定时间内完全构建工作目录的能力。 因此可以说git跟踪内容。

这是git如何在后台为最终用户管理高级命令的简要介绍。 这旨在阐明git如何管理文件,提交和分支。 正确理解git的工作原理也将有助于掌握高级git概念。


(本文翻译自Udara Bibile的文章《Understanding Git under the hood》,参考:https://medium.com/swlh/understanding-git-under-the-hood-b1aeae1d02f5)

Tags:

最近发表
标签列表