Git的机制

一旦了解了.git文件夹内部的工作原理,突然之间就能非常清楚地理解git命令了。简单概括笔记如下:

(↓ 这是最易懂的文章大家)

[https://www.slideshare.net/DQNEO/git-yapcasia2013] 的相关幻灯片。

请将以下内容用中文进行简洁的表达(内容源自[https://yakst.com/ja/posts/3811]):

请将以下内容以中文进行表达:

[http://koseki.hatenablog.com/entry/2014/04/22/inside-git-1]

简介

首先列出两个成为里程碑的事项。

    1. 当git commit时,会完整记录整个文件树的快照,而不只是记录与上一个提交点的差异(我之前误解了这一点)。git使用“对象”来表示各种信息。其中有以下4种对象类型:

blog object:包含文件信息(例如:app/foo/bar.txt)
tree object:包含目录信息(例如:app/foo)
commit object:包含提交信息
tag object:包含标签信息

对象具有ID。所谓的提交ID是指commit对象的ID。

四个目标 (sì gè

想象存在下列具体的提交日志的情况。

% git log --oneline --graph
* 8bab622 (HEAD -> dev, master) add devdev
* 75decf1 test
* f8f5ce7 first commmit

提交对象

可以以以下方式查看id为8bab622的提交内容。

% git cat-file -p 8bab622
tree f23ca64f4ace0ab87e9b90e5a02c7d32aa197ca0
parent 75decf137844a74cac913fd0432e20a60c98dd2f
author takashi suzuki <takashi@takashinoMacBook-puro.local> 1561743875 +0900
committer takashi suzuki <takashi@takashinoMacBook-puro.local> 1561743875 +0900

add devdev

这个表示了提交的内容。

作者、提交者和提交信息我都明白,但是 parent 和 tree 是什么意思?

父母:表示该提交的前一个提交的提交对象的ID。

我去看看。

% git cat-file -p 75decf137844a74cac913fd0432e20a60c98dd2f
tree 14c6e43a72c3db0994438c6fd085fff416d118d9
parent f8f5ce73034a3b4de5b3e8f45f70eea4ce3f5d0c
author takashi suzuki <takashi@takashinoMacBook-puro.local> 1561730846 +0900
committer takashi suzuki <takashi@takashinoMacBook-puro.local> 1561730846 +0900

test

确实是前一个提交的内容。

树木物体

好啦,”tree” 是什么呢?

树状物:表示了该提交时的整个文件树的树状对象的ID。

我试试看。

% git cat-file -p 14c6e43a72c3db0994438c6fd085fff416d118d9
040000 tree 29607877f62818bfb0bb871326851453cdb1e38b   lib
040000 tree d4d3444596c8dc1bd3c02e8d25556c9316ce7395   bin
100644 blob e85f913914bd9d1342eae4cdd97b5520733a592a   Rakefile
100644 blob ba03955e5bfac64888520b66ea96cdc5351fc4bc   package.json
(以下略)

在Git管理的文件树的顶层目录中,确实会显示出文件和目录。

我們來看一下表示lib目錄的tree物件內的內容。

% git cat-file -p 29607877f62818bfb0bb871326851453cdb1e38b
040000 tree 29a422c19251aeaeb907175e9b3219a9bed6c616	assets
040000 tree 29a422c19251aeaeb907175e9b3219a9bed6c616	tasks
100644 blob cf5106d72affa73ca65e0f046ee86a98e01f3a83	sample.rb

确实,显示了lib/目录中的文件和文件夹。

blob 对象

接下来,我们来看一看代表sample.rb文件的blob对象的内容。

% git cat-file -p cf5106d72affa73ca65e0f046ee86a98e01f3a83
sample script

肯定地说,会显示出sample.rb文件中的”sample script”这个字符串。

标记对象

继续了解commit、tree、blob之后,让我们看一下最后一个的tag object。

作为准备工作,给予标签。

% git tag -a v0.1 -m "this is version 0.1"

% git log --oneline --graph
* 8bab622 (HEAD -> dev, tag: v0.1, master) add devdev
* 75decf1 test
* f8f5ce7 first commmit

tag object与commit object不同,似乎无法通过git log或git show显示对象的ID。所以直接在.git/目录下查看它。

% cat .git/refs/tags/v0.1
01c56840fad536dd15b36b65827a68b2231adfb5

% git cat-file -p 01c56840fad536dd15b36b65827a68b2231adfb5
object 8bab622b7cc2b3a840ed96b191b57b43f6e05e31
type commit
tag v0.1
tagger takashi suzuki <takashi@takashinoMacBook-puro.local> 1561778282 +0900

this is version 0.1

确实,tag object拥有标签信息。

object的本质

听起来似乎有四种对象,每种对象都具有文件、目录、提交或标签的信息。

那么,什么是实际上的对象呢?

总结来说,对象的本质就是位于.git/objects的每个单独文件。

「具有作者田中太郎…的信息,ID为004dbb5…的对象」即指的是「具有通过zlib压缩的作者田中太郎…的值,位于.git/objects/00/4dbb5…的文件」。

进一步说,004dbb5…是从字符串“作者山田太郎”生成的哈希值。

亲自目睹一下。

% cat .git/objects/00/4dbb5d10a9c8cd7808870bc2437378fe26528d
x��ˊ�@E]�W�~P�I�ADf�G\��1��;���W�O.��p������~�U*�
����:������[=A~k���u��k\d������'f#��M��3ș���f���W�%

确实存在一个名为.git/objects/00/4dbb5…的文件,其内容是用zlib压缩的字符串”author taro yamada…”。(上述为在终端中强制显示该文件的结果。)

指针

在关于这些对象的讨论中,出现了commit、tag等术语,但是在Git的世界中,分支和HEAD是什么意思呢?

分支

从分支的角度来看,它是指向“提交对象”的指针。

在内部中存在一个名为.git/refs/heads/分支名称的文件,其中的内容是该分支最新提交的ID。

让我们实际去看看。

% git log --oneline --graph
* 8bab622 (HEAD -> dev, tag: v0.1, master) add devdev
* 75decf1 test
* f8f5ce7 first commmit

% cat .git/refs/heads/dev
8bab622b7cc2b3a840ed96b191b57b43f6e05e31

# コミットを1つ重ねる

% git commit -am "add ddeevv"

% git log --oneline --graph
* 4dfe659 (HEAD -> dev) add ddeevv
* 8bab622 (tag: v0.1, master) add devdev
* 75decf1 test
* f8f5ce7 first commmit

% cat .git/refs/heads/dev
4dfe6598f762695be4129aba0baaff5e3947cbf2

可以确定的是,可以看到`.git/refs/heads/master`的内容是指向最新提交的master分支的ID。

标题

分支是指向“提交对象”的指针,但HEAD是指向“分支”的指针。

换言之,HEAD是指向提交对象指针的指针。

内部的情况下,有一个名为.git/head的文件,其中的内容是当前的HEAD所在的分支名称。

让我们亲自去看看吧。

% git log --oneline --graph
* 4dfe659 (HEAD -> dev) add ddeevv
* 8bab622 (tag: v0.1, master) add devdev
* 75decf1 test
* f8f5ce7 first commmit

% cat .git/head
ref: refs/heads/dev

# HEADを移動する

% git checkout master
Switched to branch 'master'

% cat .git/head
ref: refs/heads/master

确实,存在一个名为.git/head的文件,其中的内容就是当前所在分支的名称。

最後

我本來想總結一下最後的處理,包括git add和git commit,但是這個網站很感人且非常易懂,所以我只將連結貼出來,結束這個話題。

[http://koseki.github.io/git-object-browser/zh/#/step1/.git/]

在写这篇文章的过程中,我再次意识到,理解和用语言表达是完全不同的难度。花在输出上的时间就无法用来输入,所以要多么频繁/详细地输出是个令人困扰的问题。

广告
将在 10 秒后关闭
bannerAds