From 4173df5187c8ba8bc2cc1a215f25b284d70631da Mon Sep 17 00:00:00 2001 From: Adrian Ratiu Date: Mon, 12 Jan 2026 20:46:25 +0200 Subject: submodule: introduce extensions.submodulePathConfig The idea of this extension is to abstract away the submodule gitdir path implementation: everyone is expected to use the config and not worry about how the path is computed internally, either in git or other implementations. With this extension enabled, the submodule..gitdir repo config becomes the single source of truth for all submodule gitdir paths. The submodule..gitdir config is added automatically for all new submodules when this extension is enabled. Git will throw an error if the extension is enabled and a config is missing, advising users how to migrate. Migration is manual for now. E.g. to add a missing config entry for an existing "foo" module: git config submodule.foo.gitdir .git/modules/foo Suggested-by: Junio C Hamano Suggested-by: Phillip Wood Suggested-by: Patrick Steinhardt Signed-off-by: Adrian Ratiu Signed-off-by: Junio C Hamano --- setup.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'setup.c') diff --git a/setup.c b/setup.c index 7086741e6c..207fa36e10 100644 --- a/setup.c +++ b/setup.c @@ -687,6 +687,9 @@ static enum extension_result handle_extension(const char *var, } else if (!strcmp(ext, "relativeworktrees")) { data->relative_worktrees = git_config_bool(var, value); return EXTENSION_OK; + } else if (!strcmp(ext, "submodulepathconfig")) { + data->submodule_path_cfg = git_config_bool(var, value); + return EXTENSION_OK; } return EXTENSION_UNKNOWN; } @@ -1865,6 +1868,8 @@ const char *setup_git_directory_gently(int *nongit_ok) repo_fmt.worktree_config; the_repository->repository_format_relative_worktrees = repo_fmt.relative_worktrees; + the_repository->repository_format_submodule_path_cfg = + repo_fmt.submodule_path_cfg; /* take ownership of repo_fmt.partial_clone */ the_repository->repository_format_partial_clone = repo_fmt.partial_clone; @@ -1963,6 +1968,8 @@ void check_repository_format(struct repository_format *fmt) fmt->ref_storage_format); the_repository->repository_format_worktree_config = fmt->worktree_config; + the_repository->repository_format_submodule_path_cfg = + fmt->submodule_path_cfg; the_repository->repository_format_relative_worktrees = fmt->relative_worktrees; the_repository->repository_format_partial_clone = -- cgit v1.3-5-g9baa From c349bad72969d59758e1294b4e9964dccd967fa0 Mon Sep 17 00:00:00 2001 From: Adrian Ratiu Date: Mon, 12 Jan 2026 20:46:26 +0200 Subject: 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 Suggested-by: Junio C Hamano Signed-off-by: Adrian Ratiu Signed-off-by: Junio C Hamano --- Documentation/config/extensions.adoc | 4 + Documentation/config/init.adoc | 6 ++ setup.c | 10 +++ t/t7425-submodule-gitdir-path-extension.sh | 122 +++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+) (limited to 'setup.c') diff --git a/Documentation/config/extensions.adoc b/Documentation/config/extensions.adoc index f4f57c9114..e8d9d9a19a 100644 --- a/Documentation/config/extensions.adoc +++ b/Documentation/config/extensions.adoc @@ -95,6 +95,10 @@ Git will error out if a module does not have a corresponding 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..gitdir .git/modules/`. ++ +The extension can be enabled automatically for new repositories by setting +`init.defaultSubmodulePathConfig` to `true`, for example by running +`git config --global init.defaultSubmodulePathConfig true`. worktreeConfig::: If enabled, then worktrees will load config settings from the diff --git a/Documentation/config/init.adoc b/Documentation/config/init.adoc index e45b2a8121..7b4abdaf8b 100644 --- a/Documentation/config/init.adoc +++ b/Documentation/config/init.adoc @@ -18,3 +18,9 @@ endif::[] See `--ref-format=` in linkgit:git-init[1]. Both the command line option and the `GIT_DEFAULT_REF_FORMAT` environment variable take precedence over this config. + +init.defaultSubmodulePathConfig:: + A boolean that specifies if `git init` and `git clone` should + automatically set `extensions.submodulePathConfig` to `true`. This + allows all new repositories to automatically use the submodule path + extension. Defaults to `false` when unset. diff --git a/setup.c b/setup.c index 207fa36e10..3f91a4aaec 100644 --- a/setup.c +++ b/setup.c @@ -2228,6 +2228,7 @@ void initialize_repository_version(int hash_algo, { struct strbuf repo_version = STRBUF_INIT; int target_version = GIT_REPO_VERSION; + int default_submodule_path_config = 0; /* * Note that we initialize the repository version to 1 when the ref @@ -2266,6 +2267,15 @@ void initialize_repository_version(int hash_algo, clear_repository_format(&repo_fmt); } + repo_config_get_bool(the_repository, "init.defaultSubmodulePathConfig", + &default_submodule_path_config); + if (default_submodule_path_config) { + /* extensions.submodulepathconfig requires at least version 1 */ + if (target_version == 0) + target_version = 1; + repo_config_set(the_repository, "extensions.submodulepathconfig", "true"); + } + strbuf_addf(&repo_version, "%d", target_version); repo_config_set(the_repository, "core.repositoryformatversion", repo_version.buf); diff --git a/t/t7425-submodule-gitdir-path-extension.sh b/t/t7425-submodule-gitdir-path-extension.sh index 453183e27c..03ac165de9 100755 --- a/t/t7425-submodule-gitdir-path-extension.sh +++ b/t/t7425-submodule-gitdir-path-extension.sh @@ -157,4 +157,126 @@ test_expect_success 'fetch mixed submodule changes and verify updates' ' ) ' +test_expect_success '`git init` respects init.defaultSubmodulePathConfig' ' + test_config_global init.defaultSubmodulePathConfig true && + git init repo-init && + git -C repo-init config extensions.submodulePathConfig >actual && + echo true >expect && + test_cmp expect actual && + # create a submodule and check gitdir + ( + cd repo-init && + git init -b main sub && + test_commit -C sub sub-initial && + git submodule add ./sub sub && + git config submodule.sub.gitdir >actual && + echo ".git/modules/sub" >expect && + test_cmp expect actual + ) +' + +test_expect_success '`git init` does not set extension by default' ' + git init upstream && + test_commit -C upstream initial && + test_must_fail git -C upstream config extensions.submodulePathConfig && + # create a pair of submodules and check gitdir is not created + git init -b main sub && + test_commit -C sub sub-initial && + ( + cd upstream && + git submodule add ../sub sub1 && + test_path_is_dir .git/modules/sub1 && + test_must_fail git config submodule.sub1.gitdir && + git submodule add ../sub sub2 && + test_path_is_dir .git/modules/sub2 && + test_must_fail git config submodule.sub2.gitdir && + git commit -m "Add submodules" + ) +' + +test_expect_success '`git clone` does not set extension by default' ' + test_when_finished "rm -rf repo-clone-no-ext" && + git clone upstream repo-clone-no-ext && + ( + cd repo-clone-no-ext && + + test_must_fail git config extensions.submodulePathConfig && + test_path_is_missing .git/modules/sub1 && + test_path_is_missing .git/modules/sub2 && + + # create a submodule and check gitdir is not created + git submodule add ../sub sub3 && + test_must_fail git config submodule.sub3.gitdir + ) +' + +test_expect_success '`git clone --recurse-submodules` does not set extension by default' ' + test_when_finished "rm -rf repo-clone-no-ext" && + git clone --recurse-submodules upstream repo-clone-no-ext && + ( + cd repo-clone-no-ext && + + # verify that that submodules do not have gitdir set + test_must_fail git config extensions.submodulePathConfig && + test_path_is_dir .git/modules/sub1 && + test_must_fail git config submodule.sub1.gitdir && + test_path_is_dir .git/modules/sub2 && + test_must_fail git config submodule.sub2.gitdir && + + # create another submodule and check that gitdir is not created + git submodule add ../sub sub3 && + test_path_is_dir .git/modules/sub3 && + test_must_fail git config submodule.sub3.gitdir + ) + +' + +test_expect_success '`git clone` respects init.defaultSubmodulePathConfig' ' + test_when_finished "rm -rf repo-clone" && + test_config_global init.defaultSubmodulePathConfig true && + git clone upstream repo-clone && + ( + cd repo-clone && + + # verify new repo extension is inherited from global config + git config extensions.submodulePathConfig >actual && + echo true >expect && + test_cmp expect actual && + + # new submodule has a gitdir config + git submodule add ../sub sub && + test_path_is_dir .git/modules/sub && + git config submodule.sub.gitdir >actual && + echo ".git/modules/sub" >expect && + test_cmp expect actual + ) +' + +test_expect_success '`git clone --recurse-submodules` respects init.defaultSubmodulePathConfig' ' + test_when_finished "rm -rf repo-clone-recursive" && + test_config_global init.defaultSubmodulePathConfig true && + git clone --recurse-submodules upstream repo-clone-recursive && + ( + cd repo-clone-recursive && + + # verify new repo extension is inherited from global config + git config extensions.submodulePathConfig >actual && + echo true >expect && + test_cmp expect actual && + + # previous submodules should exist + git config submodule.sub1.gitdir && + git config submodule.sub2.gitdir && + test_path_is_dir .git/modules/sub1 && + test_path_is_dir .git/modules/sub2 && + + # create another submodule and check that gitdir is created + git submodule add ../sub new-sub && + test_path_is_dir .git/modules/new-sub && + git config submodule.new-sub.gitdir >actual && + echo ".git/modules/new-sub" >expect && + test_cmp expect actual + ) +' + test_done -- cgit v1.3-5-g9baa