1 Use of index and Racy Git problem
 
   2 =================================
 
   7 The index is one of the most important data structures in Git.
 
   8 It represents a virtual working tree state by recording list of
 
   9 paths and their object names and serves as a staging area to
 
  10 write out the next tree object to be committed.  The state is
 
  11 "virtual" in the sense that it does not necessarily have to, and
 
  12 often does not, match the files in the working tree.
 
  14 There are cases Git needs to examine the differences between the
 
  15 virtual working tree state in the index and the files in the
 
  16 working tree.  The most obvious case is when the user asks `git
 
  17 diff` (or its low level implementation, `git diff-files`) or
 
  18 `git-ls-files --modified`.  In addition, Git internally checks
 
  19 if the files in the working tree are different from what are
 
  20 recorded in the index to avoid stomping on local changes in them
 
  21 during patch application, switching branches, and merging.
 
  23 In order to speed up this comparison between the files in the
 
  24 working tree and the index entries, the index entries record the
 
  25 information obtained from the filesystem via `lstat(2)` system
 
  26 call when they were last updated.  When checking if they differ,
 
  27 Git first runs `lstat(2)` on the files and compares the result
 
  28 with this information (this is what was originally done by the
 
  29 `ce_match_stat()` function, but the current code does it in
 
  30 `ce_match_stat_basic()` function).  If some of these "cached
 
  31 stat information" fields do not match, Git can tell that the
 
  32 files are modified without even looking at their contents.
 
  34 Note: not all members in `struct stat` obtained via `lstat(2)`
 
  35 are used for this comparison.  For example, `st_atime` obviously
 
  36 is not useful.  Currently, Git compares the file type (regular
 
  37 files vs symbolic links) and executable bits (only for regular
 
  38 files) from `st_mode` member, `st_mtime` and `st_ctime`
 
  39 timestamps, `st_uid`, `st_gid`, `st_ino`, and `st_size` members.
 
  40 With a `USE_STDEV` compile-time option, `st_dev` is also
 
  41 compared, but this is not enabled by default because this member
 
  42 is not stable on network filesystems.  With `USE_NSEC`
 
  43 compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
 
  44 members are also compared, but this is not enabled by default
 
  45 because in-core timestamps can have finer granularity than
 
  46 on-disk timestamps, resulting in meaningless changes when an
 
  47 inode is evicted from the inode cache.  See commit 8ce13b0
 
  48 of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
 
  49 ([PATCH] Sync in core time granularity with filesystems,
 
  55 There is one slight problem with the optimization based on the
 
  56 cached stat information.  Consider this sequence:
 
  59   $ git update-index 'foo'
 
  60   : modify 'foo' again, in-place, without changing its size
 
  62 The first `update-index` computes the object name of the
 
  63 contents of file `foo` and updates the index entry for `foo`
 
  64 along with the `struct stat` information.  If the modification
 
  65 that follows it happens very fast so that the file's `st_mtime`
 
  66 timestamp does not change, after this sequence, the cached stat
 
  67 information the index entry records still exactly match what you
 
  68 would see in the filesystem, even though the file `foo` is now
 
  70 This way, Git can incorrectly think files in the working tree
 
  71 are unmodified even though they actually are.  This is called
 
  72 the "racy Git" problem (discovered by Pasky), and the entries
 
  73 that appear clean when they may not be because of this problem
 
  74 are called "racily clean".
 
  76 To avoid this problem, Git does two things:
 
  78 . When the cached stat information says the file has not been
 
  79   modified, and the `st_mtime` is the same as (or newer than)
 
  80   the timestamp of the index file itself (which is the time `git
 
  81   update-index foo` finished running in the above example), it
 
  82   also compares the contents with the object registered in the
 
  83   index entry to make sure they match.
 
  85 . When the index file is updated that contains racily clean
 
  86   entries, cached `st_size` information is truncated to zero
 
  87   before writing a new version of the index file.
 
  89 Because the index file itself is written after collecting all
 
  90 the stat information from updated paths, `st_mtime` timestamp of
 
  91 it is usually the same as or newer than any of the paths the
 
  92 index contains.  And no matter how quick the modification that
 
  93 follows `git update-index foo` finishes, the resulting
 
  94 `st_mtime` timestamp on `foo` cannot get a value earlier
 
  95 than the index file.  Therefore, index entries that can be
 
  96 racily clean are limited to the ones that have the same
 
  97 timestamp as the index file itself.
 
  99 The callers that want to check if an index entry matches the
 
 100 corresponding file in the working tree continue to call
 
 101 `ce_match_stat()`, but with this change, `ce_match_stat()` uses
 
 102 `ce_modified_check_fs()` to see if racily clean ones are
 
 103 actually clean after comparing the cached stat information using
 
 104 `ce_match_stat_basic()`.
 
 106 The problem the latter solves is this sequence:
 
 108   $ git update-index 'foo'
 
 109   : modify 'foo' in-place without changing its size
 
 110   : wait for enough time
 
 111   $ git update-index 'bar'
 
 113 Without the latter, the timestamp of the index file gets a newer
 
 114 value, and falsely clean entry `foo` would not be caught by the
 
 115 timestamp comparison check done with the former logic anymore.
 
 116 The latter makes sure that the cached stat information for `foo`
 
 117 would never match with the file in the working tree, so later
 
 118 checks by `ce_match_stat_basic()` would report that the index entry
 
 119 does not match the file and Git does not have to fall back on more
 
 120 expensive `ce_modified_check_fs()`.
 
 126 The runtime penalty of falling back to `ce_modified_check_fs()`
 
 127 from `ce_match_stat()` can be very expensive when there are many
 
 128 racily clean entries.  An obvious way to artificially create
 
 129 this situation is to give the same timestamp to all the files in
 
 130 the working tree in a large project, run `git update-index` on
 
 131 them, and give the same timestamp to the index file:
 
 134   $ git ls-files | xargs touch -r .datestamp
 
 135   $ git ls-files | git update-index --stdin
 
 136   $ touch -r .datestamp .git/index
 
 138 This will make all index entries racily clean.  The linux project, for
 
 139 example, there are over 20,000 files in the working tree.  On my
 
 140 Athlon 64 X2 3800+, after the above:
 
 142   $ /usr/bin/time git diff-files
 
 143   1.68user 0.54system 0:02.22elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
 
 144   0inputs+0outputs (0major+67111minor)pagefaults 0swaps
 
 145   $ git update-index MAINTAINERS
 
 146   $ /usr/bin/time git diff-files
 
 147   0.02user 0.12system 0:00.14elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
 
 148   0inputs+0outputs (0major+935minor)pagefaults 0swaps
 
 150 Running `git update-index` in the middle checked the racily
 
 151 clean entries, and left the cached `st_mtime` for all the paths
 
 152 intact because they were actually clean (so this step took about
 
 153 the same amount of time as the first `git diff-files`).  After
 
 154 that, they are not racily clean anymore but are truly clean, so
 
 155 the second invocation of `git diff-files` fully took advantage
 
 156 of the cached stat information.
 
 159 Avoiding runtime penalty
 
 160 ------------------------
 
 162 In order to avoid the above runtime penalty, post 1.4.2 Git used
 
 163 to have a code that made sure the index file
 
 164 got timestamp newer than the youngest files in the index when
 
 165 there are many young files with the same timestamp as the
 
 166 resulting index file would otherwise would have by waiting
 
 167 before finishing writing the index file out.
 
 169 I suspected that in practice the situation where many paths in the
 
 170 index are all racily clean was quite rare.  The only code paths
 
 171 that can record recent timestamp for large number of paths are:
 
 173 . Initial `git add .` of a large project.
 
 175 . `git checkout` of a large project from an empty index into an
 
 176   unpopulated working tree.
 
 178 Note: switching branches with `git checkout` keeps the cached
 
 179 stat information of existing working tree files that are the
 
 180 same between the current branch and the new branch, which are
 
 181 all older than the resulting index file, and they will not
 
 182 become racily clean.  Only the files that are actually checked
 
 183 out can become racily clean.
 
 185 In a large project where raciness avoidance cost really matters,
 
 186 however, the initial computation of all object names in the
 
 187 index takes more than one second, and the index file is written
 
 188 out after all that happens.  Therefore the timestamp of the
 
 189 index file will be more than one seconds later than the
 
 190 youngest file in the working tree.  This means that in these
 
 191 cases there actually will not be any racily clean entry in
 
 194 Based on this discussion, the current code does not use the
 
 195 "workaround" to avoid the runtime penalty that does not exist in
 
 196 practice anymore.  This was done with commit 0fc82cff on Aug 15,