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

When 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> Signed-off-by: Junio C Hamano <gitster@pobox.com>

K Jayatheerth committed May 3, 2025 at 11:37 UTC ec727e189cce9e8457e2b00e0756cfdf325a12d9
3 files changed +432 -1
dir.c
+2 -1
@@ -519,7 +519,8 @@ static int do_match_pathspec(struct index_state *istate,
519 ( exclude && !(ps->items[i].magic & PATHSPEC_EXCLUDE)))
520 continue;
521
522 - if (seen && seen[i] == MATCHED_EXACTLY)
522 + if (seen && seen[i] == MATCHED_EXACTLY &&
523 + ps->items[i].nowildcard_len == ps->items[i].len)
524 continue;
525 /*
526 * Make exclude patterns optional and never report
t/meson.build
+1
@@ -787,6 +787,7 @@ integration_tests = [
787 't6134-pathspec-in-submodule.sh',
788 't6135-pathspec-with-attrs.sh',
789 't6136-pathspec-in-bare.sh',
790 + 't6137-pathspec-wildcards-literal.sh',
791 't6200-fmt-merge-msg.sh',
792 't6300-for-each-ref.sh',
793 't6301-for-each-ref-errors.sh',
t/t6137-pathspec-wildcards-literal.sh new
+429
@@ -0,0 +1,429 @@
1 +#!/bin/sh
2 +test_description='test wildcards and literals with git add/commit (subshell style)'
3 +
4 +. ./test-lib.sh
5 +
6 +test_have_prereq FUNNYNAMES || {
7 + skip_all='skipping: needs FUNNYNAMES (non-Windows only)'
8 + test_done
9 +}
10 +
11 +prepare_test_files () {
12 + for f in "*" "**" "?" "[abc]" "a" "f*" "f**" "f?z" "foo*bar" "hello?world" "hello_world"
13 + do
14 + >"$f" || return
15 + done
16 +}
17 +
18 +test_expect_success 'add wildcard *' '
19 + git init test-asterisk &&
20 + (
21 + cd test-asterisk &&
22 + prepare_test_files &&
23 + git add "*" &&
24 + cat >expect <<-EOF &&
25 + *
26 + **
27 + ?
28 + [abc]
29 + a
30 + f*
31 + f**
32 + f?z
33 + foo*bar
34 + hello?world
35 + hello_world
36 + EOF
37 + git ls-files >actual &&
38 + test_cmp expect actual
39 + )
40 +'
41 +
42 +test_expect_success 'add literal \*' '
43 + git init test-asterisk-literal &&
44 + (
45 + cd test-asterisk-literal &&
46 + prepare_test_files &&
47 + git add "\*" &&
48 + cat >expect <<-EOF &&
49 + *
50 + EOF
51 + git ls-files >actual &&
52 + test_cmp expect actual
53 + )
54 +'
55 +
56 +test_expect_success 'add wildcard **' '
57 + git init test-dstar &&
58 + (
59 + cd test-dstar &&
60 + prepare_test_files &&
61 + git add "**" &&
62 + cat >expect <<-EOF &&
63 + *
64 + **
65 + ?
66 + [abc]
67 + a
68 + f*
69 + f**
70 + f?z
71 + foo*bar
72 + hello?world
73 + hello_world
74 + EOF
75 + git ls-files >actual &&
76 + test_cmp expect actual
77 + )
78 +'
79 +
80 +test_expect_success 'add wildcard ?' '
81 + git init test-qmark &&
82 + (
83 + cd test-qmark &&
84 + prepare_test_files &&
85 + git add "?" &&
86 + cat >expect <<-\EOF | sort &&
87 + *
88 + ?
89 + a
90 + EOF
91 + git ls-files | sort >actual &&
92 + test_cmp expect actual
93 + )
94 +'
95 +
96 +test_expect_success 'add wildcard [abc]' '
97 + git init test-brackets &&
98 + (
99 + cd test-brackets &&
100 + prepare_test_files &&
101 + git add "[abc]" &&
102 + cat >expect <<-\EOF | sort &&
103 + [abc]
104 + a
105 + EOF
106 + git ls-files | sort >actual &&
107 + test_cmp expect actual
108 + )
109 +'
110 +
111 +test_expect_success 'add wildcard f*' '
112 + git init test-f-wild &&
113 + (
114 + cd test-f-wild &&
115 + prepare_test_files &&
116 + git add "f*" &&
117 + cat >expect <<-\EOF | sort &&
118 + f*
119 + f**
120 + f?z
121 + foo*bar
122 + EOF
123 + git ls-files | sort >actual &&
124 + test_cmp expect actual
125 + )
126 +'
127 +
128 +test_expect_success 'add literal f\*' '
129 + git init test-f-lit &&
130 + (
131 + cd test-f-lit &&
132 + prepare_test_files &&
133 + git add "f\*" &&
134 + cat >expect <<-\EOF &&
135 + f*
136 + EOF
137 + git ls-files >actual &&
138 + test_cmp expect actual
139 + )
140 +'
141 +
142 +test_expect_success 'add wildcard f**' '
143 + git init test-fdstar &&
144 + (
145 + cd test-fdstar &&
146 + prepare_test_files &&
147 + git add "f**" &&
148 + cat >expect <<-\EOF | sort &&
149 + f*
150 + f**
151 + f?z
152 + foo*bar
153 + EOF
154 + git ls-files | sort >actual &&
155 + test_cmp expect actual
156 + )
157 +'
158 +
159 +test_expect_success 'add literal f\*\*' '
160 + git init test-fdstar-lit &&
161 + (
162 + cd test-fdstar-lit &&
163 + prepare_test_files &&
164 + git add "f\*\*" &&
165 + cat >expect <<-\EOF &&
166 + f**
167 + EOF
168 + git ls-files >actual &&
169 + test_cmp expect actual
170 + )
171 +'
172 +
173 +test_expect_success 'add wildcard f?z' '
174 + git init test-fqz &&
175 + (
176 + cd test-fqz &&
177 + prepare_test_files &&
178 + git add "f?z" &&
179 + cat >expect <<-\EOF &&
180 + f?z
181 + EOF
182 + git ls-files >actual &&
183 + test_cmp expect actual
184 + )
185 +'
186 +
187 +test_expect_success 'add literal \? literal' '
188 + git init test-q-lit &&
189 + (
190 + cd test-q-lit &&
191 + prepare_test_files &&
192 + git add "\?" &&
193 + cat >expect <<-\EOF &&
194 + ?
195 + EOF
196 + git ls-files >actual &&
197 + test_cmp expect actual
198 + )
199 +'
200 +
201 +test_expect_success 'add wildcard foo*bar' '
202 + git init test-foobar &&
203 + (
204 + cd test-foobar &&
205 + prepare_test_files &&
206 + git add "foo*bar" &&
207 + cat >expect <<-\EOF &&
208 + foo*bar
209 + EOF
210 + git ls-files >actual &&
211 + test_cmp expect actual
212 + )
213 +'
214 +
215 +test_expect_success 'add wildcard hello?world' '
216 + git init test-hellowild &&
217 + (
218 + cd test-hellowild &&
219 + prepare_test_files &&
220 + git add "hello?world" &&
221 + cat >expect <<-\EOF &&
222 + hello?world
223 + hello_world
224 + EOF
225 + git ls-files >actual &&
226 + test_cmp expect actual
227 + )
228 +'
229 +
230 +test_expect_success 'add literal hello\?world' '
231 + git init test-hellolit &&
232 + (
233 + cd test-hellolit &&
234 + prepare_test_files &&
235 + git add "hello\?world" &&
236 + cat >expect <<-\EOF &&
237 + hello?world
238 + EOF
239 + git ls-files >actual &&
240 + test_cmp expect actual
241 + )
242 +'
243 +
244 +test_expect_success 'add literal [abc]' '
245 + git init test-brackets-lit &&
246 + (
247 + cd test-brackets-lit &&
248 + prepare_test_files &&
249 + git add "\[abc\]" &&
250 + cat >expect <<-\EOF &&
251 + [abc]
252 + EOF
253 + git ls-files >actual &&
254 + test_cmp expect actual
255 + )
256 +'
257 +
258 +test_expect_success 'commit: wildcard *' '
259 + git init test-c-asterisk &&
260 + (
261 + cd test-c-asterisk &&
262 + prepare_test_files &&
263 + git add . &&
264 + git commit -m "c1" -- "*" &&
265 + cat >expect <<-EOF &&
266 + *
267 + **
268 + ?
269 + [abc]
270 + a
271 + f*
272 + f**
273 + f?z
274 + foo*bar
275 + hello?world
276 + hello_world
277 + EOF
278 + git ls-tree -r --name-only HEAD >actual &&
279 + test_cmp expect actual
280 + )
281 +'
282 +
283 +test_expect_success 'commit: literal *' '
284 + git init test-c-asterisk-lit &&
285 + (
286 + cd test-c-asterisk-lit &&
287 + prepare_test_files &&
288 + git add . &&
289 + git commit -m "c2" -- "\*" &&
290 + cat >expect <<-EOF &&
291 + *
292 + EOF
293 + git ls-tree -r --name-only HEAD >actual &&
294 + test_cmp expect actual
295 + )
296 +'
297 +
298 +test_expect_success 'commit: wildcard f*' '
299 + git init test-c-fwild &&
300 + (
301 + cd test-c-fwild &&
302 + prepare_test_files &&
303 + git add . &&
304 + git commit -m "c3" -- "f*" &&
305 + cat >expect <<-EOF &&
306 + f*
307 + f**
308 + f?z
309 + foo*bar
310 + EOF
311 + git ls-tree -r --name-only HEAD >actual &&
312 + test_cmp expect actual
313 + )
314 +'
315 +
316 +test_expect_success 'commit: literal f\*' '
317 + git init test-c-flit &&
318 + (
319 + cd test-c-flit &&
320 + prepare_test_files &&
321 + git add . &&
322 + git commit -m "c4" -- "f\*" &&
323 + cat >expect <<-EOF &&
324 + f*
325 + EOF
326 + git ls-tree -r --name-only HEAD >actual &&
327 + test_cmp expect actual
328 + )
329 +'
330 +
331 +test_expect_success 'commit: wildcard pathspec limits commit' '
332 + git init test-c-pathlimit &&
333 + (
334 + cd test-c-pathlimit &&
335 + prepare_test_files &&
336 + git add . &&
337 + git commit -m "c5" -- "f**" &&
338 + cat >expect <<-EOF &&
339 + f*
340 + f**
341 + f?z
342 + foo*bar
343 + EOF
344 + git ls-tree -r --name-only HEAD >actual &&
345 + test_cmp expect actual
346 + )
347 +'
348 +
349 +test_expect_success 'commit: literal f\*\*' '
350 + git init test-c-fdstar-lit &&
351 + (
352 + cd test-c-fdstar-lit &&
353 + prepare_test_files &&
354 + git add . &&
355 + git commit -m "c6" -- "f\*\*" &&
356 + cat >expect <<-EOF &&
357 + f**
358 + EOF
359 + git ls-tree -r --name-only HEAD >actual &&
360 + test_cmp expect actual
361 + )
362 +'
363 +
364 +test_expect_success 'commit: wildcard ?' '
365 + git init test-c-qwild &&
366 + (
367 + cd test-c-qwild &&
368 + prepare_test_files &&
369 + git add . &&
370 + git commit -m "c7" -- "?" &&
371 + cat >expect <<-EOF &&
372 + *
373 + ?
374 + a
375 + EOF
376 + git ls-tree -r --name-only HEAD | sort >actual &&
377 + sort expect >expect.sorted &&
378 + test_cmp expect.sorted actual
379 + )
380 +'
381 +
382 +test_expect_success 'commit: literal \?' '
383 + git init test-c-qlit &&
384 + (
385 + cd test-c-qlit &&
386 + prepare_test_files &&
387 + git add . &&
388 + git commit -m "c8" -- "\?" &&
389 + cat >expect <<-EOF &&
390 + ?
391 + EOF
392 + git ls-tree -r --name-only HEAD >actual &&
393 + test_cmp expect actual
394 + )
395 +'
396 +
397 +test_expect_success 'commit: wildcard hello?world' '
398 + git init test-c-hellowild &&
399 + (
400 + cd test-c-hellowild &&
401 + prepare_test_files &&
402 + git add . &&
403 + git commit -m "c9" -- "hello?world" &&
404 + cat >expect <<-EOF &&
405 + hello?world
406 + hello_world
407 + EOF
408 + git ls-tree -r --name-only HEAD | sort >actual &&
409 + sort expect >expect.sorted &&
410 + test_cmp expect.sorted actual
411 + )
412 +'
413 +
414 +test_expect_success 'commit: literal hello\?world' '
415 + git init test-c-hellolit &&
416 + (
417 + cd test-c-hellolit &&
418 + prepare_test_files &&
419 + git add . &&
420 + git commit -m "c10" -- "hello\?world" &&
421 + cat >expect <<-EOF &&
422 + hello?world
423 + EOF
424 + git ls-tree -r --name-only HEAD >actual &&
425 + test_cmp expect actual
426 + )
427 +'
428 +
429 +test_done