git使用什么算法来检测工作树上的变化?

Khe*_*ben 49 git

这是关于内部的git.

我一直在阅读伟大的'Pro Git'一书,并学习一下git如何在内部工作(所有关于SHA1,blob,引用,tress,提交等等).顺便说一句,相当聪明的建筑.

因此,为了放入上下文,git将文件的内容引用为SHA1值,因此只需比较哈希值就能知道特定内容是否已更改.但我的问题是git如何检查工作树中的内容是否发生了变化.

天真的方法是认为,每次运行命令git status或类似命令时,它都会搜索工作目录中的所有文件,计算SHA1并将其与最后一次提交的文件进行比较.但对于大型项目来说,这似乎是非常低效的,就像Linux内核一样.

另一个想法可能是检查文件的最后修改日期,但我认为git不存储该信息(当您克隆存储库时,所有文件都有新的时间)

我确信它是以有效的方式进行的(git非常快),有人如何实现这一目标?

PD:只是添加一个关于git索引的有趣链接,特别声明索引保存有关文件时间戳的信息,即使树对象没有.

Jos*_*Lee 34

Git的索引维护git最后一次将每个文件写入工作树的时间戳(并在文件从工作树或提交中缓存时更新这些时间戳).您可以看到元数据git ls-files --debug.除了时间戳,它还会记录lstat的大小,inode和其他信息,以减少误报的可能性.

当您执行git-status时,它只是在工作树中的每个文件上调用lstat并比较元数据以便快速确定哪些文件不变.这在racy-gitupdate-index下的文档中有所描述.


bco*_*rso 10

在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 */
};
Run Code Online (Sandbox Code Playgroud)

看起来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)
Run Code Online (Sandbox Code Playgroud)

这使文件处于已修改但lstat无法检测到的状态.

为了解决这个问题,现在在lstat状态不明确的情况下,Git会比较文件的内容以确定它是否已被更改.


注意:

如果有人像我一样感到困惑,关于st_mtime描述,它表明它是通过"超过零字节"的写入来更新的,这意味着绝对的改变.

例如,对于具有单个字符的文本文件文件A:如果A更改为B总字节大小有0个净更改,但st_mtime仍将更新(必须自己尝试验证,用于ls -l查看时间戳) ).