Raw
1 #!/bin/sh
2
3 test_description='submodulePathConfig extension works as expected'
4
5 . ./test-lib.sh
6 . "$TEST_DIRECTORY"/lib-verify-submodule-gitdir-path.sh
7
8 test_expect_success 'setup: allow file protocol' '
9 git config --global protocol.file.allow always
10 '
11
12 test_expect_success 'create repo with mixed extension submodules' '
13 git init -b main legacy-sub &&
14 test_commit -C legacy-sub legacy-initial &&
15 legacy_rev=$(git -C legacy-sub rev-parse HEAD) &&
16
17 git init -b main new-sub &&
18 test_commit -C new-sub new-initial &&
19 new_rev=$(git -C new-sub rev-parse HEAD) &&
20
21 git init -b main main &&
22 (
23 cd main &&
24 git submodule add ../legacy-sub legacy &&
25 test_commit legacy-sub &&
26
27 # trigger the "die_path_inside_submodule" check
28 test_must_fail git submodule add ../new-sub "legacy/nested" &&
29
30 git config core.repositoryformatversion 1 &&
31 git config extensions.submodulePathConfig true &&
32
33 git submodule add ../new-sub "New Sub" &&
34 test_commit new &&
35
36 # retrigger the "die_path_inside_submodule" check with encoding
37 test_must_fail git submodule add ../new-sub "New Sub/nested2"
38 )
39 '
40
41 test_expect_success 'verify new submodule gitdir config' '
42 git -C main config submodule."New Sub".gitdir >actual &&
43 echo ".git/modules/New Sub" >expect &&
44 test_cmp expect actual &&
45 verify_submodule_gitdir_path main "New Sub" "modules/New Sub"
46 '
47
48 test_expect_success 'manual add and verify legacy submodule gitdir config' '
49 # the legacy module should not contain a gitdir config, because it
50 # was added before the extension was enabled. Add and test it.
51 test_must_fail git -C main config submodule.legacy.gitdir &&
52 git -C main config submodule.legacy.gitdir .git/modules/legacy &&
53 git -C main config submodule.legacy.gitdir >actual &&
54 echo ".git/modules/legacy" >expect &&
55 test_cmp expect actual &&
56 verify_submodule_gitdir_path main "legacy" "modules/legacy"
57 '
58
59 test_expect_success 'gitdir config path is relative for both absolute and relative urls' '
60 test_when_finished "rm -rf relative-cfg-path-test" &&
61 git init -b main relative-cfg-path-test &&
62 (
63 cd relative-cfg-path-test &&
64 git config core.repositoryformatversion 1 &&
65 git config extensions.submodulePathConfig true &&
66
67 # Test with absolute URL
68 git submodule add "$TRASH_DIRECTORY/new-sub" sub-abs &&
69 git config submodule.sub-abs.gitdir >actual &&
70 echo ".git/modules/sub-abs" >expect &&
71 test_cmp expect actual &&
72
73 # Test with relative URL
74 git submodule add ../new-sub sub-rel &&
75 git config submodule.sub-rel.gitdir >actual &&
76 echo ".git/modules/sub-rel" >expect &&
77 test_cmp expect actual
78 )
79 '
80
81 test_expect_success 'clone from repo with both legacy and new-style submodules' '
82 git clone --recurse-submodules main cloned-non-extension &&
83 (
84 cd cloned-non-extension &&
85
86 test_path_is_dir .git/modules/legacy &&
87 test_path_is_dir .git/modules/"New Sub" &&
88
89 test_must_fail git config submodule.legacy.gitdir &&
90 test_must_fail git config submodule."New Sub".gitdir &&
91
92 git submodule status >list &&
93 test_grep "$legacy_rev legacy" list &&
94 test_grep "$new_rev New Sub" list
95 ) &&
96
97 git clone -c extensions.submodulePathConfig=true --recurse-submodules main cloned-extension &&
98 (
99 cd cloned-extension &&
100
101 test_path_is_dir .git/modules/legacy &&
102 test_path_is_dir ".git/modules/New Sub" &&
103
104 git config submodule.legacy.gitdir &&
105 git config submodule."New Sub".gitdir &&
106
107 git submodule status >list &&
108 test_grep "$legacy_rev legacy" list &&
109 test_grep "$new_rev New Sub" list
110 )
111 '
112
113 test_expect_success 'commit and push changes to encoded submodules' '
114 git -C legacy-sub config receive.denyCurrentBranch updateInstead &&
115 git -C new-sub config receive.denyCurrentBranch updateInstead &&
116 git -C main config receive.denyCurrentBranch updateInstead &&
117 (
118 cd cloned-extension &&
119
120 git -C legacy switch --track -C main origin/main &&
121 test_commit -C legacy second-commit &&
122 git -C legacy push &&
123
124 git -C "New Sub" switch --track -C main origin/main &&
125 test_commit -C "New Sub" second-commit &&
126 git -C "New Sub" push &&
127
128 # Stage and commit submodule changes in superproject
129 git switch --track -C main origin/main &&
130 git add legacy "New Sub" &&
131 git commit -m "update submodules" &&
132
133 # push superproject commit to main repo
134 git push
135 ) &&
136
137 # update expected legacy & new submodule checksums
138 legacy_rev=$(git -C legacy-sub rev-parse HEAD) &&
139 new_rev=$(git -C new-sub rev-parse HEAD)
140 '
141
142 test_expect_success 'fetch mixed submodule changes and verify updates' '
143 (
144 cd main &&
145
146 # only update submodules because superproject was
147 # pushed into at the end of last test
148 git submodule update --init --recursive &&
149
150 test_path_is_dir .git/modules/legacy &&
151 test_path_is_dir ".git/modules/New Sub" &&
152
153 # Verify both submodules are at the expected commits
154 git submodule status >list &&
155 test_grep "$legacy_rev legacy" list &&
156 test_grep "$new_rev New Sub" list
157 )
158 '
159
160 test_expect_success '`git init` respects init.defaultSubmodulePathConfig' '
161 test_config_global init.defaultSubmodulePathConfig true &&
162 git init repo-init &&
163 git -C repo-init config extensions.submodulePathConfig >actual &&
164 echo true >expect &&
165 test_cmp expect actual &&
166 # create a submodule and check gitdir
167 (
168 cd repo-init &&
169 git init -b main sub &&
170 test_commit -C sub sub-initial &&
171 git submodule add ./sub sub &&
172 git config submodule.sub.gitdir >actual &&
173 echo ".git/modules/sub" >expect &&
174 test_cmp expect actual
175 )
176 '
177
178 test_expect_success '`git init` does not set extension by default' '
179 git init upstream &&
180 test_commit -C upstream initial &&
181 test_must_fail git -C upstream config extensions.submodulePathConfig &&
182 # create a pair of submodules and check gitdir is not created
183 git init -b main sub &&
184 test_commit -C sub sub-initial &&
185 (
186 cd upstream &&
187 git submodule add ../sub sub1 &&
188 test_path_is_dir .git/modules/sub1 &&
189 test_must_fail git config submodule.sub1.gitdir &&
190 git submodule add ../sub sub2 &&
191 test_path_is_dir .git/modules/sub2 &&
192 test_must_fail git config submodule.sub2.gitdir &&
193 git commit -m "Add submodules"
194 )
195 '
196
197 test_expect_success '`git clone` does not set extension by default' '
198 test_when_finished "rm -rf repo-clone-no-ext" &&
199 git clone upstream repo-clone-no-ext &&
200 (
201 cd repo-clone-no-ext &&
202
203 test_must_fail git config extensions.submodulePathConfig &&
204 test_path_is_missing .git/modules/sub1 &&
205 test_path_is_missing .git/modules/sub2 &&
206
207 # create a submodule and check gitdir is not created
208 git submodule add ../sub sub3 &&
209 test_must_fail git config submodule.sub3.gitdir
210 )
211 '
212
213 test_expect_success '`git clone --recurse-submodules` does not set extension by default' '
214 test_when_finished "rm -rf repo-clone-no-ext" &&
215 git clone --recurse-submodules upstream repo-clone-no-ext &&
216 (
217 cd repo-clone-no-ext &&
218
219 # verify that that submodules do not have gitdir set
220 test_must_fail git config extensions.submodulePathConfig &&
221 test_path_is_dir .git/modules/sub1 &&
222 test_must_fail git config submodule.sub1.gitdir &&
223 test_path_is_dir .git/modules/sub2 &&
224 test_must_fail git config submodule.sub2.gitdir &&
225
226 # create another submodule and check that gitdir is not created
227 git submodule add ../sub sub3 &&
228 test_path_is_dir .git/modules/sub3 &&
229 test_must_fail git config submodule.sub3.gitdir
230 )
231
232 '
233
234 test_expect_success '`git clone` respects init.defaultSubmodulePathConfig' '
235 test_when_finished "rm -rf repo-clone" &&
236 test_config_global init.defaultSubmodulePathConfig true &&
237 git clone upstream repo-clone &&
238 (
239 cd repo-clone &&
240
241 # verify new repo extension is inherited from global config
242 git config extensions.submodulePathConfig >actual &&
243 echo true >expect &&
244 test_cmp expect actual &&
245
246 # new submodule has a gitdir config
247 git submodule add ../sub sub &&
248 test_path_is_dir .git/modules/sub &&
249 git config submodule.sub.gitdir >actual &&
250 echo ".git/modules/sub" >expect &&
251 test_cmp expect actual
252 )
253 '
254
255 test_expect_success '`git clone --recurse-submodules` respects init.defaultSubmodulePathConfig' '
256 test_when_finished "rm -rf repo-clone-recursive" &&
257 test_config_global init.defaultSubmodulePathConfig true &&
258 git clone --recurse-submodules upstream repo-clone-recursive &&
259 (
260 cd repo-clone-recursive &&
261
262 # verify new repo extension is inherited from global config
263 git config extensions.submodulePathConfig >actual &&
264 echo true >expect &&
265 test_cmp expect actual &&
266
267 # previous submodules should exist
268 git config submodule.sub1.gitdir &&
269 git config submodule.sub2.gitdir &&
270 test_path_is_dir .git/modules/sub1 &&
271 test_path_is_dir .git/modules/sub2 &&
272
273 # create another submodule and check that gitdir is created
274 git submodule add ../sub new-sub &&
275 test_path_is_dir .git/modules/new-sub &&
276 git config submodule.new-sub.gitdir >actual &&
277 echo ".git/modules/new-sub" >expect &&
278 test_cmp expect actual
279 )
280 '
281
282 test_expect_success 'submodule--helper migrates legacy modules' '
283 (
284 cd upstream &&
285
286 # previous submodules exist and were not migrated yet
287 test_must_fail git config submodule.sub1.gitdir &&
288 test_must_fail git config submodule.sub2.gitdir &&
289 test_path_is_dir .git/modules/sub1 &&
290 test_path_is_dir .git/modules/sub2 &&
291
292 # run migration
293 git submodule--helper migrate-gitdir-configs &&
294
295 # test that migration worked
296 git config submodule.sub1.gitdir >actual &&
297 echo ".git/modules/sub1" >expect &&
298 test_cmp expect actual &&
299 git config submodule.sub2.gitdir >actual &&
300 echo ".git/modules/sub2" >expect &&
301 test_cmp expect actual &&
302
303 # repository extension is enabled after migration
304 git config extensions.submodulePathConfig >actual &&
305 echo "true" >expect &&
306 test_cmp expect actual
307 )
308 '
309
310 test_expect_success '`git clone --recurse-submodules` works after migration' '
311 test_when_finished "rm -rf repo-clone-recursive" &&
312
313 # test with extension disabled after the upstream repo was migrated
314 git clone --recurse-submodules upstream repo-clone-recursive &&
315 (
316 cd repo-clone-recursive &&
317
318 # init.defaultSubmodulePathConfig was disabled before clone, so
319 # the repo extension config should also be off, the migration ignored
320 test_must_fail git config extensions.submodulePathConfig &&
321
322 # modules should look like there was no migration done
323 test_must_fail git config submodule.sub1.gitdir &&
324 test_must_fail git config submodule.sub2.gitdir &&
325 test_path_is_dir .git/modules/sub1 &&
326 test_path_is_dir .git/modules/sub2
327 ) &&
328 rm -rf repo-clone-recursive &&
329
330 # enable the extension, then retry the clone
331 test_config_global init.defaultSubmodulePathConfig true &&
332 git clone --recurse-submodules upstream repo-clone-recursive &&
333 (
334 cd repo-clone-recursive &&
335
336 # repository extension is enabled
337 git config extensions.submodulePathConfig >actual &&
338 echo "true" >expect &&
339 test_cmp expect actual &&
340
341 # gitdir configs exist for submodules
342 git config submodule.sub1.gitdir &&
343 git config submodule.sub2.gitdir &&
344 test_path_is_dir .git/modules/sub1 &&
345 test_path_is_dir .git/modules/sub2
346 )
347 '
348
349 test_expect_success 'setup submodules with nested git dirs' '
350 git init nested &&
351 test_commit -C nested nested &&
352 (
353 cd nested &&
354 cat >.gitmodules <<-EOF &&
355 [submodule "hippo"]
356 url = .
357 path = thing1
358 [submodule "hippo/hooks"]
359 url = .
360 path = thing2
361 EOF
362 git clone . thing1 &&
363 git clone . thing2 &&
364 git add .gitmodules thing1 thing2 &&
365 test_tick &&
366 git commit -m nested
367 )
368 '
369
370 test_expect_success 'git dirs of encoded sibling submodules must not be nested' '
371 git clone -c extensions.submodulePathConfig=true --recurse-submodules nested clone_nested &&
372
373 verify_submodule_gitdir_path clone_nested hippo modules/hippo &&
374 git -C clone_nested config submodule.hippo.gitdir >actual &&
375 test_grep "\.git/modules/hippo$" actual &&
376
377 verify_submodule_gitdir_path clone_nested hippo/hooks modules/hippo%2fhooks &&
378 git -C clone_nested config submodule.hippo/hooks.gitdir >actual &&
379 test_grep "\.git/modules/hippo%2fhooks$" actual
380 '
381
382 test_expect_success 'submodule git dir nesting detection must work with parallel cloning' '
383 git clone -c extensions.submodulePathConfig=true --recurse-submodules --jobs=2 nested clone_parallel &&
384
385 verify_submodule_gitdir_path clone_parallel hippo modules/hippo &&
386 git -C clone_nested config submodule.hippo.gitdir >actual &&
387 test_grep "\.git/modules/hippo$" actual &&
388
389 verify_submodule_gitdir_path clone_parallel hippo/hooks modules/hippo%2fhooks &&
390 git -C clone_nested config submodule.hippo/hooks.gitdir >actual &&
391 test_grep "\.git/modules/hippo%2fhooks$" actual
392 '
393
394 test_expect_success 'disabling extensions.submodulePathConfig prevents nested submodules' '
395 (
396 cd clone_nested &&
397 # disable extension and verify failure
398 git config --replace-all extensions.submodulePathConfig false &&
399 test_must_fail git submodule add ./thing2 hippo/foobar &&
400 # re-enable extension and verify it works
401 git config --replace-all extensions.submodulePathConfig true &&
402 git submodule add ./thing2 hippo/foobar
403 )
404 '
405
406 test_expect_success CASE_INSENSITIVE_FS 'verify case-folding conflicts are correctly encoded' '
407 git clone -c extensions.submodulePathConfig=true main cloned-folding &&
408 (
409 cd cloned-folding &&
410
411 # conflict: the "folding" gitdir will already be taken
412 git submodule add ../new-sub "folding" &&
413 test_commit lowercase &&
414 git submodule add ../new-sub "FoldinG" &&
415 test_commit uppercase &&
416
417 # conflict: the "foo" gitdir will already be taken
418 git submodule add ../new-sub "FOO" &&
419 test_commit uppercase-foo &&
420 git submodule add ../new-sub "foo" &&
421 test_commit lowercase-foo &&
422
423 # create a multi conflict between foobar, fooBar and foo%42ar
424 # the "foo" gitdir will already be taken
425 git submodule add ../new-sub "foobar" &&
426 test_commit lowercase-foobar &&
427 git submodule add ../new-sub "foo%42ar" &&
428 test_commit encoded-foo%42ar &&
429 git submodule add ../new-sub "fooBar" &&
430 test_commit mixed-fooBar
431 ) &&
432 verify_submodule_gitdir_path cloned-folding "folding" "modules/folding" &&
433 verify_submodule_gitdir_path cloned-folding "FoldinG" "modules/%46oldin%47" &&
434 verify_submodule_gitdir_path cloned-folding "FOO" "modules/FOO" &&
435 verify_submodule_gitdir_path cloned-folding "foo" "modules/foo0" &&
436 verify_submodule_gitdir_path cloned-folding "foobar" "modules/foobar" &&
437 verify_submodule_gitdir_path cloned-folding "foo%42ar" "modules/foo%42ar" &&
438 verify_submodule_gitdir_path cloned-folding "fooBar" "modules/fooBar0"
439 '
440
441 test_expect_success CASE_INSENSITIVE_FS 'verify hashing conflict resolution as a last resort' '
442 git clone -c extensions.submodulePathConfig=true main cloned-hash &&
443 (
444 cd cloned-hash &&
445
446 # conflict: add all submodule conflicting variants until we reach the
447 # final hashing conflict resolution for submodule "foo"
448 git submodule add ../new-sub "foo" &&
449 git submodule add ../new-sub "foo0" &&
450 git submodule add ../new-sub "foo1" &&
451 git submodule add ../new-sub "foo2" &&
452 git submodule add ../new-sub "foo3" &&
453 git submodule add ../new-sub "foo4" &&
454 git submodule add ../new-sub "foo5" &&
455 git submodule add ../new-sub "foo6" &&
456 git submodule add ../new-sub "foo7" &&
457 git submodule add ../new-sub "foo8" &&
458 git submodule add ../new-sub "foo9" &&
459 git submodule add ../new-sub "%46oo" &&
460 git submodule add ../new-sub "%46oo0" &&
461 git submodule add ../new-sub "%46oo1" &&
462 git submodule add ../new-sub "%46oo2" &&
463 git submodule add ../new-sub "%46oo3" &&
464 git submodule add ../new-sub "%46oo4" &&
465 git submodule add ../new-sub "%46oo5" &&
466 git submodule add ../new-sub "%46oo6" &&
467 git submodule add ../new-sub "%46oo7" &&
468 git submodule add ../new-sub "%46oo8" &&
469 git submodule add ../new-sub "%46oo9" &&
470 test_commit add-foo-variants &&
471 git submodule add ../new-sub "Foo" &&
472 test_commit add-uppercase-foo
473 ) &&
474 verify_submodule_gitdir_path cloned-hash "foo" "modules/foo" &&
475 verify_submodule_gitdir_path cloned-hash "foo0" "modules/foo0" &&
476 verify_submodule_gitdir_path cloned-hash "foo1" "modules/foo1" &&
477 verify_submodule_gitdir_path cloned-hash "foo2" "modules/foo2" &&
478 verify_submodule_gitdir_path cloned-hash "foo3" "modules/foo3" &&
479 verify_submodule_gitdir_path cloned-hash "foo4" "modules/foo4" &&
480 verify_submodule_gitdir_path cloned-hash "foo5" "modules/foo5" &&
481 verify_submodule_gitdir_path cloned-hash "foo6" "modules/foo6" &&
482 verify_submodule_gitdir_path cloned-hash "foo7" "modules/foo7" &&
483 verify_submodule_gitdir_path cloned-hash "foo8" "modules/foo8" &&
484 verify_submodule_gitdir_path cloned-hash "foo9" "modules/foo9" &&
485 verify_submodule_gitdir_path cloned-hash "%46oo" "modules/%46oo" &&
486 verify_submodule_gitdir_path cloned-hash "%46oo0" "modules/%46oo0" &&
487 verify_submodule_gitdir_path cloned-hash "%46oo1" "modules/%46oo1" &&
488 verify_submodule_gitdir_path cloned-hash "%46oo2" "modules/%46oo2" &&
489 verify_submodule_gitdir_path cloned-hash "%46oo3" "modules/%46oo3" &&
490 verify_submodule_gitdir_path cloned-hash "%46oo4" "modules/%46oo4" &&
491 verify_submodule_gitdir_path cloned-hash "%46oo5" "modules/%46oo5" &&
492 verify_submodule_gitdir_path cloned-hash "%46oo6" "modules/%46oo6" &&
493 verify_submodule_gitdir_path cloned-hash "%46oo7" "modules/%46oo7" &&
494 verify_submodule_gitdir_path cloned-hash "%46oo8" "modules/%46oo8" &&
495 verify_submodule_gitdir_path cloned-hash "%46oo9" "modules/%46oo9" &&
496 hash=$(printf "Foo" | git hash-object --stdin) &&
497 verify_submodule_gitdir_path cloned-hash "Foo" "modules/${hash}"
498 '
499
500 test_expect_success 'submodule gitdir conflicts with previously encoded name (local config)' '
501 git init -b main super_with_encoded &&
502 (
503 cd super_with_encoded &&
504
505 git config core.repositoryformatversion 1 &&
506 git config extensions.submodulePathConfig true &&
507
508 # Add a submodule with a nested path
509 git submodule add --name "nested/sub" ../sub nested/sub &&
510 test_commit add-encoded-gitdir &&
511
512 verify_submodule_gitdir_path . "nested/sub" "modules/nested%2fsub" &&
513 test_path_is_dir ".git/modules/nested%2fsub"
514 ) &&
515
516 # create a submodule that will conflict with the encoded gitdir name:
517 # the existing gitdir is ".git/modules/nested%2fsub", which is used
518 # by "nested/sub", so the new submod will get another (non-conflicting)
519 # name: "nested%252fsub".
520 (
521 cd super_with_encoded &&
522 git submodule add ../sub "nested%2fsub" &&
523 verify_submodule_gitdir_path . "nested%2fsub" "modules/nested%252fsub" &&
524 test_path_is_dir ".git/modules/nested%252fsub"
525 )
526 '
527
528 test_done