git使用什么algorithm来检测工作树上的变化?
这是关于git
的内部。
我一直在阅读伟大的“Pro Git”一书,并学习一些关于git在内部工作的内容(关于SHA1,blob,引用,tress,commit等等)。 顺便说一下,相当聪明的build筑。
因此,为了放入上下文,git将文件的内容作为SHA1值引用,所以它能够知道特定内容是否仅仅比较了散列值而发生了变化。 但是我的问题是关于git如何检查工作树中的内容是否已经改变。
天真的做法是,每当你运行一个命令作为git status
或类似命令时,它将search工作目录中的所有文件,计算出SHA1并将其与最后一次提交的文件进行比较。 但是对于大型项目来说,这似乎是非常低效的,就像Linux内核一样。
另一个想法可能是检查文件的最后修改date,但我认为git没有存储这些信息(当你克隆一个仓库时,所有的文件都有一个新的时间)
我敢肯定,它是在一个有效的方式(git是真的很快),做任何人如何实现?
PD:只是添加一个关于git索引的有趣链接 ,特别指出索引保留了关于文件时间戳的信息,即使树对象没有。
Git的索引保存了git最后一次将每个文件写入工作树的时间戳(并且每当文件从工作树或提交中caching时就更新这些时间戳)。 你可以用git ls-files --debug
查看元数据。 除了时间戳之外,它还logging来自lstat的大小,inode和其他信息,以减less误报的可能性。
在执行git-status时,只需在工作树中的每个文件上调用lstat ,并比较元数据以便快速确定哪些文件未更改。 这在racy-git和update-index下的文档中有描述。
在一个unix文件系统上,文件信息被跟踪,并且可以使用lstat方法来加以识别。 stat结构包含多个时间戳,大小信息等等:
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
看起来Git最初只是依靠这个统计结构来决定一个文件是否已经被修改( 参见参考资料 ):
在检查它们是否不同时,Git首先运行
lstat(2)
文件,并将结果与此信息进行比较
然而,报告的竞争条件( racy-git )发现如果文件被修改如下:
: modify 'foo' $ git update-index 'foo' : modify 'foo' again, in-place, without changing its size (And quickly enough to not change it's timestamps)
这使文件处于已被修改但不能被lstat检测到的状态。
为了解决这个问题,现在在lstat状态不明确的情况下,Git比较文件的内容以确定它是否已经改变。
注意:
如果有人像我一样对st_mtime描述感到困惑,那就说明它是通过写入“超过零字节”来更新的,这意味着绝对的改变。
例如,对于具有单个字符的文本文件文件A
:如果A
更改为B
,则总字节大小的净改变为0,但是st_mtime仍将被更新(必须亲自尝试来validation,使用ls -l
看时间戳)。