Git重命名了文件和inode

时间:2018-02-04 22:16:02

标签: git file-rename

考虑我们将以下命令应用于git下跟踪的文件(hello.txt)(在干净的工作副本中):

echo "hi" >> hello.txt
mv hello.txt bye.txt
git rm hello.txt
git add bye.txt
git status

结果:

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    hello.txt -> bye.txt

所以,git知道它是同一个文件,即使它被重命名。 我有一些模糊的内存,git检查inode以确定新文件与旧的已删除文件相同。 Thisthis SO回答说,git只会检查文件的内容,并且不会以任何方式检查它是否是相同的inode。 (我的结论(*):如果我对文件做了更大的修改,git就不会检测到重命名,即使inode仍然是相同的。)

在我看来很明显,我错了,git不检查inode(或任何其他文件系统信息),只是内容。但后来,我找到了this other answer,声称

  

除了时间戳,它[即git]记录大小,inode和其他   来自lstat的信息,以减少误报的可能性。什么时候   你执行git-status,它只是在每个文件中调用lstat   工作树并比较元数据以便快速确定   哪些文件没有变化。

我实际上有两个问题:

  1. 我的理解下面是否正确?
  2. Git确实依赖(也)inode来检测文件是否被更改,但它不使用inode来检测文件重命名。

    1. 假设1.是正确的。为什么git不依赖于inode来检测文件重命名? 如果确实如此,那么我们就不会遇到上面的问题,标有(*)。 (即,无论内容有多大,它都会检测到重命名。)
    2. (我认为答案类似于&#34;因此在没有inode的系统上的行为是相同的,例如Windows&#34;。但是,如果是这样的话那么这个&#34;相同的行为&#34;已经因依赖inode来检测变化而被打破。)

1 个答案:

答案 0 :(得分:0)

我认为您在这里混合了两个不同的概念:

  • git存储
  • git客户行为

首先有关内部 git中文件的存储。 简而言之:当文件存储在git中时,根本没有引用inode和diff。

您知道git在提交树上运行。每个提交都引用一棵树(其含义类似于文件系统中的目录):

$ git cat-file commit HEAD  # example for some random git repo on my disk
tree e68e0f9afad22357e47d0a341770f2315ee16b2c
parent 6d13fea5d0c1d0b4aedf96b7141c05c73bf9c9cb
author Timur Batyrshin <erthad@gmail.com> 1590062438 +0300
committer Timur Batyrshin <erthad@gmail.com> 1590062438 +0300

add icon to the workflow

这里e68e0f9afad22357e47d0a341770f2315ee16b2c是附加到此提交的哈希引用树对象。您可以浏览其内容:

$ git ls-tree e68e0f9afad22357e47d0a341770f2315ee16b2c
100644 blob 2dd98d7ddcdb1c24d5fa368c349614baec840167    .gitignore
100644 blob 71cf7988bc6ca7e38fbb8d0490cb0b9f2368d3dc    LICENSE
100644 blob 67ed24d3dd5ed71a9b03180d0540276c659e71c3    README.md
100644 blob 5ab2fb346e9bf27d048bad4725ae1180a0d1fffc    icon.png
100644 blob 198e0a4a3df7eedc752643d1a7d21b825ff5f2b2    info.plist
100755 blob 9969b7006112d4d25a7af472cd63ba61e6fd3736    login.sh
100755 blob 834e97824d38849d9254aa4607e636dc5ef7bae4    populate.sh
100755 blob 48bf586e84f820c1434959e8064fe8331a0ff5e3    show.rb

正如您所看到的,git中的树存储文件名,文件模式(与UNIX文件模式相似,尽管有所不同)和对存储该文件内容的二进制Blob的引用。 例如,这是来自特定提交的.gitignore文件内容的前三行,该提交具有哈希2dd98d7ddcdb1c24d5fa368c349614baec840167

$ git cat-file blob 2dd98d7ddcdb1c24d5fa368c349614baec840167 | head -n 3
*.gem
*.rbc
/.config

总结:每个git提交都指向一个树对象。树对象依次指向具有文件内容的特定Blob(或其他子树)。

Git存储没有对diff的引用,也没有对inode的引用。

Git存储甚至没有重命名的引用:不同的树指向不同的Blob,并且当您需要diff时,git client只会比较两个Blob并为您生成diff。人们通常会对看到重命名感兴趣,因此git也会为您生成该信息。我猜最初它只在下一次提交中引用相同blob的文件名变得不同时才显示重命名,并在以后重命名几个版本时开始显示小的差异。

现在进入第二部分: git客户行为。 Git能够非常快速地遍历历史和树结构,但是当您浏览差异时,git client必须计算您需要的每个差异,并且在大型存储库中可能非常耗时。

由于这个原因,git客户端通常采用各种缓存机制和其他方式来加快进程。这可以是文件统计信息的缓存,比较inode以及您可能想到的任何其他内容。 @torek的答案很好地描述了问题和解决方法。