submodule: allow runtime enabling extensions.submodulePathConfig

Add a new config `init.defaultSubmodulePathConfig` which allows enabling `extensions.submodulePathConfig` for new submodules by default (those created via git init or clone). Important: setting init.defaultSubmodulePathConfig = true does not globally enable `extensions.submodulePathConfig`. Existing repositories will still have the extension disabled and will require migration (for example via git submodule--helper command added in the next commit). Suggested-by: Patrick Steinhardt <ps@pks.im> Suggested-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Adrian Ratiu committed Jan 12, 2026 at 20:46 UTC c349bad72969d59758e1294b4e9964dccd967fa0
4 files changed +142
Documentation/config/extensions.adoc
+4
@@ -95,6 +95,10 @@ Git will error out if a module does not have a corresponding
95 Existing (pre-extension) submodules need to be migrated by adding the missing
96 config entries. This is done manually for now, e.g. for each submodule:
97 `git config submodule.<name>.gitdir .git/modules/<name>`.
98 ++
99 +The extension can be enabled automatically for new repositories by setting
100 +`init.defaultSubmodulePathConfig` to `true`, for example by running
101 +`git config --global init.defaultSubmodulePathConfig true`.
102
103 worktreeConfig:::
104 If enabled, then worktrees will load config settings from the
Documentation/config/init.adoc
+6
@@ -18,3 +18,9 @@ endif::[]
18 See `--ref-format=` in linkgit:git-init[1]. Both the command line
19 option and the `GIT_DEFAULT_REF_FORMAT` environment variable take
20 precedence over this config.
21 +
22 +init.defaultSubmodulePathConfig::
23 + A boolean that specifies if `git init` and `git clone` should
24 + automatically set `extensions.submodulePathConfig` to `true`. This
25 + allows all new repositories to automatically use the submodule path
26 + extension. Defaults to `false` when unset.
setup.c
+10
@@ -2228,6 +2228,7 @@ void initialize_repository_version(int hash_algo,
2228 {
2229 struct strbuf repo_version = STRBUF_INIT;
2230 int target_version = GIT_REPO_VERSION;
2231 + int default_submodule_path_config = 0;
2232
2233 /*
2234 * Note that we initialize the repository version to 1 when the ref
@@ -2266,6 +2267,15 @@ void initialize_repository_version(int hash_algo,
2267 clear_repository_format(&repo_fmt);
2268 }
2269
2270 + repo_config_get_bool(the_repository, "init.defaultSubmodulePathConfig",
2271 + &default_submodule_path_config);
2272 + if (default_submodule_path_config) {
2273 + /* extensions.submodulepathconfig requires at least version 1 */
2274 + if (target_version == 0)
2275 + target_version = 1;
2276 + repo_config_set(the_repository, "extensions.submodulepathconfig", "true");
2277 + }
2278 +
2279 strbuf_addf(&repo_version, "%d", target_version);
2280 repo_config_set(the_repository, "core.repositoryformatversion", repo_version.buf);
2281
t/t7425-submodule-gitdir-path-extension.sh
+122
@@ -157,4 +157,126 @@ test_expect_success 'fetch mixed submodule changes and verify updates' '
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_done