Merge branch 'en/dir-traversal'

"git clean" and "git ls-files -i" had confusion around working on
or showing ignored paths inside an ignored directory, which has
been corrected.

* en/dir-traversal:
  dir: introduce readdir_skip_dot_and_dotdot() helper
  dir: update stale description of treat_directory()
  dir: traverse into untracked directories if they may have ignored subfiles
  dir: avoid unnecessary traversal into ignored directory
  t3001, t7300: add testcase showcasing missed directory traversal
  t7300: add testcase showing unnecessary traversal into ignored directory
  ls-files: error out on -i unless -o or -c are specified
  dir: report number of visited directories and paths with trace2
  dir: convert trace calls to trace2 equivalents
This commit is contained in:
Junio C Hamano
2021-05-20 08:54:58 +09:00
18 changed files with 298 additions and 172 deletions

View File

@ -116,7 +116,7 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' '
test_expect_success 'Checking XDG ignore file when HOME is unset' '
(sane_unset HOME &&
git config --unset core.excludesfile &&
git ls-files --exclude-standard --ignored >actual) &&
git ls-files --exclude-standard --ignored --others >actual) &&
test_must_be_empty actual
'

View File

@ -292,6 +292,11 @@ EOF
test_cmp expect actual
'
test_expect_success 'ls-files with "**" patterns and --directory' '
# Expectation same as previous test
git ls-files --directory -o -i --exclude "**/a.1" >actual &&
test_cmp expect actual
'
test_expect_success 'ls-files with "**" patterns and no slashes' '
git ls-files -o -i --exclude "one**a.1" >actual &&

View File

@ -29,11 +29,11 @@ test_expect_success 'add file to gitignore' '
'
check_all_output
test_expect_success 'ls-files -i lists only tracked-but-ignored files' '
test_expect_success 'ls-files -i -c lists only tracked-but-ignored files' '
echo content >other-file &&
git add other-file &&
echo file >expect &&
git ls-files -i --exclude-standard >output &&
git ls-files -i -c --exclude-standard >output &&
test_cmp expect output
'

View File

@ -57,6 +57,20 @@ iuc () {
return $ret
}
get_relevant_traces () {
# From the GIT_TRACE2_PERF data of the form
# $TIME $FILE:$LINE | d0 | main | data | r1 | ? | ? | read_directo | $RELEVANT_STAT
# extract the $RELEVANT_STAT fields. We don't care about region_enter
# or region_leave, or stats for things outside read_directory.
INPUT_FILE=$1
OUTPUT_FILE=$2
grep data.*read_directo $INPUT_FILE |
cut -d "|" -f 9 |
grep -v visited \
>"$OUTPUT_FILE"
}
test_lazy_prereq UNTRACKED_CACHE '
{ git update-index --test-untracked-cache; ret=$?; } &&
test $ret -ne 1
@ -129,19 +143,21 @@ EOF
test_expect_success 'status first time (empty cache)' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 3
gitignore invalidation: 1
directory invalidation: 0
opendir: 4
....path:
....node-creation:3
....gitignore-invalidation:1
....directory-invalidation:0
....opendir:4
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'untracked cache after first status' '
@ -151,19 +167,21 @@ test_expect_success 'untracked cache after first status' '
test_expect_success 'status second time (fully populated cache)' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 0
....path:
....node-creation:0
....gitignore-invalidation:0
....directory-invalidation:0
....opendir:0
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'untracked cache after second status' '
@ -174,8 +192,8 @@ test_expect_success 'untracked cache after second status' '
test_expect_success 'modify in root directory, one dir invalidation' '
avoid_racy &&
: >four &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -189,13 +207,15 @@ A two
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 1
opendir: 1
....path:
....node-creation:0
....gitignore-invalidation:0
....directory-invalidation:1
....opendir:1
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
@ -223,8 +243,8 @@ EOF
test_expect_success 'new .gitignore invalidates recursively' '
avoid_racy &&
echo four >.gitignore &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -238,13 +258,15 @@ A two
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 1
directory invalidation: 1
opendir: 4
....path:
....node-creation:0
....gitignore-invalidation:1
....directory-invalidation:1
....opendir:4
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
@ -272,8 +294,8 @@ EOF
test_expect_success 'new info/exclude invalidates everything' '
avoid_racy &&
echo three >>.git/info/exclude &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -285,13 +307,15 @@ A two
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 1
directory invalidation: 0
opendir: 4
....path:
....node-creation:0
....gitignore-invalidation:1
....directory-invalidation:0
....opendir:4
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'verify untracked cache dump' '
@ -330,8 +354,8 @@ EOF
'
test_expect_success 'status after the move' '
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -343,13 +367,15 @@ A one
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 1
....path:
....node-creation:0
....gitignore-invalidation:0
....directory-invalidation:0
....opendir:1
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'verify untracked cache dump' '
@ -389,8 +415,8 @@ EOF
'
test_expect_success 'status after the move' '
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -402,13 +428,15 @@ A two
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 1
....path:
....node-creation:0
....gitignore-invalidation:0
....directory-invalidation:0
....opendir:1
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'verify untracked cache dump' '
@ -438,8 +466,8 @@ test_expect_success 'set up for sparse checkout testing' '
'
test_expect_success 'status after commit' '
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -448,13 +476,15 @@ test_expect_success 'status after commit' '
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 2
....path:
....node-creation:0
....gitignore-invalidation:0
....directory-invalidation:0
....opendir:2
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'untracked cache correct after commit' '
@ -496,9 +526,9 @@ test_expect_success 'create/modify files, some of which are gitignored' '
'
test_expect_success 'test sparse status with untracked cache' '
: >../trace &&
: >../trace.output &&
avoid_racy &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -509,13 +539,15 @@ test_expect_success 'test sparse status with untracked cache' '
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../status.actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 1
directory invalidation: 2
opendir: 2
....path:
....node-creation:0
....gitignore-invalidation:1
....directory-invalidation:2
....opendir:2
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'untracked cache correct after status' '
@ -539,8 +571,8 @@ EOF
test_expect_success 'test sparse status again with untracked cache' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -551,13 +583,15 @@ test_expect_success 'test sparse status again with untracked cache' '
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../status.actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 0
....path:
....node-creation:0
....gitignore-invalidation:0
....directory-invalidation:0
....opendir:0
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'set up for test of subdir and sparse checkouts' '
@ -568,8 +602,8 @@ test_expect_success 'set up for test of subdir and sparse checkouts' '
test_expect_success 'test sparse status with untracked cache and subdir' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
cat >../status.expect <<EOF &&
@ -581,13 +615,15 @@ test_expect_success 'test sparse status with untracked cache and subdir' '
EOF
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../status.actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 2
gitignore invalidation: 0
directory invalidation: 1
opendir: 3
....path:
....node-creation:2
....gitignore-invalidation:0
....directory-invalidation:1
....opendir:3
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
@ -616,19 +652,21 @@ EOF
test_expect_success 'test sparse status again with untracked cache and subdir' '
avoid_racy &&
: >../trace &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
: >../trace.output &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git status --porcelain >../status.actual &&
iuc status --porcelain >../status.iuc &&
test_cmp ../status.expect ../status.iuc &&
test_cmp ../status.expect ../status.actual &&
get_relevant_traces ../trace.output ../trace.relevant &&
cat >../trace.expect <<EOF &&
node creation: 0
gitignore invalidation: 0
directory invalidation: 0
opendir: 0
....path:
....node-creation:0
....gitignore-invalidation:0
....directory-invalidation:0
....opendir:0
EOF
test_cmp ../trace.expect ../trace
test_cmp ../trace.expect ../trace.relevant
'
test_expect_success 'move entry in subdir from untracked to cached' '

View File

@ -746,4 +746,46 @@ test_expect_success 'clean untracked paths by pathspec' '
test_must_be_empty actual
'
test_expect_success 'avoid traversing into ignored directories' '
test_when_finished rm -f output error trace.* &&
test_create_repo avoid-traversing-deep-hierarchy &&
(
cd avoid-traversing-deep-hierarchy &&
mkdir -p untracked/subdir/with/a &&
>untracked/subdir/with/a/random-file.txt &&
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
git clean -ffdxn -e untracked
) &&
# Make sure we only visited into the top-level directory, and did
# not traverse into the "untracked" subdirectory since it was excluded
grep data.*read_directo.*directories-visited trace.output |
cut -d "|" -f 9 >trace.relevant &&
cat >trace.expect <<-EOF &&
..directories-visited:1
EOF
test_cmp trace.expect trace.relevant
'
test_expect_success 'traverse into directories that may have ignored entries' '
test_when_finished rm -f output &&
test_create_repo need-to-traverse-into-hierarchy &&
(
cd need-to-traverse-into-hierarchy &&
mkdir -p modules/foobar/src/generated &&
> modules/foobar/src/generated/code.c &&
> modules/foobar/Makefile &&
echo "/modules/**/src/generated/" >.gitignore &&
git clean -fX modules/foobar >../output &&
grep Removing ../output &&
test_path_is_missing modules/foobar/src/generated/code.c &&
test_path_is_file modules/foobar/Makefile
)
'
test_done

View File

@ -334,7 +334,7 @@ test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR'
git config core.fsmonitor .git/hooks/fsmonitor-test &&
git update-index --untracked-cache &&
git update-index --fsmonitor &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-before" \
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-before" \
git status &&
test-tool dump-untracked-cache >../before
) &&
@ -346,12 +346,12 @@ test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR'
EOF
(
cd dot-git &&
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace-after" \
GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-after" \
git status &&
test-tool dump-untracked-cache >../after
) &&
grep "directory invalidation" trace-before >>before &&
grep "directory invalidation" trace-after >>after &&
grep "directory-invalidation" trace-before | cut -d"|" -f 9 >>before &&
grep "directory-invalidation" trace-after | cut -d"|" -f 9 >>after &&
# UNTR extension unchanged, dir invalidation count unchanged
test_cmp before after
'