每次提交都会在git中创建一个新的树对象吗?

时间:2019-04-20 21:15:49

标签: git

我正在学习git的内部知识以及git对象模型如何“在幕后”工作。

如果我更改一些文件并将其提交到本地git存储库中,则将创建一个新的git commit object。每个提交对象都有关联的tree object。每个树对象都包含它指向的文件(blob)的SHA1。那么,这是否意味着每个新的提交(假设其中有一些文件更改)总会生成一个新的树对象(即使它指向文件系统上的同一目录,它的SHA1与之前的所有树都不同)?

我的解释是否正确? 另外,是否可以在不更改文件的情况下提交?在那种情况下,不需要新的tree object,但我不知道git中是否可以进行这种类型的提交。

3 个答案:

答案 0 :(得分:4)

  

我的解释是否正确?

实际上-是-但请参见下文

  

还可以在不更改文件的情况下进行提交吗?在那种情况下,就不需要新的树对象,但是我不知道在git中是否可以进行这种类型的提交。

@Lasse已经提到git commit --allow-empty作为重用 last 树的一种方式,但这是一个非常不寻常的命令。当您只想修复最后的提交消息时,一个很常见的命令是git commit --amend

还请注意:现有树可以重用,并且这些树不必来自最后一次提交。常见的情况是git rebase --interactive,只是重新编写提交消息(类似于git commit --amend,但对于远离HEAD的提交)。

另一种情况:考虑此提交顺序:

commit 0
commit A
commit B
commit C
revert C  # will reuse tree from B
revert B  # will reuse tree from A
revert A  # will reuse tree from 0

在这种情况下,老树也被重用。

下一个场景:git merge -s ours(不要与git merge -X ours混淆)将合并另一个分支,但忽略任何更改。换句话说:merge-commit和第一个父节点共享同一棵树。

用于做奇怪事情的瑞士军刀当然是git filter-branch,在这里您可以用几种方式重写提交,但不影响树木。

答案 1 :(得分:2)

让我们一次迈出一步。

每次将文件添加到存储库时(通常是通过将其添加到索引然后提交),都会添加整个文件的快照。计算哈希值,该哈希值是此文件的标识符。

但是,如果您以5-6提交,则设法将文件内容恢复到以前的状态,则其新哈希将已存在于存储库中,因此不会添加任何其他文件。取而代之的是,凡是要引用此文件的内容都将使用哈希,因此将引用“旧”文件。

树对象只是文本文件,其中包含目录中文件的哈希以及标识子树(子文件夹)的哈希。树对象的哈希也从树的内容中计算得出,因此取决于文件的哈希和子树的哈希。

换句话说,在上述还原文件的场景中,如果最终将存储库中 all 文件的内容还原到上一次提交时的状态,新树的哈希将已经存在,并且不会添加任何 new 树对象。取而代之的是,无论引用什么树,最有可能的提交都将使用哈希并引用“旧”树。

在大多数情况下,这可能有点理论化。最终将所有文件恢复到较旧状态的情况可能不是经常遇到的情况。因此,实际上,每次创建提交时,您很有可能也会创建并添加一个或多个新的树对象。

要添加没有文件更改的提交(称为“空提交”),可以使用以下git命令:

git commit --allow-empty

您可以像往常一样塞满-m "message"之类的东西。

这是一个例子:

λ git init .
Initialized empty Git repository in D:/Temp/.git/

λ echo a >test.txt                                                             
λ git add .                                                                    
λ git commit -m test1                                                          
[master (root-commit) dc613fe] test1                                           
 1 file changed, 1 insertion(+)                                                
 create mode 100644 test.txt                                                   

λ git commit -m test2 --allow-empty                                            
[master c197192] test2                                                         

λ git lg                                                                       
* c197192: (7 seconds ago) test2 (HEAD -> master)                              
| Lasse Vågsæther Karlsen <lasse@vkarlsen.no> (Sat, 20 Apr 2019 23:28:44 +0200)
|                                                                              
* dc613fe: (17 seconds ago) test1                                              
  Lasse Vågsæther Karlsen <lasse@vkarlsen.no> (Sat, 20 Apr 2019 23:28:34 +0200)

现在,如果我输出这两个提交的内容:

λ git cat-file -p c197192
tree 35b422a71005d59dd6af858a3425b608b63f7b5a
parent dc613fe57276009b399d8152a657cb971fad605a
author Lasse Vågsæther Karlsen <lasse@vkarlsen.no> 1555795724 +0200
committer Lasse Vågsæther Karlsen <lasse@vkarlsen.no> 1555795724 +0200

test2

λ git cat-file -p dc613fe
tree 35b422a71005d59dd6af858a3425b608b63f7b5a
author Lasse Vågsæther Karlsen <lasse@vkarlsen.no> 1555795714 +0200
committer Lasse Vågsæther Karlsen <lasse@vkarlsen.no> 1555795714 +0200

test1

您可以看到它们都引用了完全相同的树对象,如下所示:

λ git cat-file -p 35b422a71005d59dd6af858a3425b608b63f7b5a
100644 blob f5eea678d87a8664e4c76e12d3ef5c4ff775ad58    test.txt

答案 2 :(得分:0)

如果树的内容(目录具有相同的名称和布局,文件具有相同的名称和相同的内容)与以前的修订版相同,则该树将是“重复的”,因此它是teo指向同一棵树的修订