Skip to content

Commit 38918c4

Browse files
committed
Merge branch 'td/ls-files-pathspec-prefilter' into next
`git ls-files --modified` and `git ls-files --deleted` have been optimized to filter with pathspec before calling lstat() when there is only a single pathspec item, avoiding unnecessary filesystem access for entries that will not be shown. * td/ls-files-pathspec-prefilter: ls-files: filter pathspec before lstat
2 parents 93340b5 + 3f5203e commit 38918c4

4 files changed

Lines changed: 61 additions & 0 deletions

File tree

builtin/ls-files.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,17 @@ static void show_files(struct repository *repo, struct dir_struct *dir)
453453
continue;
454454
if (ce_skip_worktree(ce))
455455
continue;
456+
/*
457+
* match_pathspec() is linear in pathspec.nr, so prefilter only
458+
* the single-pathspec case. Only entries shown by show_ce()
459+
* satisfy --error-unmatch.
460+
*/
461+
if (pathspec.nr == 1 &&
462+
!match_pathspec(repo->index, &pathspec, fullname.buf,
463+
fullname.len, max_prefix_len, NULL,
464+
S_ISDIR(ce->ce_mode) ||
465+
S_ISGITLINK(ce->ce_mode)))
466+
continue;
456467
stat_err = lstat(fullname.buf, &st);
457468
if (stat_err && (errno != ENOENT && errno != ENOTDIR))
458469
error_errno("cannot lstat '%s'", fullname.buf);

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,7 @@ benchmarks = [
11411141
'perf/p1500-graph-walks.sh',
11421142
'perf/p1501-rev-parse-oneline.sh',
11431143
'perf/p2000-sparse-operations.sh',
1144+
'perf/p3010-ls-files.sh',
11441145
'perf/p3400-rebase.sh',
11451146
'perf/p3404-rebase-interactive.sh',
11461147
'perf/p4000-diff-algorithms.sh',

t/perf/p3010-ls-files.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/sh
2+
3+
test_description='Tests ls-files worktree performance'
4+
5+
. ./perf-lib.sh
6+
7+
test_perf_large_repo
8+
test_checkout_worktree
9+
10+
test_expect_success 'select a zero-prefix pathspec' '
11+
tracked_file=$(git ls-files | sed -n 1p) &&
12+
test -n "$tracked_file" &&
13+
pathspec="?${tracked_file#?}" &&
14+
test_export pathspec
15+
'
16+
17+
test_perf 'ls-files --deleted with pathspec' '
18+
git -c core.fsmonitor=false ls-files --deleted \
19+
-- "$pathspec" >/dev/null
20+
'
21+
22+
test_perf 'ls-files --deleted with all-matching pathspec' '
23+
git -c core.fsmonitor=false ls-files --deleted -- "*" >/dev/null
24+
'
25+
26+
test_perf 'ls-files --modified with pathspec' '
27+
git -c core.fsmonitor=false ls-files --modified \
28+
-- "$pathspec" >/dev/null
29+
'
30+
31+
test_done

t/t3010-ls-files-killed-modified.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,22 @@ test_expect_success 'validate git ls-files -m output.' '
124124
test_cmp .expected .output
125125
'
126126

127+
test_expect_success 'worktree modes honor wildcard pathspecs' '
128+
cat >.expected <<-\EOF &&
129+
path2/file2
130+
path3/file3
131+
EOF
132+
git ls-files --deleted -- "path?/file?" >.output &&
133+
test_cmp .expected .output &&
134+
135+
cat >.expected <<-\EOF &&
136+
path7
137+
path8
138+
EOF
139+
git ls-files --modified --error-unmatch -- "path[78]" >.output &&
140+
test_cmp .expected .output &&
141+
142+
test_must_fail git ls-files --modified --error-unmatch -- path10
143+
'
144+
127145
test_done

0 commit comments

Comments
 (0)