@matheusbsbs / matheus-bagliano / commits / 679f2f9fdd
Fork 0

unpack-trees: skip stat on fsmonitor-valid files

The index might be aware that a file hasn't modified via fsmonitor, but unpack-trees did not pay attention to it and checked via ie_match_stat which can be inefficient on certain filesystems. This significantly slows down commands that run oneway_merge, like checkout and reset --hard. This patch makes oneway_merge check whether a file is considered unchanged through fsmonitor and skips ie_match_stat on it. unpack-trees also now correctly copies over fsmonitor validity state from the source index. Finally, for correctness, we force a refresh of fsmonitor state in tweak_fsmonitor. After this change, commands like stash (that use reset --hard internally) go from 8s or more to ~2s on a 250k file repository on a mac. Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Kevin Willford <Kevin.Willford@microsoft.com> Signed-off-by: Utsav Shah <utsav@dropbox.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Utsav Shah committed Nov 20, 2019 at 08:32 UTC 679f2f9fdd2173d87251aee357dd0e46ce977f42
3 files changed +29 -9
fsmonitor.c
+17 -6
@@ -191,13 +191,26 @@ void refresh_fsmonitor(struct index_state *istate)
191 }
192 if (bol < query_result.len)
193 fsmonitor_refresh_callback(istate, buf + bol);
194 +
195 + /* Now mark the untracked cache for fsmonitor usage */
196 + if (istate->untracked)
197 + istate->untracked->use_fsmonitor = 1;
198 } else {
199 +
200 + /* We only want to run the post index changed hook if we've actually changed entries, so keep track
201 + * if we actually changed entries or not */
202 + int is_cache_changed = 0;
203 /* Mark all entries invalid */
196 - for (i = 0; i < istate->cache_nr; i++)
197 - istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
204 + for (i = 0; i < istate->cache_nr; i++) {
205 + if (istate->cache[i]->ce_flags & CE_FSMONITOR_VALID) {
206 + is_cache_changed = 1;
207 + istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
208 + }
209 + }
210
211 /* If we're going to check every file, ensure we save the results */
200 - istate->cache_changed |= FSMONITOR_CHANGED;
212 + if (is_cache_changed)
213 + istate->cache_changed |= FSMONITOR_CHANGED;
214
215 if (istate->untracked)
216 istate->untracked->use_fsmonitor = 0;
@@ -259,9 +272,7 @@ void tweak_fsmonitor(struct index_state *istate)
272 (uintmax_t)istate->fsmonitor_dirty->bit_size, istate->cache_nr);
273 ewah_each_bit(istate->fsmonitor_dirty, fsmonitor_ewah_callback, istate);
274
262 - /* Now mark the untracked cache for fsmonitor usage */
263 - if (istate->untracked)
264 - istate->untracked->use_fsmonitor = 1;
275 + refresh_fsmonitor(istate);
276 }
277
278 ewah_free(istate->fsmonitor_dirty);
t/t7519-status-fsmonitor.sh
+7 -2
@@ -106,6 +106,8 @@ EOF
106
107 # test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit
108 test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' '
109 + write_script .git/hooks/fsmonitor-test<<-\EOF &&
110 + EOF
111 git update-index --fsmonitor &&
112 git update-index --fsmonitor-valid dir1/modified &&
113 git update-index --fsmonitor-valid dir2/modified &&
@@ -164,6 +166,8 @@ EOF
166
167 # test that newly added files are marked valid
168 test_expect_success 'newly added files are marked valid' '
169 + write_script .git/hooks/fsmonitor-test<<-\EOF &&
170 + EOF
171 git add new &&
172 git add dir1/new &&
173 git add dir2/new &&
@@ -218,11 +222,12 @@ test_expect_success '*only* files returned by the integration script get flagged
222 # Ensure commands that call refresh_index() to move the index back in time
223 # properly invalidate the fsmonitor cache
224 test_expect_success 'refresh_index() invalidates fsmonitor cache' '
221 - write_script .git/hooks/fsmonitor-test<<-\EOF &&
222 - EOF
225 clean_repo &&
226 dirty_repo &&
227 + write_integration_script &&
228 git add . &&
229 + write_script .git/hooks/fsmonitor-test<<-\EOF &&
230 + EOF
231 git commit -m "to reset" &&
232 git reset HEAD~1 &&
233 git status >actual &&
unpack-trees.c
+5 -1
@@ -1494,6 +1494,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
1494 o->merge_size = len;
1495 mark_all_ce_unused(o->src_index);
1496
1497 + if (o->src_index->fsmonitor_last_update)
1498 + o->result.fsmonitor_last_update = o->src_index->fsmonitor_last_update;
1499 +
1500 /*
1501 * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
1502 */
@@ -2374,7 +2377,8 @@ int oneway_merge(const struct cache_entry * const *src,
2377
2378 if (old && same(old, a)) {
2379 int update = 0;
2377 - if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old)) {
2380 + if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old) &&
2381 + !(old->ce_flags & CE_FSMONITOR_VALID)) {
2382 struct stat st;
2383 if (lstat(old->name, &st) ||
2384 ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))