From 13f8336b2b0603d31320fbb805175dc457db2b74 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Tue, 21 Sep 2021 15:06:33 -0400 Subject: [PATCH] fsmonitor: optimize processing of directory events Teach Git to perform binary search over the cache-entries for a directory notification and then linearly scan forward to find the immediate children. Previously, when the FSMonitor reported a modified directory Git would perform a linear search on the entire cache-entry array for all entries matching that directory prefix and invalidate them. Since the cache-entry array is already sorted, we can use a binary search to find the first matching entry and then only linearly walk forward and invalidate entries until the prefix changes. Also, the original code would invalidate anything having the same directory prefix. Since a directory event should only be received for items that are immediately within the directory (and not within sub-directories of it), only invalidate those entries and not the whole subtree. Signed-off-by: Jeff Hostetler --- fsmonitor.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/fsmonitor.c b/fsmonitor.c index 2befde1ffd..b973e30aa9 100644 --- a/fsmonitor.c +++ b/fsmonitor.c @@ -199,25 +199,24 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name) { int i, len = strlen(name); if (name[len - 1] == '/') { - - /* - * TODO We should binary search to find the first path with - * TODO this directory prefix. Then linearly update entries - * TODO while the prefix matches. Taking care to search without - * TODO the trailing slash -- because '/' sorts after a few - * TODO interesting special chars, like '.' and ' '. - */ + const char *rel; + int pos = index_name_pos(istate, name, len); + if (pos < 0) + pos = -pos - 1; /* Mark all entries for the folder invalid */ - for (i = 0; i < istate->cache_nr; i++) { - if (istate->cache[i]->ce_flags & CE_FSMONITOR_VALID && - starts_with(istate->cache[i]->name, name)) + for (i = pos; i < istate->cache_nr; i++) { + if (!starts_with(istate->cache[i]->name, name)) + break; + /* Only mark the immediate children in the folder */ + rel = istate->cache[i]->name + len; + if (!strchr(rel, '/')) istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID; } /* Need to remove the / from the path for the untracked cache */ name[len - 1] = '\0'; } else { - int pos = index_name_pos(istate, name, strlen(name)); + int pos = index_name_pos(istate, name, len); if (pos >= 0) { struct cache_entry *ce = istate->cache[pos];