git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2] Dir: Fix and test wildcard pathspec handling

From
K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
Date
Apr 22, 2025, 16:05 UTC
Message-ID
<20250422160547.577524-1-jayatheerthkulkarni2005@gmail.com>

Ensure wildcards expand, even with literal file match. Fixes 'git add f*' skipping files like 'foo' if 'f*' exists. Use 'f\*' to add the literal. Tests added for add and commit where dir.c logic applies. Skips windows specific test.

reported-by: piotrsiupa <piotrsiupa@gmail.com>
Mentored-by: Jeff King <peff@peff.net>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 dir.c                                 |   3 +-
 t/meson.build                         |   1 +
 t/t6137-pathspec-wildcards-literal.sh | 282 ++++++++++++++++++++++++++
 3 files changed, 285 insertions(+), 1 deletion(-)
 create mode 100755 t/t6137-pathspec-wildcards-literal.sh
diff --git a/dir.c b/dir.c
index 28b0e03feb..9405fee83a 100644
--- a/dir.c
+++ b/dir.c
@@ -519,7 +519,8 @@ static int do_match_pathspec(struct index_state *istate,
 		    ( exclude && !(ps->items[i].magic & PATHSPEC_EXCLUDE)))
 			continue;
 
-		if (seen && seen[i] == MATCHED_EXACTLY)
+		if (seen && seen[i] == MATCHED_EXACTLY &&
+			ps->items[i].nowildcard_len == ps->items[i].len)
 			continue;
 		/*
 		 * Make exclude patterns optional and never report
diff --git a/t/meson.build b/t/meson.build
index bfb744e886..61285852e9 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -788,6 +788,7 @@ integration_tests = [
   't6134-pathspec-in-submodule.sh',
   't6135-pathspec-with-attrs.sh',
   't6136-pathspec-in-bare.sh',
+  't6137-pathspec-wildcards-literal.sh',
   't6200-fmt-merge-msg.sh',
   't6300-for-each-ref.sh',
   't6301-for-each-ref-errors.sh',
diff --git a/t/t6137-pathspec-wildcards-literal.sh b/t/t6137-pathspec-wildcards-literal.sh
new file mode 100755
index 0000000000..abf837bf6c
--- /dev/null
+++ b/t/t6137-pathspec-wildcards-literal.sh
@@ -0,0 +1,282 @@
+#!/bin/sh
+
+test_description='test wildcards and literals with various git commands'
+
+. ./test-lib.sh
+
+test_have_prereq FUNNYNAMES || {
+    skip_all='skipping: needs FUNNYNAMES (non-Windows only)'
+    test_done
+}
+
+reset_git_repo () {
+    rm -rf .git &&
+    git init &&
+    rm -rf "actual_files" "expected_files"
+}
+
+end_test_properly() {
+    cd .. &&
+    rm -rf "testdir"
+}
+
+
+test_expect_success 'setup' '
+    mkdir testdir &&
+    cd testdir &&
+    touch "*" "?" "[abc]" "f*" "f?z" "a" &&
+    touch "**" "foo*bar" "hello?world" "f**" "hello_world" &&
+    git init
+'
+
+test_expect_success 'check * wildcard in git add' '
+    git init &&
+    git add "*" &&
+    cat >expected_files <<EOF &&
+*
+**
+?
+[abc]
+a
+f*
+f**
+f?z
+foo*bar
+hello?world
+hello_world
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check \* literal in git add' '
+    reset_git_repo &&
+    git add "\*" &&
+    cat >expected_files <<EOF &&
+*
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check f* wildcard in git add' '
+    reset_git_repo &&
+    git add "f*" &&
+    cat >expected_files <<EOF &&
+f*
+f**
+f?z
+foo*bar
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check f\* literal in git add' '
+    reset_git_repo &&
+    git add "f\*" &&
+    cat >expected_files <<EOF &&
+f*
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check f** wildcard in git add' '
+    reset_git_repo &&
+    git add "f**" &&
+    cat >expected_files <<EOF &&
+f*
+f**
+f?z
+foo*bar
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check f\*\* literal in git add' '
+    reset_git_repo &&
+    git add "f\*\*" &&
+    cat >expected_files <<EOF &&
+f**
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check ? wildcard in git add' '
+    reset_git_repo &&
+    git add "?" &&
+    cat >expected_files <<EOF &&
+*
+?
+a
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check \? literal in git add' '
+    reset_git_repo &&
+    git add "\?" &&
+    cat >expected_files <<EOF &&
+?
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check hello?world wildcard in git add' '
+    reset_git_repo &&
+    git add "hello?world" &&
+    cat >expected_files <<EOF &&
+hello?world
+hello_world
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'check hello\?world literal in git add' '
+    reset_git_repo &&
+    git add "hello\?world" &&
+    cat >expected_files <<EOF &&
+hello?world
+EOF
+    git ls-files >actual_files &&
+    test_cmp expected_files actual_files
+'
+
+test_expect_success 'commit: wildcard *' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "*" &&
+    cat >expected_files <<-\EOF &&
+*
+**
+?
+[abc]
+a
+f*
+f**
+f?z
+foo*bar
+hello?world
+hello_world
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'commit: literal *' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "\*" &&
+    cat >expected_files <<-\EOF &&
+*
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'commit: wildcard f*' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "f*" &&
+    cat >expected_files <<-\EOF &&
+f*
+f**
+f?z
+foo*bar
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'commit: literal f\*' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "f\*" &&
+    cat >expected_files <<-\EOF &&
+f*
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'commit: wildcard pathspec limits commit' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "f**" &&
+    cat >expected_files <<-\EOF &&
+f*
+f**
+f?z
+foo*bar
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'commit: literal f\*\*' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "f\*\*" &&
+    cat >expected_files <<-\EOF &&
+f**
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'commit: wildcard ?' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "?" &&
+    cat >expected_files <<-\EOF &&
+*
+?
+a
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'commit: literal \?' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "\?" &&
+    cat >expected_files <<-\EOF &&
+?
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'check hello?world wildcard in git commit' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "hello?world" &&
+    cat >expected_files <<-\EOF &&
+hello?world
+hello_world
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+test_expect_success 'check hello\?world literal in git commit' '
+    reset_git_repo &&
+    git add . &&
+    git commit --allow-empty -m "Test" -- "hello\?world" &&
+    cat >expected_files <<-\EOF &&
+hello?world
+EOF
+    git ls-tree -r --name-only HEAD > actual_files &&
+    test_cmp expected_files actual_files 
+'
+
+end_test_properly
+
+test_done
-- 
2.49.0.223.ga3111b2db4.dirty
Next: Junio C Hamano
Message 1 of 7 in “Dir: Fix and test wildcard pathspec handling”
  1. Dir: Fix and test wildcard pathspec handlingK Jayatheerth, Apr 22, 2025
  2. Junio C HamanoApr 22, 2025
  3. dir.c: literal match with wildcard in pathspec should still globK Jayatheerth, May 3, 2025
  4. JAYATHEERTH KMay 3, 2025
  5. Junio C HamanoMay 5, 2025
  6. dir.c: literal match with wildcard in pathspec should still globK Jayatheerth, May 5, 2025
  7. JAYATHEERTH KMay 5, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.