Skip to content

Commit 0cacebf

Browse files
pcloudsgitster
authored andcommitted
dir.c: ignore paths containing .git when invalidating untracked cache
read_directory() code ignores all paths named ".git" even if it's not a valid git repository. See treat_path() for details. Since ".git" is basically invisible to read_directory(), when we are asked to invalidate a path that contains ".git", we can safely ignore it because the slow path would not consider it anyway. This helps when fsmonitor is used and we have a real ".git" repo at worktree top. Occasionally .git/index will be updated and if the fsmonitor hook does not filter it, untracked cache is asked to invalidate the path ".git/index". Without this patch, we invalidate the root directory unncessarily, which: - makes read_directory() fall back to slow path for root directory (slower) - makes the index dirty (because UNTR extension is updated). Depending on the index size, writing it down could also be slow. A note about the new "safe_path" knob. Since this new check could be relatively expensive, avoid it when we know it's not needed. If the path comes from the index, it can't contain ".git". If it does contain, we may be screwed up at many more levels, not just this one. Noticed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent b673155 commit 0cacebf

6 files changed

Lines changed: 49 additions & 8 deletions

File tree

dir.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,7 +1712,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
17121712
if (!de)
17131713
return treat_path_fast(dir, untracked, cdir, istate, path,
17141714
baselen, pathspec);
1715-
if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
1715+
if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
17161716
return path_none;
17171717
strbuf_setlen(path, baselen);
17181718
strbuf_addstr(path, de->d_name);
@@ -2909,24 +2909,26 @@ static int invalidate_one_component(struct untracked_cache *uc,
29092909
}
29102910

29112911
void untracked_cache_invalidate_path(struct index_state *istate,
2912-
const char *path)
2912+
const char *path, int safe_path)
29132913
{
29142914
if (!istate->untracked || !istate->untracked->root)
29152915
return;
2916+
if (!safe_path && !verify_path(path))
2917+
return;
29162918
invalidate_one_component(istate->untracked, istate->untracked->root,
29172919
path, strlen(path));
29182920
}
29192921

29202922
void untracked_cache_remove_from_index(struct index_state *istate,
29212923
const char *path)
29222924
{
2923-
untracked_cache_invalidate_path(istate, path);
2925+
untracked_cache_invalidate_path(istate, path, 1);
29242926
}
29252927

29262928
void untracked_cache_add_to_index(struct index_state *istate,
29272929
const char *path)
29282930
{
2929-
untracked_cache_invalidate_path(istate, path);
2931+
untracked_cache_invalidate_path(istate, path, 1);
29302932
}
29312933

29322934
/* Update gitfile and core.worktree setting to connect work tree and git dir */

dir.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ static inline int dir_path_match(const struct dir_entry *ent,
347347
int cmp_dir_entry(const void *p1, const void *p2);
348348
int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
349349

350-
void untracked_cache_invalidate_path(struct index_state *, const char *);
350+
void untracked_cache_invalidate_path(struct index_state *, const char *, int safe_path);
351351
void untracked_cache_remove_from_index(struct index_state *, const char *);
352352
void untracked_cache_add_to_index(struct index_state *, const char *);
353353

fsmonitor.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ static void fsmonitor_refresh_callback(struct index_state *istate, const char *n
130130
* as it could be a new untracked file.
131131
*/
132132
trace_printf_key(&trace_fsmonitor, "fsmonitor_refresh_callback '%s'", name);
133-
untracked_cache_invalidate_path(istate, name);
133+
untracked_cache_invalidate_path(istate, name, 0);
134134
}
135135

136136
void refresh_fsmonitor(struct index_state *istate)

fsmonitor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ static inline void mark_fsmonitor_invalid(struct index_state *istate, struct cac
6565
{
6666
if (core_fsmonitor) {
6767
ce->ce_flags &= ~CE_FSMONITOR_VALID;
68-
untracked_cache_invalidate_path(istate, ce->name);
68+
untracked_cache_invalidate_path(istate, ce->name, 1);
6969
trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_invalid '%s'", ce->name);
7070
}
7171
}

t/t7519-status-fsmonitor.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,43 @@ test_expect_success 'splitting the index results in the same state' '
314314
test_cmp expect actual
315315
'
316316

317+
test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR' '
318+
test_create_repo dot-git &&
319+
(
320+
cd dot-git &&
321+
mkdir -p .git/hooks &&
322+
: >tracked &&
323+
: >modified &&
324+
mkdir dir1 &&
325+
: >dir1/tracked &&
326+
: >dir1/modified &&
327+
mkdir dir2 &&
328+
: >dir2/tracked &&
329+
: >dir2/modified &&
330+
write_integration_script &&
331+
git config core.fsmonitor .git/hooks/fsmonitor-test &&
332+
git update-index --untracked-cache &&
333+
git update-index --fsmonitor &&
334+
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-before" \
335+
git status &&
336+
test-dump-untracked-cache >../before
337+
) &&
338+
cat >>dot-git/.git/hooks/fsmonitor-test <<-\EOF &&
339+
printf ".git\0"
340+
printf ".git/index\0"
341+
printf "dir1/.git\0"
342+
printf "dir1/.git/index\0"
343+
EOF
344+
(
345+
cd dot-git &&
346+
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-after" \
347+
git status &&
348+
test-dump-untracked-cache >../after
349+
) &&
350+
grep "directory invalidation" trace-before >>before &&
351+
grep "directory invalidation" trace-after >>after &&
352+
# UNTR extension unchanged, dir invalidation count unchanged
353+
test_cmp before after
354+
'
355+
317356
test_done

unpack-trees.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1506,7 +1506,7 @@ static void invalidate_ce_path(const struct cache_entry *ce,
15061506
if (!ce)
15071507
return;
15081508
cache_tree_invalidate_path(o->src_index, ce->name);
1509-
untracked_cache_invalidate_path(o->src_index, ce->name);
1509+
untracked_cache_invalidate_path(o->src_index, ce->name, 1);
15101510
}
15111511

15121512
/*

0 commit comments

Comments
 (0)