Subject: [PATCH v6 05/10] submodule: allow runtime enabling extensions.submodulePathConfig

From: Adrian Ratiu <[email-masked]>
Date: Sat, 13 Dec 2025 10:08:11 +0200

This adds the ability to enable the new extension via a runtime
config to avoid having to enable it in each repo configuration.

Suggested-by: Patrick Steinhardt <[email-masked]>
Suggested-by: Junio C Hamano <[email-masked]>
Signed-off-by: Adrian Ratiu <[email-masked]>
---
 Documentation/config/extensions.adoc       |  2 +
 builtin/submodule--helper.c                |  8 +++-
 submodule.c                                |  4 +-
 submodule.h                                |  2 +
 t/t7425-submodule-gitdir-path-extension.sh | 46 ++++++++++++++++++++++
 5 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/Documentation/config/extensions.adoc b/Documentation/config/extensions.adoc
index 6ce1dcc98b..929e4e1bf1 100644
--- a/Documentation/config/extensions.adoc
+++ b/Documentation/config/extensions.adoc
@@ -77,6 +77,8 @@ submodulePathConfig:::
 	If enabled, the submodule.<name>.gitdir config is the single source of
 	truth for submodule gitdir paths and is always set for new submodules.
 	Git will error if a module does not have submodule.<name>.gitdir set.
+	This extension can also be enabled as a global runtime config, with
+	the local repository config having precedence (overwrites it).
 	Existing pre-extension submodules need to be migrated by adding the
 	missing config entries. This is done manually for now, e.g. for each
 	submodule: "git config submodule.<name>.gitdir .git/modules/<name>".
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 699ac32004..2b5b4f575b 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -466,7 +466,8 @@ static void create_default_gitdir_config(const char *submodule_name)
 	struct strbuf gitdir_path = STRBUF_INIT;
 
 	/* The config is set only when extensions.submodulePathConfig is enabled */
-	if (!the_repository->repository_format_submodule_path_cfg)
+	if (!the_repository->repository_format_submodule_path_cfg &&
+	    !submodule_path_config_enabled)
 		return;
 
 	repo_git_path_append(the_repository, &gitdir_path, "modules/%s", submodule_name);
@@ -3483,7 +3484,7 @@ static void die_on_repo_without_commits(const char *path)
 static int module_add(int argc, const char **argv, const char *prefix,
 		      struct repository *repo UNUSED)
 {
-	int force = 0, quiet = 0, progress = 0, dissociate = 0;
+	int force = 0, quiet = 0, progress = 0, dissociate = 0, path_cfg_ext = 0;
 	struct add_data add_data = ADD_DATA_INIT;
 	const char *ref_storage_format = NULL;
 	char *to_free = NULL;
@@ -3517,6 +3518,9 @@ static int module_add(int argc, const char **argv, const char *prefix,
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
+	if (!repo_config_get_bool(the_repository, "extensions.submodulepathconfig", &path_cfg_ext))
+		submodule_path_config_enabled = path_cfg_ext;
+
 	if (!is_writing_gitmodules_ok())
 		die(_("please make sure that the .gitmodules file is in the working tree"));
 
diff --git a/submodule.c b/submodule.c
index 85ca7ea0fb..5752909999 100644
--- a/submodule.c
+++ b/submodule.c
@@ -32,6 +32,8 @@
 #include "read-cache-ll.h"
 #include "setup.h"
 
+int submodule_path_config_enabled;
+
 static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
 static int initialized_fetch_ref_tips;
 static struct oid_array ref_tips_before_fetch;
@@ -2575,7 +2577,7 @@ void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
 	int ret;
 
 	/* If extensions.submodulePathConfig is disabled, continue to use the plain path */
-	if (!r->repository_format_submodule_path_cfg) {
+	if (!r->repository_format_submodule_path_cfg && !submodule_path_config_enabled) {
 		repo_git_path_append(r, buf, "modules/%s", submodule_name);
 		if (validate_submodule_git_dir(buf->buf, submodule_name) < 0)
 			die(_("refusing to create/use '%s' in another submodule's "
diff --git a/submodule.h b/submodule.h
index b10e16e6c0..c15630bf26 100644
--- a/submodule.h
+++ b/submodule.h
@@ -172,4 +172,6 @@ void absorb_git_dir_into_superproject(const char *path,
  */
 int get_superproject_working_tree(struct strbuf *buf);
 
+extern int submodule_path_config_enabled;
+
 #endif
diff --git a/t/t7425-submodule-gitdir-path-extension.sh b/t/t7425-submodule-gitdir-path-extension.sh
index 5d52a289f8..2f198bff82 100755
--- a/t/t7425-submodule-gitdir-path-extension.sh
+++ b/t/t7425-submodule-gitdir-path-extension.sh
@@ -135,4 +135,50 @@ test_expect_success 'fetch mixed submodule changes and verify updates' '
 	)
 '
 
+test_expect_success 'runtime config extensions.submodulePathConfig on new repo' '
+	git config --global extensions.submodulePathConfig true &&
+	git init -b main runtime-test-new-repo &&
+	(
+		cd runtime-test-new-repo &&
+
+		git init -b main sub &&
+		test_commit -C sub sub-initial &&
+
+		git submodule add ./sub sub &&
+
+		# Verify that the gitdir config was created correctly
+		git config submodule.sub.gitdir > actual &&
+		echo ".git/modules/sub" > expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'runtime config extensions.submodulePathConfig on existing repo' '
+	# create a repo with the extension disabled then enable it
+	git config --global extensions.submodulePathConfig false &&
+	git init -b main runtime-test-existing-repo &&
+	(
+		cd runtime-test-existing-repo &&
+
+		git init -b main sub &&
+		test_commit -C sub sub-initial &&
+
+		git submodule add ./sub sub &&
+
+		# gitdir should not exist for this repo: it must be migrated
+		test_must_fail git config submodule.sub.gitdir
+	) &&
+	git config --global extensions.submodulePathConfig true &&
+	(
+		cd runtime-test-existing-repo &&
+
+		git submodule add ./sub sub2 &&
+
+		# gitdir should exist after enabling the global config
+		git config submodule.sub2.gitdir > actual &&
+		echo ".git/modules/sub2" > expect &&
+		test_cmp expect actual
+	)
+'
+
 test_done
-- 
2.51.2