git 索引中的 ctime 和 mtime 有什么用?

What is ctime and mtime used for in the git index?

第一次将文件添加到索引时,文件的创建时间戳存储在索引记录的 ctime 中,文件当前修改的时间戳存储在 mtime 字段中。

在更改后添加文件时,mtime 会更新为文件的当前时间戳。

签出文件时,它不使用 mtime 作为 这些时间戳在 git 的世界中有什么用?

查看对索引的引用,它们似乎就在那里。也许他们有一些从未实现的未来目的?

$ git ls-files --debug readme.txt
readme.txt
  ctime: 1531246924:79161700
  mtime: 1531258019:669963000
  dev: 0        ino: 0
  uid: 0        gid: 0
  size: 13      flags: 0

索引有三个名称:"the index"(你在这里使用的那个),staging-area(在某种意义上更好,因为它描述了它的功能),以及 缓存 。这是适用于此处的姓氏。

索引之所以成为索引或缓存,而不仅仅是暂存区,是因为它索引并缓存了工作树的内容。缓存的目的是加快速度。但是缓存总是有一个问题:在某些时候,缓存中的部分或全部数据会变得无效。提供 fast/short-cut 访问真实、实际、正确数据的缓存不再反映现实。必须丢弃缓存或用正确的数据重新填充缓存。

现在,Git 的索引包含文件的 副本 ,或者至少在 git checkout 时间,在工作中- tree.1 如果您以某种方式更改了 文件,Git 如何知道缓存的副本无效?

Git 的答案是与缓存的副本一起存储一些 stat 信息 关于 该文件,包括文件的大小以及您观察到的两个时间戳。如果您以某种方式修改文件——即使只是改变其模式,这不会触及文件的内容(因此大小和 mtime 保持不变)——ctime 时间戳将被更新,并且 Git 将知道有些事情已经改变了。当然如果你修改内容或者改变大小,Git也会知道。


1从技术上讲,索引保存了一个 cleaned 文件副本,准备提交。如果您有行尾设置 and/or 涂抹 and/or 清洁过滤器,清洁后的副本可能实际上与工作树副本不匹配。


您注意到:

When the file is checked out it does not use the mtime for the [unfinished thought here]

我认为您观察到的是 Git 的其他优化之一。如果您 运行 git checkout <commit-specifier> 在空的情况下(例如,克隆操作的第一次签出), Git 必须第一次完全填充索引和工作树。但是,一旦你这样做了,如果你现在 运行 一个 different git checkout 来检查一些 other 提交, Git 可以优化结帐:

  • 索引包含当前提交的所有文件,存储在工作树中。
  • 您要签出的新提交中有许多文件与当前提交 100% 相同。
  • 因此,Git 只需替换索引和工作树中的 不同的 文件。

因此,工作树中两次提交之间相同文件的 mtime 保持不变,因为这些文件根本没有被触及。

这只是缓存进行缓存的另一个方面。但是,它确实发挥了切换分支的能力,同时在索引 and/or 工作树中进行了一些修改。有关详细信息,请参阅 Checkout another branch when there are uncommitted changes on the current branch

. If you modify the file in some way—even to the extent of just changing its mode, which does not touch the file's contents (so the size and mtime remain unchanged)—the ctime timestamp will be updated, and Git will know that something has changed

实际上...不是 git mv:在 Git 2.36(2022 年第二季度)之前,“git mv"(man) 未能刷新它移动的条目的缓存统计信息。

参见 commit b7f9130 (29 Mar 2022) by Victoria Dye (vdye)
(由 Junio C Hamano -- gitster -- in commit 909d5b6 合并,2022 年 4 月 4 日)

mv: refresh stat info for moved entry

Reported-by: Maximilian Reichel
Signed-off-by: Victoria Dye

Update the stat info of the moved index entry in 'rename_index_entry_at()' if the entry is up-to-date with the index.
Internally, 'git mv'(man) uses 'rename_index_entry_at()' to move the source index entry to the destination.
However, it directly copies the stat info of the original cache entry, which will not reflect the 'ctime' of the file renaming operation that happened as part of the move.

If a file is otherwise up-to-date with the index, that difference in 'ctime' will make the entry appear out-of-date until the next index-refreshing operation (e.g., 'git status'(man)).

Some commands, such as 'git reset'(man), use the cached stat information to determine whether a file is up-to-date; if this information is incorrect, the command will fail when it should pass.
In order to ensure a moved entry is evaluated as 'up-to-date' when appropriate, refresh the destination index entry's stat info in 'git mv' if and only if the file is up-to-date.