The index is one of the most important data structures in Git.
It represents a virtual working tree state by recording list of
paths and their object names and serves as a staging area to
write out the next tree object to be committed. The state is
"virtual" in the sense that it does not necessarily have to, and
often does not, match the files in the working tree.
There are cases where Git needs to examine the differences between the
virtual working tree state in the index and the files in the
working tree. The most obvious case is when the user asks git
diff (or its low level implementation, git diff-files) or
git-ls-files --modified. In addition, Git internally checks
if the files in the working tree are different from what are
recorded in the index to avoid stomping on local changes in them
during patch application, switching branches, and merging.
In order to speed up this comparison between the files in the
working tree and the index entries, the index entries record the
information obtained from the filesystem via lstat(2) system
call when they were last updated. When checking if they differ,
Git first runs lstat(2) on the files and compares the result
with this information (this is what was originally done by the
ce_match_stat() function, but the current code does it in
ce_match_stat_basic() function). If some of these "cached
stat information" fields do not match, Git can tell that the
files are modified without even looking at their contents.
Note: not all members in struct stat obtained via lstat(2)
are used for this comparison. For example, st_atime obviously
is not useful. Currently, Git compares the file type (regular
files vs symbolic links) and executable bits (only for regular
files) from st_mode member, st_mtime and st_ctime
timestamps, st_uid, st_gid, st_ino, and st_size members.
With a USE_STDEV compile-time option, st_dev is also
compared, but this is not enabled by default because this member
is not stable on network filesystems. With USE_NSEC
compile-time option, st_mtim.tv_nsec and st_ctim.tv_nsec
members are also compared. On Linux, this is not enabled by default
because in-core timestamps can have finer granularity than
on-disk timestamps, resulting in meaningless changes when an
inode is evicted from the inode cache. See commit 8ce13b0
of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
([PATCH] Sync in core time granularity with filesystems,
2005-01-04). This patch is included in kernel 2.6.11 and newer, but
only fixes the issue for file systems with exactly 1 ns or 1 s
resolution. Other file systems are still broken in current Linux
kernels (e.g. CEPH, CIFS, NTFS, UDF), see
https://lore.kernel.org/lkml/5577240D.7020309@gmail.com/