]> git.ipfire.org Git - thirdparty/git.git/commitdiff
unpack-trees: skip stat on fsmonitor-valid files
authorUtsav Shah <utsav@dropbox.com>
Wed, 20 Nov 2019 08:32:17 +0000 (08:32 +0000)
committerJunio C Hamano <gitster@pobox.com>
Thu, 21 Nov 2019 03:48:18 +0000 (12:48 +0900)
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>
fsmonitor.c
t/t7519-status-fsmonitor.sh
unpack-trees.c

index 0477500b392b2baee699b51f2afa39381e66504d..868cca01e250896054b1f7025c7180c87ab1b88d 100644 (file)
@@ -191,13 +191,26 @@ void refresh_fsmonitor(struct index_state *istate)
                }
                if (bol < query_result.len)
                        fsmonitor_refresh_callback(istate, buf + bol);
+
+               /* Now mark the untracked cache for fsmonitor usage */
+               if (istate->untracked)
+                       istate->untracked->use_fsmonitor = 1;
        } else {
+
+               /* We only want to run the post index changed hook if we've actually changed entries, so keep track
+                * if we actually changed entries or not */
+               int is_cache_changed = 0;
                /* Mark all entries invalid */
-               for (i = 0; i < istate->cache_nr; i++)
-                       istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
+               for (i = 0; i < istate->cache_nr; i++) {
+                       if (istate->cache[i]->ce_flags & CE_FSMONITOR_VALID) {
+                               is_cache_changed = 1;
+                               istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
+                       }
+               }
 
                /* If we're going to check every file, ensure we save the results */
-               istate->cache_changed |= FSMONITOR_CHANGED;
+               if (is_cache_changed)
+                       istate->cache_changed |= FSMONITOR_CHANGED;
 
                if (istate->untracked)
                        istate->untracked->use_fsmonitor = 0;
@@ -259,9 +272,7 @@ void tweak_fsmonitor(struct index_state *istate)
                                    (uintmax_t)istate->fsmonitor_dirty->bit_size, istate->cache_nr);
                        ewah_each_bit(istate->fsmonitor_dirty, fsmonitor_ewah_callback, istate);
 
-                       /* Now mark the untracked cache for fsmonitor usage */
-                       if (istate->untracked)
-                               istate->untracked->use_fsmonitor = 1;
+                       refresh_fsmonitor(istate);
                }
 
                ewah_free(istate->fsmonitor_dirty);
index 997d5fb349dfd7e5ee068d2c34b3e091f678f891..87cf1dd19dc1351b12a9f9e343b68c7c4395fff1 100755 (executable)
@@ -106,6 +106,8 @@ EOF
 
 # test that "update-index --fsmonitor-valid" sets the fsmonitor valid bit
 test_expect_success 'update-index --fsmonitor-valid" sets the fsmonitor valid bit' '
+       write_script .git/hooks/fsmonitor-test<<-\EOF &&
+       EOF
        git update-index --fsmonitor &&
        git update-index --fsmonitor-valid dir1/modified &&
        git update-index --fsmonitor-valid dir2/modified &&
@@ -164,6 +166,8 @@ EOF
 
 # test that newly added files are marked valid
 test_expect_success 'newly added files are marked valid' '
+       write_script .git/hooks/fsmonitor-test<<-\EOF &&
+       EOF
        git add new &&
        git add dir1/new &&
        git add dir2/new &&
@@ -218,11 +222,12 @@ test_expect_success '*only* files returned by the integration script get flagged
 # Ensure commands that call refresh_index() to move the index back in time
 # properly invalidate the fsmonitor cache
 test_expect_success 'refresh_index() invalidates fsmonitor cache' '
-       write_script .git/hooks/fsmonitor-test<<-\EOF &&
-       EOF
        clean_repo &&
        dirty_repo &&
+       write_integration_script &&
        git add . &&
+       write_script .git/hooks/fsmonitor-test<<-\EOF &&
+       EOF
        git commit -m "to reset" &&
        git reset HEAD~1 &&
        git status >actual &&
index 62276d4fef5f67e7147377f16e24e30abca0906a..35313d1ac464cf6593885ba4a7fce54f2bcf2c8e 100644 (file)
@@ -1494,6 +1494,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);
 
+       if (o->src_index->fsmonitor_last_update)
+               o->result.fsmonitor_last_update = o->src_index->fsmonitor_last_update;
+
        /*
         * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
         */
@@ -2374,7 +2377,8 @@ int oneway_merge(const struct cache_entry * const *src,
 
        if (old && same(old, a)) {
                int update = 0;
-               if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old)) {
+               if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old) &&
+                       !(old->ce_flags & CE_FSMONITOR_VALID)) {
                        struct stat st;
                        if (lstat(old->name, &st) ||
                            ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))