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