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

[PATCH] dir.c: literal match with wildcard in pathspec should still glob

From
K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
Date
May 3, 2025, 06:07 UTC
Message-ID
<20250503060736.587286-1-jayatheerthkulkarni2005@gmail.com>
In-Reply-To
<xmqqecxk3u5l.fsf@gitster.g>

With a path with wildcard characters, e.g. 'f*o', exists in the working tree, "git add -- 'f*o'" stops after happily finding that there is 'f*o' and adding it to the index, without realizing there may be other paths, e.g. 'foooo', that may match the given pathspec.

This is because dir.c:do_match_pathspec() disables further matches with pathspec when it finds an exact match.

Reported-by: piotrsiupa <piotrsiupa@gmail.com>
Helped-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 | 429 ++++++++++++++++++++++++++
 3 files changed, 432 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..20abad5667
--- /dev/null
+++ b/t/t6137-pathspec-wildcards-literal.sh
@@ -0,0 +1,429 @@
+#!/bin/sh
+test_description='test wildcards and literals with git add/commit (subshell style)'
+
+. ./test-lib.sh
+
+test_have_prereq FUNNYNAMES || {
+	skip_all='skipping: needs FUNNYNAMES (non-Windows only)'
+	test_done
+}
+
+prepare_test_files () {
+	for f in "*" "**" "?" "[abc]" "a" "f*" "f**" "f?z" "foo*bar" "hello?world" "hello_world"
+	do
+		>"$f" || return
+	done
+}
+
+test_expect_success 'add wildcard *' '
+	git init test-asterisk &&
+	(
+		cd test-asterisk &&
+		prepare_test_files &&
+		git add "*" &&
+		cat >expect <<-EOF &&
+		*
+		**
+		?
+		[abc]
+		a
+		f*
+		f**
+		f?z
+		foo*bar
+		hello?world
+		hello_world
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add literal \*' '
+	git init test-asterisk-literal &&
+	(
+		cd test-asterisk-literal &&
+		prepare_test_files &&
+		git add "\*" &&
+		cat >expect <<-EOF &&
+		*
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add wildcard **' '
+	git init test-dstar &&
+	(
+		cd test-dstar &&
+		prepare_test_files &&
+		git add "**" &&
+		cat >expect <<-EOF &&
+		*
+		**
+		?
+		[abc]
+		a
+		f*
+		f**
+		f?z
+		foo*bar
+		hello?world
+		hello_world
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add wildcard ?' '
+	git init test-qmark &&
+	(
+		cd test-qmark &&
+		prepare_test_files &&
+		git add "?" &&
+		cat >expect <<-\EOF | sort &&
+		*
+		?
+		a
+		EOF
+		git ls-files | sort >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add wildcard [abc]' '
+	git init test-brackets &&
+	(
+		cd test-brackets &&
+		prepare_test_files &&
+		git add "[abc]" &&
+		cat >expect <<-\EOF | sort &&
+		[abc]
+		a
+		EOF
+		git ls-files | sort >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add wildcard f*' '
+	git init test-f-wild &&
+	(
+		cd test-f-wild &&
+		prepare_test_files &&
+		git add "f*" &&
+		cat >expect <<-\EOF | sort &&
+		f*
+		f**
+		f?z
+		foo*bar
+		EOF
+		git ls-files | sort >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add literal f\*' '
+	git init test-f-lit &&
+	(
+		cd test-f-lit &&
+		prepare_test_files &&
+		git add "f\*" &&
+		cat >expect <<-\EOF &&
+		f*
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add wildcard f**' '
+	git init test-fdstar &&
+	(
+		cd test-fdstar &&
+		prepare_test_files &&
+		git add "f**" &&
+		cat >expect <<-\EOF | sort &&
+		f*
+		f**
+		f?z
+		foo*bar
+		EOF
+		git ls-files | sort >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add literal f\*\*' '
+	git init test-fdstar-lit &&
+	(
+		cd test-fdstar-lit &&
+		prepare_test_files &&
+		git add "f\*\*" &&
+		cat >expect <<-\EOF &&
+		f**
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add wildcard f?z' '
+	git init test-fqz &&
+	(
+		cd test-fqz &&
+		prepare_test_files &&
+		git add "f?z" &&
+		cat >expect <<-\EOF &&
+		f?z
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add literal \? literal' '
+	git init test-q-lit &&
+	(
+		cd test-q-lit &&
+		prepare_test_files &&
+		git add "\?" &&
+		cat >expect <<-\EOF &&
+		?
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add wildcard foo*bar' '
+	git init test-foobar &&
+	(
+		cd test-foobar &&
+		prepare_test_files &&
+		git add "foo*bar" &&
+		cat >expect <<-\EOF &&
+		foo*bar
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add wildcard hello?world' '
+	git init test-hellowild &&
+	(
+		cd test-hellowild &&
+		prepare_test_files &&
+		git add "hello?world" &&
+		cat >expect <<-\EOF &&
+		hello?world
+		hello_world
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add literal hello\?world' '
+	git init test-hellolit &&
+	(
+		cd test-hellolit &&
+		prepare_test_files &&
+		git add "hello\?world" &&
+		cat >expect <<-\EOF &&
+		hello?world
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'add literal [abc]' '
+	git init test-brackets-lit &&
+	(
+		cd test-brackets-lit &&
+		prepare_test_files &&
+		git add "\[abc\]" &&
+		cat >expect <<-\EOF &&
+		[abc]
+		EOF
+		git ls-files >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'commit: wildcard *' '
+	git init test-c-asterisk &&
+	(
+		cd test-c-asterisk &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c1" -- "*" &&
+		cat >expect <<-EOF &&
+		*
+		**
+		?
+		[abc]
+		a
+		f*
+		f**
+		f?z
+		foo*bar
+		hello?world
+		hello_world
+		EOF
+		git ls-tree -r --name-only HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'commit: literal *' '
+	git init test-c-asterisk-lit &&
+	(
+		cd test-c-asterisk-lit &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c2" -- "\*" &&
+		cat >expect <<-EOF &&
+		*
+		EOF
+		git ls-tree -r --name-only HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'commit: wildcard f*' '
+	git init test-c-fwild &&
+	(
+		cd test-c-fwild &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c3" -- "f*" &&
+		cat >expect <<-EOF &&
+		f*
+		f**
+		f?z
+		foo*bar
+		EOF
+		git ls-tree -r --name-only HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'commit: literal f\*' '
+	git init test-c-flit &&
+	(
+		cd test-c-flit &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c4" -- "f\*" &&
+		cat >expect <<-EOF &&
+		f*
+		EOF
+		git ls-tree -r --name-only HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'commit: wildcard pathspec limits commit' '
+	git init test-c-pathlimit &&
+	(
+		cd test-c-pathlimit &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c5" -- "f**" &&
+		cat >expect <<-EOF &&
+		f*
+		f**
+		f?z
+		foo*bar
+		EOF
+		git ls-tree -r --name-only HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'commit: literal f\*\*' '
+	git init test-c-fdstar-lit &&
+	(
+		cd test-c-fdstar-lit &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c6" -- "f\*\*" &&
+		cat >expect <<-EOF &&
+		f**
+		EOF
+		git ls-tree -r --name-only HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'commit: wildcard ?' '
+	git init test-c-qwild &&
+	(
+		cd test-c-qwild &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c7" -- "?" &&
+		cat >expect <<-EOF &&
+		*
+		?
+		a
+		EOF
+		git ls-tree -r --name-only HEAD | sort >actual &&
+		sort expect >expect.sorted &&
+		test_cmp expect.sorted actual
+	)
+'
+
+test_expect_success 'commit: literal \?' '
+	git init test-c-qlit &&
+	(
+		cd test-c-qlit &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c8" -- "\?" &&
+		cat >expect <<-EOF &&
+		?
+		EOF
+		git ls-tree -r --name-only HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'commit: wildcard hello?world' '
+	git init test-c-hellowild &&
+	(
+		cd test-c-hellowild &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c9" -- "hello?world"  &&
+		cat >expect <<-EOF &&
+		hello?world
+		hello_world
+		EOF
+		git ls-tree -r --name-only HEAD | sort >actual &&
+		sort expect >expect.sorted &&
+		test_cmp expect.sorted actual
+	)
+'
+
+test_expect_success 'commit: literal hello\?world' '
+	git init test-c-hellolit &&
+	(
+		cd test-c-hellolit &&
+		prepare_test_files &&
+		git add . &&
+		git commit -m "c10" -- "hello\?world" &&
+		cat >expect <<-EOF &&
+		hello?world
+		EOF
+		git ls-tree -r --name-only HEAD >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_done
-- 
2.49.GIT
Previous: Junio C HamanoNext: JAYATHEERTH K
Message 3 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.