您有一篇git 原理,请注意查收(二)

简介: 您有一篇git 原理,请注意查收(二)

3. 新增一个文件

现在,我们已经了解了.git目录中初始文件的情况,让我们执行第一个将内容添加到.git目录的操作。我们将创建一个文件并将其添加到暂存区(但还没有提交)。

echo 'hello,789' > file
git add file

我们继续使用erd -y inverted .git来查看文件变化。

image.png

git add file这会引起两个主要的更改。

  • 首先,它新增了索引文件(index)。Index用于存储有关当前暂存区的信息,用于表示名为file的文件已被添加到暂存区。
  • 第二个更为重要的更改是添加了一个新文件夹objects/c3,其中包含一个名为dc8e6cf3e1117a8d9731ddde9916da644296aa的文件。这是Git中存储对象的部分。

窥探objects中信息

我们使用file来查看一下内容是何方神圣。

file .git/objects/c3/dc8e6cf3e1117a8d9731ddde9916da644296aa
.git/objects/c3/dc8e6cf3e1117a8d9731ddde9916da644296aa: zlib compressed data

结果显示这是经过zlib压缩的数据。这就很让人抓马。你有张良计,我有过墙梯,我们可以使用zlib库对其解压处理。

zlib-flate -uncompress < .git/objects/c3/dc8e6cf3e1117a8d9731ddde9916da644296aa
blob 10hello,789

结果显示它包含了文件名为file的文件的类型大小数据

也就是说,/c3/dc8e6cf3e1117a8d9731ddde9916da644296aa表示它是一个大小为10的blob,内容是hello,789的数据。(只不过是被zlib处理了)

上面提到的/c3/dc8e6cf3e1117a8d9731ddde9916da644296aa这是Git对象的一部分,用于存储文件内容。

注意,此时我们用到了zlib库,我们可以通过brew install zlib下载该库。(我是Mac环境,其他环境大家自行寻找解决方案)


文件名的由来

文件名来自内容的SHA-1 hash值。

如果我们将zlib压缩的数据通过sha1sum命令处理,我们会得到文件名。

$ zlib-flate -uncompress <.git/objects/c3/dc8e6cf3e1117a8d9731ddde9916da644296aa | sha1sum
c3dc8e6cf3e1117a8d9731ddde9916da644296aa 

Git使用内容的SHA-1散列值,取前两个字符(在这种情况下是c3),创建一个文件夹,然后将剩余部分用作文件名Git从前两个字符创建文件夹,以确保我们不会在单个objects文件夹下有太多文件。

Mac环境下,我们需要通过brew install md5sha1sum


git cat-file

由于objects的内容在Git中比较重要,Git还特意提供了一个名为git cat-file的命令,用于查看git对象的内容。 使用git cat-file命令

  • 带有-t选项查看类型(type)
  • 带有-s选项查看大小(size)
  • 带有-p选项查看内容(pretty-print)
  • 这个选项用于显示 Git 对象的内容,以更易读的方式呈现,通常用于查看提交、树或标签对象的内容
git cat-file -t c3dc8e6cf3e1117a8d9731ddde9916da644296aa
blob
git cat-file -s c3dc8e6cf3e1117a8d9731ddde9916da644296aa
10
git cat-file -p c3dc8e6cf3e1117a8d9731ddde9916da644296aa
hello,789

4. git commit

既然,已经将内容通过git add 添加到Index暂存区),接下来我们就需要将内容commitlocal repository:(本地仓库)

前面讲过,下面的ci等同于commit

git ci -m '首次提交'

继续使用erd -y inverted .git 来查看目录结构


image.png

嚯,一下多了很多文件。让我们来解读一下。

首先是一个新文件COMMIT_EDITMSG,它包含了(最新的)提交消息。

如果我们运行git ci命令而没有使用-m标志,那么Git获取提交消息的方式是打开一个文本编辑器,使用COMMIT_EDITMSG文件来让用户编辑提交消息。一旦用户更新了消息并退出编辑器,Git就会使用该文件的内容作为提交消息。

它还添加了一个全新的logs文件夹。这是Git用来记录仓库中所有提交更改的一种方式。我们将能够在这里看到所有refsHEAD的提交更改

refs/heads目录,其中新增了一个名为master的文件。这是对主分支(master)的引用。

使用cat查看对于的内容信息。

cat .git/refs/heads/master
fe010b33df5078cdbd96f2397aad60ec5f42a967

看起来它指向了其中一个新对象。我们用内置命令cat-file查看内容。

$ git cat-file -t fe010b33df5078cdbd96f2397aad60ec5f42a967
commit
$ git cat-file -p fe010b33df5078cdbd96f2397aad60ec5f42a967
tree 658524b859ae78d902597253a3b68b4da3b70466
author xxx <xxx@simple> 1697178492 +0800
committer xxx <xxx@xxx> 1697178492 +0800
首次提交

我们也可以使用以下命令查看该引用的类型:git cat-file -t refs/heads/master

看起来这是一种新的对象类型,似乎是一个提交对象commit object)。提交对象的内容告诉我们,它包含一个哈希为658524b859ae78d902597253a3b68b4da3b70466树对象tree object),这看起来就像我们在提交时添加的另一个对象。提交对象还包含了作者提交者的信息。最后,它还显示了这个提交的提交消息是什么。

我们继续来看看树对象包含了什么内容。

git cat-file -t 658524b859ae78d902597253a3b68b4da3b70466
tree
git cat-file -p 658524b859ae78d902597253a3b68b4da3b70466
100644 blob c3dc8e6cf3e1117a8d9731ddde9916da644296aa    file

我们会发现该文件指向了在我们执行git add file时添加的原始对象(c3dc8e6cf3e1117a8d9731ddde9916da644296aa)。

image.png

树对象内部使用更多的树对象来表示文件夹,这些树对象提交对象相连,用于表示目录结构。


5. 新增修改

让我们对文件进行更改并查看它是如何工作的。

echo 'git,hello,789' > file
git ci -am "修改文件内容"

还是利用erd查看文档目录

image.png

  • 创建了3个新的对象。
  • 一个包含文件新内容的blob对象
  • 一个是一个树对象
  • 最后一个是一个提交对象

让我们再次从HEADrefs/heads/master开始跟踪它们。

git cat-file -p refs/heads/master
tree 02185c57f2040abcaa0c67dfd7026464d916da2b
parent fe010b33df5078cdbd96f2397aad60ec5f42a967
author 789 <789@xx.net> 1697179597 +0800
committer 789 <789@xxx.net> 1697179597 +0800
修改文件内容
git cat-file -p 02185c57f2040abcaa0c67dfd7026464d916da2b
100644 blob 1f9224976e282aa9e32398a5bca0cec08041f1dc    file
git cat-file -p 1f9224976e282aa9e32398a5bca0cec08041f1dc
git,hello,789

提交对象现在有一个额外的属性,名为parent,它链接到前一个提交,因为这个提交是建立在前一个提交之上的。

这是Git中的提交历史的关键概念,

每个提交都有一个或多个父提交,形成一个提交链。


6. 创建分支

是时候创建一个分支了。让我们使用git br fix-text命令创建一个名为fix-text的分支。

前面讲过,下面的br等同于branch

image.png

这将在refs/heads文件夹下创建一个新文件,文件名为分支名称,文件内容为最新提交的ID。

我们首先用git log查看提交记录

image.png

发现最新的提交记录efa223e697c6452a393963887f9926ea0662c923

cat .git/refs/heads/fix-text
efa223e697c6452a393963887f9926ea0662c923

在Git中,分支是非常轻量级的。标签(Tags)的行为也类似,只不过它们是创建在refs/tags下的。

还会在logs目录下添加一个文件,用于存储与主分支类似的提交历史数据。这有助于跟踪各个分支的提交历史。Git的分支和标签是非常有用的版本控制工具,可以帮助我们管理项目的不同状态和版本。


7. 分支切换

Git中,检出(checkout)操作是获取提交树对象,并将working tree中的文件更新为与树对象记录的状态相匹配。

image.png

在这种情况下,因为我们从master切换到fix-text,而这两个分支都指向相同的提交和底层树对象,Git在working tree中没有任何事情要处理。

前面讲过,下面的co等同于checkout

git co fix-text 

.git目录内执行co操作时,唯一的变化是.git/HEAD文件现在将指向fix-text

cat .git/HEAD
ref: refs/heads/fix-text

另外,让我进行一次提交。

echo 'hello,git' > file
git ci -am "更换文本内容"

这将在fix-text分支上创建一个新的commit,将文件file中的内容更改为hello,git


8. 分支合并

合并(merging)有主要三种方式。

  1. 最简单和最容易的方式是快进合并fast forward merge
  • 在这种情况下,我们只需将一个分支指向另一个分支指向的commit object
  • 这实际上涉及将refs/heads/fix-text中的hash复制到refs/heads/master
  1. 第二种方式是变基合并rebase merge
  • 在这种情况下,我们首先逐个将我们的更改应用到主分支(main或master)当前指向的每个提交,然后执行类似于快进合并的操作。
  1. 最后一种方式是通过创建一个独立的合并提交来合并两个分支。
  • 这在于它将在其提交对象中有两个父节点(parent entries)。

首先,让我们看看在合并之前图形是什么样子。(先将分支切换回master(git co master))

git log --graph --oneline --all
* 4359ab4 (fix-text) 更换文本内容
* efa223e (HEAD -> master) 修改文件内容
* fe010b3 首次提交

执行合并操作

git merge fix-text
更新 efa223e..4359ab4
Fast-forward
 file | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

上面的操作,将一个master分支的引用(指向的哈希值)更新为fix-text分支的引用指向的哈希值。

git log --graph --oneline --all
* 4359ab4 (HEAD -> master, fix-text) 更换文本内容
* efa223e 修改文件内容
* fe010b3 首次提交

9. 远程提交

为了演示这一点,首先让我创建另一个Git仓库,它可以作为这个仓库的远程仓库

新建一个仓库

$ mkdir fake_git_remote
$ cd fake_git_remote && git init --bare

切换到我们dot_git项目中,为仓库设置remote

git remote add origin ../fake_git_remote

顺便说一下,添加一个新的远程仓库是一项配置更改,我们可以在.git/config文件中查看这个更改。我会让我们自己去查看这个更改是什么。

image.png

现在让我们进行推送操作。

git push origin master

让我们看看我们的本地仓库中发生了什么变化。

image.png

它添加了一个新的refs/remotes,用于存储有关不同远程仓库中的所有可用内容的信息。

但是发送到另一个Git仓库的是什么呢?实际上,

发送的内容就是.git/objects目录中的所有对象,以及我们显式推送的refs下的所有分支和标签。

这就是另一个Git仓库需要获取我们的完整Git历史记录所需的一切内容。


image.png


后记

分享是一种态度

全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。

相关文章
|
5月前
|
存储 开发工具 git
Git的基本操作和原理
Git的基本操作和原理
|
2月前
|
存储 开发工具 数据库
Git的工作原理是什么
【8月更文挑战第24天】Git的工作原理是什么
33 0
|
4月前
|
前端开发 持续交付 开发工具
详细介绍Git的基本原理、在前端开发中的应用以及如何使用Git来优化团队协作
【6月更文挑战第14天】Git是前端开发中的必备工具,它通过分布式版本控制管理代码历史,支持分支、合并和冲突解决,促进团队协作。在前端开发中,Git用于代码追踪、版本控制、代码审查和持续集成部署,优化团队协作。制定分支策略、编写清晰提交信息、定期合并清理分支以及使用Git钩子和自动化工具能进一步提升效率。理解并善用Git,能有效提升前端项目的质量和开发效率。
62 3
|
5月前
|
运维 测试技术 开发工具
Git 的原理与使用(下)(二)
新特性或新功能开发完成后,开发人员需合到 develop 分支。
50 2
|
5月前
|
Java 网络安全 开发工具
Git 的原理与使用(中)(三)
别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
38 1
|
5月前
|
存储 安全 开发工具
Git 的原理与使用(中)(二)
Fast Forward 模式(ff模式) 通常合并分支时,如果可以,Git 会采用 Fast forward 模式。
31 1
|
5月前
|
安全 Java 开发工具
Git 的原理与使用(中)(一)
分支是Git的杀手级功能之一。
34 1
|
5月前
|
存储 算法 开发工具
Git 的原理与使用(上) (二)
如果直接将某个文件拷贝到 .git 文件的同级目录gitcode下,此时这个文件是不会被Git管理的。
42 1
|
5月前
|
Linux 开发工具 git
Git 的原理与使用(下)(一)
在完成origin/dev分支合并到origin/master分支的操作后,origin/dev分支对于我们来说就没用了,那么dev分支就可以被删除掉。
48 0
|
5月前
|
Unix Linux 开发工具
Git 的原理与使用(上) (一)
Git是一个分布式版本控制系统,它被广泛用于协作开发和管理软件项目。开发人员可以通过Git来跟踪文件的变化、协调工作、并管理项目的不同版本。
29 0