@@ -34,11 +34,38 @@ test_expect_success 'add superproject worktree' '
3434 git -C main worktree add "$base_path/worktree" "$rev1_hash_main"
3535'
3636
37- test_expect_failure ' submodule is checked out just after worktree add' '
37+ test_expect_failure ' submodule is checked out just after worktree add (without flag) ' '
3838 git -C worktree diff --submodule main"^!" >out &&
3939 grep "file1 updated" out
4040'
4141
42+ test_expect_success ' worktree add --recurse-submodules initializes submodules' '
43+ git -C main worktree add --recurse-submodules \
44+ "$base_path/worktree-recurse" "$rev1_hash_main" &&
45+ git -C worktree-recurse diff --submodule main"^!" >out &&
46+ grep "file1 updated" out
47+ '
48+
49+ test_expect_success ' submodule in --recurse-submodules worktree uses per-worktree gitdir' '
50+ # The per-worktree submodule gitdir must live under the worktree entry,
51+ # not under $GIT_COMMON_DIR/modules/, so it is cleaned up with the
52+ # worktree and does not disturb the main worktree submodule.
53+ sub_gitdir="$base_path/main/.git/modules/sub/worktrees/worktree-recurse" &&
54+ test -d "$sub_gitdir" &&
55+ # .git pointer in the working tree must reference the per-worktree gitdir
56+ echo "gitdir: ../../main/.git/modules/sub/worktrees/worktree-recurse" \
57+ >expect-gitfile &&
58+ cat "$base_path/worktree-recurse/sub/.git" >actual-gitfile &&
59+ test_cmp expect-gitfile actual-gitfile &&
60+ # The per-worktree gitdir must have a commondir file pointing at the
61+ # shared submodule repo, not its own object store.
62+ echo "../.." >expect-commondir &&
63+ test_cmp expect-commondir "$sub_gitdir/commondir" &&
64+ test_path_is_missing "$sub_gitdir/objects" &&
65+ # The working tree is populated (test_commit creates <name>.t files)
66+ test -f "$base_path/worktree-recurse/sub/file1.t"
67+ '
68+
4269test_expect_success ' add superproject worktree and initialize submodules' '
4370 git -C main worktree add "$base_path/worktree-submodule-update" "$rev1_hash_main" &&
4471 git -C worktree-submodule-update submodule update
@@ -62,7 +89,7 @@ test_expect_success 'submodule is checked out after manually adding submodule wo
6289test_expect_success ' checkout --recurse-submodules uses $GIT_DIR for submodules in a linked worktree' '
6390 git -C main worktree add "$base_path/checkout-recurse" --detach &&
6491 git -C checkout-recurse submodule update --init &&
65- echo "gitdir: ../../main/.git/worktrees/checkout-recurse/modules/sub " >expect-gitfile &&
92+ echo "gitdir: ../../main/.git/modules/sub/ worktrees/checkout-recurse" >expect-gitfile &&
6693 cat checkout-recurse/sub/.git >actual-gitfile &&
6794 test_cmp expect-gitfile actual-gitfile &&
6895 git -C main/sub rev-parse HEAD >expect-head-main &&
@@ -73,22 +100,94 @@ test_expect_success 'checkout --recurse-submodules uses $GIT_DIR for submodules
73100 test_cmp expect-head-main actual-head-main
74101'
75102
76- test_expect_success ' core.worktree is removed in $GIT_DIR/modules/<name>/config, not in $GIT_COMMON_DIR/modules/<name>/config' '
103+ test_expect_success ' per-worktree submodule gitdir uses commondir; shared config is unchanged' '
104+ # The shared submodule repo core.worktree points at the main worktree.
77105 echo "../../../sub" >expect-main &&
78106 git -C main/sub config --get core.worktree >actual-main &&
79107 test_cmp expect-main actual-main &&
80- echo "../../../../../../checkout-recurse/sub" >expect-linked &&
81- git -C checkout-recurse/sub config --get core.worktree >actual-linked &&
82- test_cmp expect-linked actual-linked &&
108+
109+ # The per-worktree submodule gitdir has a commondir file pointing at
110+ # the shared submodule repo, not its own objects or refs.
111+ linked_sm_gitdir="main/.git/modules/sub/worktrees/checkout-recurse" &&
112+ test -f "$linked_sm_gitdir/commondir" &&
113+ echo "../.." >expect-commondir &&
114+ test_cmp expect-commondir "$linked_sm_gitdir/commondir" &&
115+
116+ # Checking out a commit that removes the submodule leaves the shared
117+ # submodule repo intact.
83118 git -C checkout-recurse checkout --recurse-submodules first &&
84- test_expect_code 1 git -C main/.git/worktrees/checkout-recurse/modules/sub config --get core.worktree >linked-config &&
85- test_must_be_empty linked-config &&
86119 git -C main/sub config --get core.worktree >actual-main &&
87120 test_cmp expect-main actual-main
88121'
89122
123+ test_expect_success ' worktree remove cleans up per-worktree submodule gitdir' '
124+ git -C main worktree add "$base_path/remove-recurse" "$rev1_hash_main" &&
125+ git -C remove-recurse submodule update --init &&
126+ test -d "main/.git/modules/sub/worktrees/remove-recurse" &&
127+ git -C main worktree remove remove-recurse &&
128+ test_path_is_missing "main/.git/worktrees/remove-recurse" &&
129+ test_path_is_missing "remove-recurse" &&
130+ # The per-worktree submodule gitdir must also be removed.
131+ test_path_is_missing "main/.git/modules/sub/worktrees/remove-recurse" &&
132+ # The shared submodule repo must not be affected.
133+ test -d "main/.git/modules/sub" &&
134+ git -C main/sub log --oneline -1
135+ '
136+
90137test_expect_success ' unsetting core.worktree does not prevent running commands directly against the submodule repository' '
91- git -C main/.git/worktrees/checkout-recurse/modules/sub log
138+ git -C main/.git/modules/sub/worktrees/checkout-recurse log
139+ '
140+
141+ test_expect_success ' auto-absorb: submodule with in-tree gitdir is absorbed on first linked-worktree submodule init' '
142+ # Clone the superproject without initializing the submodule, then
143+ # clone the submodule in-tree (legacy layout: .git/ is a directory,
144+ # not absorbed into $GIT_DIR/modules/).
145+ git clone "$base_path/origin/main" main-intree &&
146+ test_when_finished "rm -rf main-intree worktree-absorb" &&
147+ git -C main-intree submodule init &&
148+ git clone "$base_path/origin/sub" main-intree/sub &&
149+ git -C main-intree/sub checkout "$rev1_hash_sub" &&
150+ # The submodule gitdir is in-tree: a directory, not a pointer file.
151+ test -d "main-intree/sub/.git" &&
152+ test_path_is_missing "main-intree/.git/modules" &&
153+
154+ # Initialize the submodule in a linked worktree: absorb_in_main_worktree
155+ # should relocate the in-tree gitdir to modules/sub, then set up the
156+ # per-worktree gitdir with commondir indirection.
157+ git -C main-intree worktree add "$base_path/worktree-absorb" "$rev1_hash_main" &&
158+ git -C worktree-absorb submodule update --init &&
159+
160+ # Absorption happened: submodule gitdir now lives under modules/.
161+ test -d "main-intree/.git/modules/sub" &&
162+ # Per-worktree gitdir with commondir exists.
163+ test -f "main-intree/.git/modules/sub/worktrees/worktree-absorb/commondir" &&
164+ # Submodule working tree is populated.
165+ test -f "worktree-absorb/sub/file1.t"
166+ '
167+
168+ test_expect_success ' worktree remove handles submodule with slash in name (nested modules path)' '
169+ # Create a superproject with a submodule whose path contains a slash so
170+ # that its gitdir lives at modules/nested/sub/ rather than modules/sub/.
171+ git init nested-super &&
172+ test_when_finished "rm -rf nested-super nested-worktree" &&
173+ git init nested-super/nested/sub &&
174+ test_commit -C nested-super/nested/sub file1 &&
175+ (
176+ cd nested-super &&
177+ git -c protocol.file.allow=always submodule add ./nested/sub nested/sub &&
178+ git commit -m "add nested/sub"
179+ ) &&
180+
181+ git -C nested-super worktree add "$base_path/nested-worktree" HEAD &&
182+ git -C nested-worktree submodule update --init &&
183+
184+ # The per-worktree gitdir should be at modules/nested/sub/ with commondir.
185+ test -f "nested-super/.git/modules/nested/sub/worktrees/nested-worktree/commondir" &&
186+
187+ # worktree remove must succeed: all submodule gitdirs are per-worktree.
188+ git -C nested-super worktree remove "$base_path/nested-worktree" &&
189+ test_path_is_missing "nested-super/.git/worktrees/nested-worktree" &&
190+ test_path_is_missing "nested-worktree"
92191'
93192
94193test_done
0 commit comments