show_files() checks whether each index entry is deleted or modified before show_ce() applies the pathspec. prune_index() avoids most of this work for pathspecs with a common directory prefix, but a top-level name or leading wildcard leaves every entry to be checked.
Match the pathspec before lstat() for the deleted and modified modes. Keep the later match in show_ce() so --error-unmatch is satisfied only by entries that are actually shown.
On a repository with 859,211 index entries, a 19,931,862-byte index, and 25,303,439 packed objects occupying 21.13 GiB, I ran the following command with the parent and patched binaries:
hyperfine --warmup 0 --runs 3 \
'git -c core.fsmonitor=false ls-files --deleted -- README.md'The results were:
parent this commit elapsed 60.742 s 1.061 s user 1.117 s 0.963 s system 10.740 s 0.042 s
Both revisions were built with -O3, -mcpu=native, and ThinLTO using Apple clang 21.0.0 on macOS 26.5. The machine was a MacBook Pro (Mac16,6) with a 16-core Apple M4 Max (12 performance and four efficiency cores) and 128 GB RAM.
Assisted-by: Codex gpt-5.5 Signed-off-by: Tamir Duberstein <tamird@gmail.com> --- builtin/ls-files.c | 7 +++++++ t/meson.build | 1 + t/perf/p3010-ls-files.sh | 27 +++++++++++++++++++++++++++ t/t3010-ls-files-killed-modified.sh | 18 ++++++++++++++++++ 4 files changed, 53 insertions(+)
Show changes to 4 files +53 −1
builtin/ls-files.c, t/meson.build, t/perf/p3010-ls-files.sh, t/t3010-ls-files-killed-modified.sh
diff --git a/builtin/ls-files.c b/builtin/ls-files.c index e1a22b41b9..702c607183 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -450,6 +450,13 @@ static void show_files(struct repository *repo, struct dir_struct *dir) continue; if (ce_skip_worktree(ce)) continue; + /* Only entries shown by show_ce() satisfy --error-unmatch. */ + if (pathspec.nr && + !match_pathspec(repo->index, &pathspec, fullname.buf, + fullname.len, max_prefix_len, NULL, + S_ISDIR(ce->ce_mode) || + S_ISGITLINK(ce->ce_mode))) + continue; stat_err = lstat(fullname.buf, &st); if (stat_err && (errno != ENOENT && errno != ENOTDIR)) error_errno("cannot lstat '%s'", fullname.buf); diff --git a/t/meson.build b/t/meson.build index 2af8d01279..ee8086e6ef 100644 --- a/t/meson.build +++ b/t/meson.build @@ -1140,6 +1140,7 @@ benchmarks = [ 'perf/p1500-graph-walks.sh', 'perf/p1501-rev-parse-oneline.sh', 'perf/p2000-sparse-operations.sh', + 'perf/p3010-ls-files.sh', 'perf/p3400-rebase.sh', 'perf/p3404-rebase-interactive.sh', 'perf/p4000-diff-algorithms.sh', diff --git a/t/perf/p3010-ls-files.sh b/t/perf/p3010-ls-files.sh new file mode 100755 index 0000000000..bb80768063 --- /dev/null +++ b/t/perf/p3010-ls-files.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +test_description='Tests ls-files worktree performance' + +. ./perf-lib.sh + +test_perf_large_repo +test_checkout_worktree + +test_expect_success 'select a zero-prefix pathspec' ' + tracked_file=$(git ls-files | sed -n 1p) && + test -n "$tracked_file" && + pathspec="?${tracked_file#?}" && + test_export pathspec +' + +test_perf 'ls-files --deleted with pathspec' ' + git -c core.fsmonitor=false ls-files --deleted \ + -- "$pathspec" >/dev/null +' + +test_perf 'ls-files --modified with pathspec' ' + git -c core.fsmonitor=false ls-files --modified \ + -- "$pathspec" >/dev/null +' + +test_done diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh index 7af4532cd1..6e38e10219 100755 --- a/t/t3010-ls-files-killed-modified.sh +++ b/t/t3010-ls-files-killed-modified.sh @@ -124,4 +124,22 @@ test_expect_success 'validate git ls-files -m output.' ' test_cmp .expected .output ' +test_expect_success 'worktree modes honor wildcard pathspecs' ' + cat >.expected <<-\EOF && + path2/file2 + path3/file3 + EOF + git ls-files --deleted -- "path?/file?" >.output && + test_cmp .expected .output && + + cat >.expected <<-\EOF && + path7 + path8 + EOF + git ls-files --modified --error-unmatch -- "path[78]" >.output && + test_cmp .expected .output && + + test_must_fail git ls-files --modified --error-unmatch -- path10 +' + test_done --- base-commit: 9ac3f193c05c2237e2b14ebaa1149e9fc8a1abe0 change-id: 20260607-ls-files-pathspec-lstat-885125a5d644 Best regards, -- Tamir Duberstein <tamird@gmail.com>