3 test_description
='Test grep recurse-submodules feature
5 This test verifies the recurse-submodules feature correctly greps across
9 TEST_CREATE_REPO_NO_TEMPLATE
=1
12 GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
=1
13 export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
15 test_expect_success
'setup directory structure and submodule' '
16 echo "(1|2)d(3|4)" >a &&
20 git commit -m "add a and b" &&
23 echo "(1|2)d(3|4)" >submodule/a &&
24 git -C submodule add a &&
25 git -C submodule commit -m "add a" &&
26 git submodule add ./submodule &&
27 git commit -m "added submodule" &&
31 test_expect_success
'grep correctly finds patterns in a submodule' '
32 cat >expect <<-\EOF &&
35 submodule/a:(1|2)d(3|4)
38 git grep -e "(3|4)" --recurse-submodules >actual &&
39 test_cmp expect actual
42 test_expect_success
'grep finds patterns in a submodule via config' '
43 test_config submodule.recurse true &&
44 # expect from previous test
45 git grep -e "(3|4)" >actual &&
46 test_cmp expect actual
49 test_expect_success
'grep --no-recurse-submodules overrides config' '
50 test_config submodule.recurse true &&
51 cat >expect <<-\EOF &&
56 git grep -e "(3|4)" --no-recurse-submodules >actual &&
57 test_cmp expect actual
60 test_expect_success
'grep and basic pathspecs' '
61 cat >expect <<-\EOF &&
62 submodule/a:(1|2)d(3|4)
65 git grep -e. --recurse-submodules -- submodule >actual &&
66 test_cmp expect actual
69 test_expect_success
'grep and nested submodules' '
70 git init submodule/sub &&
71 echo "(1|2)d(3|4)" >submodule/sub/a &&
72 git -C submodule/sub add a &&
73 git -C submodule/sub commit -m "add a" &&
75 git -C submodule submodule add ./sub &&
76 git -C submodule add sub &&
77 git -C submodule commit -m "added sub" &&
80 git commit -m "updated submodule" &&
83 cat >expect <<-\EOF &&
86 submodule/a:(1|2)d(3|4)
87 submodule/sub/a:(1|2)d(3|4)
90 git grep -e "(3|4)" --recurse-submodules >actual &&
91 test_cmp expect actual
94 test_expect_success
'grep and multiple patterns' '
95 cat >expect <<-\EOF &&
97 submodule/a:(1|2)d(3|4)
98 submodule/sub/a:(1|2)d(3|4)
101 git grep -e "(3|4)" --and -e "(1|2)" --recurse-submodules >actual &&
102 test_cmp expect actual
105 test_expect_success
'grep and multiple patterns' '
106 cat >expect <<-\EOF &&
110 git grep -e "(3|4)" --and --not -e "(1|2)" --recurse-submodules >actual &&
111 test_cmp expect actual
114 test_expect_success
'basic grep tree' '
115 cat >expect <<-\EOF &&
118 HEAD:submodule/a:(1|2)d(3|4)
119 HEAD:submodule/sub/a:(1|2)d(3|4)
122 git grep -e "(3|4)" --recurse-submodules HEAD >actual &&
123 test_cmp expect actual
126 test_expect_success
'grep tree HEAD^' '
127 cat >expect <<-\EOF &&
130 HEAD^:submodule/a:(1|2)d(3|4)
133 git grep -e "(3|4)" --recurse-submodules HEAD^ >actual &&
134 test_cmp expect actual
137 test_expect_success
'grep tree HEAD^^' '
138 cat >expect <<-\EOF &&
143 git grep -e "(3|4)" --recurse-submodules HEAD^^ >actual &&
144 test_cmp expect actual
147 test_expect_success
'grep tree and pathspecs' '
148 cat >expect <<-\EOF &&
149 HEAD:submodule/a:(1|2)d(3|4)
150 HEAD:submodule/sub/a:(1|2)d(3|4)
153 git grep -e "(3|4)" --recurse-submodules HEAD -- submodule >actual &&
154 test_cmp expect actual
157 test_expect_success
'grep tree and pathspecs' '
158 cat >expect <<-\EOF &&
159 HEAD:submodule/a:(1|2)d(3|4)
160 HEAD:submodule/sub/a:(1|2)d(3|4)
163 git grep -e "(3|4)" --recurse-submodules HEAD -- "submodule*a" >actual &&
164 test_cmp expect actual
167 test_expect_success
'grep tree and more pathspecs' '
168 cat >expect <<-\EOF &&
169 HEAD:submodule/a:(1|2)d(3|4)
172 git grep -e "(3|4)" --recurse-submodules HEAD -- "submodul?/a" >actual &&
173 test_cmp expect actual
176 test_expect_success
'grep tree and more pathspecs' '
177 cat >expect <<-\EOF &&
178 HEAD:submodule/sub/a:(1|2)d(3|4)
181 git grep -e "(3|4)" --recurse-submodules HEAD -- "submodul*/sub/a" >actual &&
182 test_cmp expect actual
185 test_expect_success
!MINGW
'grep recurse submodule colon in name' '
187 test_when_finished "rm -rf parent" &&
188 echo "(1|2)d(3|4)" >"parent/fi:le" &&
189 git -C parent add "fi:le" &&
190 git -C parent commit -m "add fi:le" &&
194 test_when_finished "rm -rf su:b" &&
195 echo "(1|2)d(3|4)" >"su:b/fi:le" &&
196 git -C "su:b" add "fi:le" &&
197 git -C "su:b" commit -m "add fi:le" &&
200 git -C parent submodule add "../su:b" "su:b" &&
201 git -C parent commit -m "add submodule" &&
204 cat >expect <<-\EOF &&
206 su:b/fi:le:(1|2)d(3|4)
208 git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual &&
209 test_cmp expect actual &&
211 cat >expect <<-\EOF &&
212 HEAD:fi:le:(1|2)d(3|4)
213 HEAD:su:b/fi:le:(1|2)d(3|4)
215 git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules HEAD >actual &&
216 test_cmp expect actual
219 test_expect_success
'grep history with moved submoules' '
221 test_when_finished "rm -rf parent" &&
222 echo "(1|2)d(3|4)" >parent/file &&
223 git -C parent add file &&
224 git -C parent commit -m "add file" &&
228 test_when_finished "rm -rf sub" &&
229 echo "(1|2)d(3|4)" >sub/file &&
230 git -C sub add file &&
231 git -C sub commit -m "add file" &&
234 git -C parent submodule add ../sub dir/sub &&
235 git -C parent commit -m "add submodule" &&
238 cat >expect <<-\EOF &&
239 dir/sub/file:(1|2)d(3|4)
242 git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual &&
243 test_cmp expect actual &&
245 git -C parent mv dir/sub sub-moved &&
246 git -C parent commit -m "moved submodule" &&
249 cat >expect <<-\EOF &&
251 sub-moved/file:(1|2)d(3|4)
253 git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual &&
254 test_cmp expect actual &&
256 cat >expect <<-\EOF &&
257 HEAD^:dir/sub/file:(1|2)d(3|4)
258 HEAD^:file:(1|2)d(3|4)
260 git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules HEAD^ >actual &&
261 test_cmp expect actual
264 test_expect_success
'grep using relative path' '
265 test_when_finished "rm -rf parent sub" &&
267 echo "(1|2)d(3|4)" >sub/file &&
268 git -C sub add file &&
269 git -C sub commit -m "add file" &&
273 echo "(1|2)d(3|4)" >parent/file &&
274 git -C parent add file &&
276 echo "(1|2)d(3|4)" >parent/src/file2 &&
277 git -C parent add src/file2 &&
278 git -C parent submodule add ../sub &&
279 git -C parent commit -m "add files and submodule" &&
283 cat >expect <<-\EOF &&
285 src/file2:(1|2)d(3|4)
288 git -C parent grep --recurse-submodules -e "(1|2)d(3|4)" >actual &&
289 test_cmp expect actual &&
291 # Relative path to top
292 cat >expect <<-\EOF &&
295 ../sub/file:(1|2)d(3|4)
297 git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" -- .. >actual &&
298 test_cmp expect actual &&
300 # Relative path to submodule
301 cat >expect <<-\EOF &&
302 ../sub/file:(1|2)d(3|4)
304 git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" -- ../sub >actual &&
305 test_cmp expect actual
308 test_expect_success
'grep from a subdir' '
309 test_when_finished "rm -rf parent sub" &&
311 echo "(1|2)d(3|4)" >sub/file &&
312 git -C sub add file &&
313 git -C sub commit -m "add file" &&
318 echo "(1|2)d(3|4)" >parent/src/file &&
319 git -C parent add src/file &&
320 git -C parent submodule add ../sub src/sub &&
321 git -C parent submodule add ../sub sub &&
322 git -C parent commit -m "add files and submodules" &&
325 # Verify grep from root works
326 cat >expect <<-\EOF &&
328 src/sub/file:(1|2)d(3|4)
331 git -C parent grep --recurse-submodules -e "(1|2)d(3|4)" >actual &&
332 test_cmp expect actual &&
334 # Verify grep from a subdir works
335 cat >expect <<-\EOF &&
339 git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" >actual &&
340 test_cmp expect actual
343 test_incompatible_with_recurse_submodules
()
345 test_expect_success
"--recurse-submodules and $1 are incompatible" "
346 test_must_fail git grep -e. --recurse-submodules $1 2>actual &&
347 test_i18ngrep 'not supported with --recurse-submodules' actual
351 test_incompatible_with_recurse_submodules
--untracked
353 test_expect_success
'grep --recurse-submodules --no-index ignores --recurse-submodules' '
354 git grep --recurse-submodules --no-index -e "^(.|.)[\d]" >actual &&
355 cat >expect <<-\EOF &&
357 submodule/a:(1|2)d(3|4)
358 submodule/sub/a:(1|2)d(3|4)
360 test_cmp expect actual
363 test_expect_success
'grep --recurse-submodules should pass the pattern type along' '
365 test_must_fail git grep -F --recurse-submodules -e "(.|.)[\d]" &&
366 test_must_fail git -c grep.patternType=fixed grep --recurse-submodules -e "(.|.)[\d]" &&
369 git grep -G --recurse-submodules -e "(.|.)[\d]" >actual &&
370 cat >expect <<-\EOF &&
372 submodule/a:(1|2)d(3|4)
373 submodule/sub/a:(1|2)d(3|4)
375 test_cmp expect actual &&
376 git -c grep.patternType=basic grep --recurse-submodules -e "(.|.)[\d]" >actual &&
377 test_cmp expect actual &&
380 git grep -E --recurse-submodules -e "(.|.)[\d]" >actual &&
381 cat >expect <<-\EOF &&
382 .gitmodules:[submodule "submodule"]
383 .gitmodules: path = submodule
384 .gitmodules: url = ./submodule
386 submodule/.gitmodules:[submodule "sub"]
387 submodule/a:(1|2)d(3|4)
388 submodule/sub/a:(1|2)d(3|4)
390 test_cmp expect actual &&
391 git -c grep.patternType=extended grep --recurse-submodules -e "(.|.)[\d]" >actual &&
392 test_cmp expect actual &&
393 git -c grep.extendedRegexp=true grep --recurse-submodules -e "(.|.)[\d]" >actual &&
394 test_cmp expect actual &&
397 if test_have_prereq PCRE
399 git grep -P --recurse-submodules -e "(.|.)[\d]" >actual &&
400 cat >expect <<-\EOF &&
403 submodule/a:(1|2)d(3|4)
404 submodule/sub/a:(1|2)d(3|4)
406 test_cmp expect actual &&
407 git -c grep.patternType=perl grep --recurse-submodules -e "(.|.)[\d]" >actual &&
408 test_cmp expect actual
412 test_expect_success
'grep --recurse-submodules with submodules without .gitmodules in the working tree' '
413 test_when_finished "git -C submodule checkout .gitmodules" &&
414 rm submodule/.gitmodules &&
415 git grep --recurse-submodules -e "(.|.)[\d]" >actual &&
416 cat >expect <<-\EOF &&
418 submodule/a:(1|2)d(3|4)
419 submodule/sub/a:(1|2)d(3|4)
421 test_cmp expect actual
427 git submodule foreach
--recursive 'git reset --hard' &&
428 git submodule foreach
--recursive 'git clean -fd'
431 test_expect_success
'grep --recurse-submodules without --cached considers worktree modifications' '
433 echo "A modified line in submodule" >>submodule/a &&
434 echo "submodule/a:A modified line in submodule" >expect &&
435 git grep --recurse-submodules "A modified line in submodule" >actual &&
436 test_cmp expect actual
439 test_expect_success
'grep --recurse-submodules with --cached ignores worktree modifications' '
441 echo "A modified line in submodule" >>submodule/a &&
442 test_must_fail git grep --recurse-submodules --cached "A modified line in submodule" >actual 2>&1 &&
443 test_must_be_empty actual
446 test_expect_failure
'grep --textconv: superproject .gitattributes does not affect submodules' '
448 test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
449 echo "a diff=d2x" >.gitattributes &&
451 cat >expect <<-\EOF &&
454 git grep --textconv --recurse-submodules x >actual &&
455 test_cmp expect actual
458 test_expect_failure
'grep --textconv: superproject .gitattributes (from index) does not affect submodules' '
460 test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
461 echo "a diff=d2x" >.gitattributes &&
462 git add .gitattributes &&
465 cat >expect <<-\EOF &&
468 git grep --textconv --recurse-submodules x >actual &&
469 test_cmp expect actual
472 test_expect_failure
'grep --textconv: superproject .git/info/attributes does not affect submodules' '
474 test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
475 super_info="$(git rev-parse --git-path info)" &&
476 super_attr="$super_info/attributes" &&
477 test_when_finished "rm -f \"$super_attr\"" &&
478 mkdir "$super_info" &&
479 echo "a diff=d2x" >"$super_attr" &&
481 cat >expect <<-\EOF &&
484 git grep --textconv --recurse-submodules x >actual &&
485 test_cmp expect actual
488 # Note: what currently prevents this test from passing is not that the
489 # .gitattributes file from "./submodule" is being ignored, but that it is being
490 # propagated to the nested "./submodule/sub" files.
492 test_expect_failure
'grep --textconv correctly reads submodule .gitattributes' '
494 test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
495 echo "a diff=d2x" >submodule/.gitattributes &&
497 cat >expect <<-\EOF &&
498 submodule/a:(1|2)x(3|4)
500 git grep --textconv --recurse-submodules x >actual &&
501 test_cmp expect actual
504 test_expect_failure
'grep --textconv correctly reads submodule .gitattributes (from index)' '
506 test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
507 echo "a diff=d2x" >submodule/.gitattributes &&
508 git -C submodule add .gitattributes &&
509 rm submodule/.gitattributes &&
511 cat >expect <<-\EOF &&
512 submodule/a:(1|2)x(3|4)
514 git grep --textconv --recurse-submodules x >actual &&
515 test_cmp expect actual
518 test_expect_failure
'grep --textconv correctly reads submodule .git/info/attributes' '
520 test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
522 submodule_info="$(git -C submodule rev-parse --path-format=absolute --git-path info)" &&
523 submodule_attr="$submodule_info/attributes" &&
524 test_when_finished "rm -f \"$submodule_attr\"" &&
525 echo "a diff=d2x" >"$submodule_attr" &&
527 cat >expect <<-\EOF &&
528 submodule/a:(1|2)x(3|4)
530 git grep --textconv --recurse-submodules x >actual &&
531 test_cmp expect actual
534 test_expect_failure
'grep saves textconv cache in the appropriate repository' '
536 test_config_global diff.d2x_cached.textconv "sed -e \"s/d/x/\"" &&
537 test_config_global diff.d2x_cached.cachetextconv true &&
538 echo "a diff=d2x_cached" >submodule/.gitattributes &&
540 # We only read/write to the textconv cache when grepping from an OID,
541 # as the working tree file might have modifications.
542 git grep --textconv --cached --recurse-submodules x &&
544 super_textconv_cache="$(git rev-parse --git-path refs/notes/textconv/d2x_cached)" &&
545 sub_textconv_cache="$(git -C submodule rev-parse \
546 --path-format=absolute --git-path refs/notes/textconv/d2x_cached)" &&
547 test_path_is_missing "$super_textconv_cache" &&
548 test_path_is_file "$sub_textconv_cache"
551 test_expect_success
'grep partially-cloned submodule' '
552 # Set up clean superproject and submodule for partial cloning.
554 git init super/sub &&
557 test_commit --no-tag "Add file in superproject" \
558 super-file "Some content for super-file" &&
559 test_commit -C sub --no-tag "Add file in submodule" \
560 sub-file "Some content for sub-file" &&
561 git submodule add ./sub &&
562 git commit -m "Add other as submodule sub" &&
564 test_commit -C sub --no-tag --append "Update file in submodule" \
565 sub-file "Some more content for sub-file" &&
567 git commit -m "Update submodule" &&
569 git config --local uploadpack.allowfilter 1 &&
570 git config --local uploadpack.allowanysha1inwant 1 &&
571 git -C sub config --local uploadpack.allowfilter 1 &&
572 git -C sub config --local uploadpack.allowanysha1inwant 1
574 # Clone the superproject & submodule, then make sure we can lazy-fetch submodule objects.
575 git clone --filter=blob:none --also-filter-submodules \
576 --recurse-submodules "file://$(pwd)/super" partial &&
579 cat >expect <<-\EOF &&
580 HEAD^:sub/sub-file:Some content for sub-file
581 HEAD^:super-file:Some content for super-file
584 GIT_TRACE2_EVENT="$(pwd)/trace2.log" git grep -e content \
585 --recurse-submodules HEAD^ >actual &&
586 test_cmp expect actual &&
587 # Verify that we actually fetched data from the promisor remote:
588 grep \"category\":\"promisor\",\"key\":\"fetch_count\",\"value\":\"1\" trace2.log