Skip to content

Commit a98d291

Browse files
authored
Merge pull request git-for-windows#3456 from jeffhostetler/try-v4-fsmonitor-part5
FSMonitor: deepening a directory causes confusing events
2 parents 0055932 + 4c3206e commit a98d291

File tree

2 files changed

+99
-13
lines changed

2 files changed

+99
-13
lines changed

fsmonitor.c

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -198,37 +198,75 @@ int fsmonitor_is_trivial_response(const struct strbuf *query_result)
198198
static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
199199
{
200200
int i, len = strlen(name);
201+
int pos = index_name_pos(istate, name, len);
202+
203+
trace_printf_key(&trace_fsmonitor,
204+
"fsmonitor_refresh_callback '%s' (pos %d)",
205+
name, pos);
206+
201207
if (name[len - 1] == '/') {
202-
const char *rel;
203-
int pos = index_name_pos(istate, name, len);
208+
/*
209+
* The daemon can decorate directory events, such as
210+
* moves or renames, with a trailing slash if the OS
211+
* FS Event contains sufficient information, such as
212+
* MacOS.
213+
*
214+
* Use this to invalidate the entire cone under that
215+
* directory.
216+
*
217+
* We do not expect an exact match because the index
218+
* does not normally contain directory entries, so we
219+
* start at the insertion point and scan.
220+
*/
204221
if (pos < 0)
205222
pos = -pos - 1;
206223

207224
/* Mark all entries for the folder invalid */
208225
for (i = pos; i < istate->cache_nr; i++) {
209226
if (!starts_with(istate->cache[i]->name, name))
210227
break;
211-
/* Only mark the immediate children in the folder */
212-
rel = istate->cache[i]->name + len;
213-
if (!strchr(rel, '/'))
214-
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
228+
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
215229
}
216-
/* Need to remove the / from the path for the untracked cache */
230+
231+
/*
232+
* We need to remove the traling "/" from the path
233+
* for the untracked cache.
234+
*/
217235
name[len - 1] = '\0';
236+
} else if (pos >= 0) {
237+
/*
238+
* We have an exact match for this path and can just
239+
* invalidate it.
240+
*/
241+
istate->cache[pos]->ce_flags &= ~CE_FSMONITOR_VALID;
218242
} else {
219-
int pos = index_name_pos(istate, name, len);
243+
/*
244+
* The path is not a tracked file -or- it is a
245+
* directory event on a platform that cannot
246+
* distinguish between file and directory events in
247+
* the event handler, such as Windows.
248+
*
249+
* Scan as if it is a directory and invalidate the
250+
* cone under it. (But remember to ignore items
251+
* between "name" and "name/", such as "name-" and
252+
* "name.".
253+
*/
254+
pos = -pos - 1;
220255

221-
if (pos >= 0) {
222-
struct cache_entry *ce = istate->cache[pos];
223-
ce->ce_flags &= ~CE_FSMONITOR_VALID;
256+
for (i = pos; i < istate->cache_nr; i++) {
257+
if (!starts_with(istate->cache[i]->name, name))
258+
break;
259+
if ((unsigned char)istate->cache[i]->name[len] > '/')
260+
break;
261+
if (istate->cache[i]->name[len] == '/')
262+
istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
224263
}
225264
}
226265

227266
/*
228267
* Mark the untracked cache dirty even if it wasn't found in the index
229268
* as it could be a new untracked file.
230269
*/
231-
trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
232270
untracked_cache_invalidate_path(istate, name, 0);
233271
}
234272

t/t7527-builtin-fsmonitor.sh

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,16 @@ test_expect_success 'setup' '
261261
trace*
262262
EOF
263263
264+
mkdir -p T1/T2/T3/T4 &&
265+
echo 1 >T1/F1 &&
266+
echo 1 >T1/T2/F1 &&
267+
echo 1 >T1/T2/T3/F1 &&
268+
echo 1 >T1/T2/T3/T4/F1 &&
269+
echo 2 >T1/F2 &&
270+
echo 2 >T1/T2/F2 &&
271+
echo 2 >T1/T2/T3/F2 &&
272+
echo 2 >T1/T2/T3/T4/F2 &&
273+
264274
git -c core.useBuiltinFSMonitor= add . &&
265275
test_tick &&
266276
git -c core.useBuiltinFSMonitor= commit -m initial &&
@@ -354,6 +364,19 @@ verify_status() {
354364
echo HELLO AFTER
355365
}
356366

367+
move_directory_contents_deeper() {
368+
mkdir T1/_new_
369+
mv T1/[A-Z]* T1/_new_
370+
}
371+
372+
move_directory_up() {
373+
mv T1/T2/T3 T1
374+
}
375+
376+
move_directory() {
377+
mv T1/T2/T3 T1/T2/NewT3
378+
}
379+
357380
# The next few test cases confirm that our fsmonitor daemon sees each type
358381
# of OS filesystem notification that we care about. At this layer we just
359382
# ensure we are getting the OS notifications and do not try to confirm what
@@ -622,8 +645,8 @@ test_expect_success 'Matrix: setup for untracked-cache,fsmonitor matrix' '
622645
'
623646

624647
matrix_clean_up_repo () {
625-
git reset --hard HEAD
626648
git clean -fd
649+
git reset --hard HEAD
627650
}
628651

629652
matrix_try () {
@@ -687,6 +710,31 @@ do
687710
matrix_try $uc_val $fsm_val rename_files
688711
matrix_try $uc_val $fsm_val file_to_directory
689712
matrix_try $uc_val $fsm_val directory_to_file
713+
714+
# NEEDSWORK: On Windows the untracked-cache is buggy when FSMonitor
715+
# is DISABLED. Turn off a few test that cause it problems until
716+
# we can debug it.
717+
#
718+
try_moves="true"
719+
test_have_prereq UNTRACKED_CACHE,WINDOWS && \
720+
test $uc_val = true && \
721+
test $fsm_val = false && \
722+
try_moves="false"
723+
if test $try_moves = true
724+
then
725+
matrix_try $uc_val $fsm_val move_directory_contents_deeper
726+
matrix_try $uc_val $fsm_val move_directory_up
727+
matrix_try $uc_val $fsm_val move_directory
728+
fi
729+
730+
if test $fsm_val = true
731+
then
732+
test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor at end" '
733+
test_might_fail git config --unset core.useBuiltinFSMonitor &&
734+
git update-index --no-fsmonitor &&
735+
test_might_fail git fsmonitor--daemon stop 2>/dev/null
736+
'
737+
fi
690738
done
691739
done
692740

0 commit comments

Comments
 (0)