Browse Source

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 <jeffhost@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Jeff Hostetler 2 years ago committed by Junio C Hamano
parent
commit
95a4e78a74
  1. 69
      fsmonitor.c

69
fsmonitor.c

@ -184,30 +184,68 @@ static int query_fsmonitor_hook(struct repository *r,
static void fsmonitor_refresh_callback(struct index_state *istate, char *name) static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
{ {
int i, len = strlen(name); int i, len = strlen(name);
if (name[len - 1] == '/') { int pos = index_name_pos(istate, name, len);

trace_printf_key(&trace_fsmonitor,
"fsmonitor_refresh_callback '%s' (pos %d)",
name, pos);


if (name[len - 1] == '/') {
/* /*
* TODO We should binary search to find the first path with * The daemon can decorate directory events, such as
* TODO this directory prefix. Then linearly update entries * moves or renames, with a trailing slash if the OS
* TODO while the prefix matches. Taking care to search without * FS Event contains sufficient information, such as
* TODO the trailing slash -- because '/' sorts after a few * MacOS.
* TODO interesting special chars, like '.' and ' '. *
* Use this to invalidate the entire cone under that
* directory.
*
* We do not expect an exact match because the index
* does not normally contain directory entries, so we
* start at the insertion point and scan.
*/ */
if (pos < 0)
pos = -pos - 1;


/* Mark all entries for the folder invalid */ /* Mark all entries for the folder invalid */
for (i = 0; i < istate->cache_nr; i++) { for (i = pos; i < istate->cache_nr; i++) {
if (istate->cache[i]->ce_flags & CE_FSMONITOR_VALID && if (!starts_with(istate->cache[i]->name, name))
starts_with(istate->cache[i]->name, name)) break;
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID; istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
} }
/* Need to remove the / from the path for the untracked cache */
/*
* We need to remove the traling "/" from the path
* for the untracked cache.
*/
name[len - 1] = '\0'; name[len - 1] = '\0';
} else if (pos >= 0) {
/*
* We have an exact match for this path and can just
* invalidate it.
*/
istate->cache[pos]->ce_flags &= ~CE_FSMONITOR_VALID;
} else { } else {
int pos = index_name_pos(istate, name, strlen(name)); /*

* The path is not a tracked file -or- it is a
if (pos >= 0) { * directory event on a platform that cannot
struct cache_entry *ce = istate->cache[pos]; * distinguish between file and directory events in
ce->ce_flags &= ~CE_FSMONITOR_VALID; * the event handler, such as Windows.
*
* Scan as if it is a directory and invalidate the
* cone under it. (But remember to ignore items
* between "name" and "name/", such as "name-" and
* "name.".
*/
pos = -pos - 1;

for (i = pos; i < istate->cache_nr; i++) {
if (!starts_with(istate->cache[i]->name, name))
break;
if ((unsigned char)istate->cache[i]->name[len] > '/')
break;
if (istate->cache[i]->name[len] == '/')
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
} }
} }


@ -215,7 +253,6 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
* Mark the untracked cache dirty even if it wasn't found in the index * Mark the untracked cache dirty even if it wasn't found in the index
* as it could be a new untracked file. * as it could be a new untracked file.
*/ */
trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
untracked_cache_invalidate_path(istate, name, 0); untracked_cache_invalidate_path(istate, name, 0);
} }



Loading…
Cancel
Save