git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 2/9] submodule: create new gitdirs under submodules path

From
PWPhillip Wood <phillip.wood123@gmail.com>
Date
Sep 8, 2025, 14:24 UTC
Message-ID
<fc69ee66-815f-48ec-a5fb-99cac5f4d58c@gmail.com>
In-Reply-To
<20250816213642.3517822-3-adrian.ratiu@collabora.com>
Hi Adrian
On 16/08/2025 22:36, Adrian Ratiu wrote:
Show 14 quoted lines
> This is in preparation for encoding the submodule names to avoid conflicts
> like submodules named foo and foo/bar together with case-insensitive file-
> system handling and other corner cases like reserved filenames on Windows.
> 
> Backward compatibility is kept with plain-name modules already existing at
> paths like .git/modules/<name>, however a clear separation between legacy
> (plain) and new (encoded) namespaces is desirable, to avoid situations like
> an existing plain-name module containing the encoding escape character/
> 
> Thus we split the new-style (encoded) gitdir name paths to .git/submodules,
> while legacy-style paths remain under .git/modules.
> 
> This is just a default directory change with the accompanying test updates,
> in preparation for the actual encoding additions in future commits.

Does this need an extentions.submoduleEncoding (name suggestions welcome) config key to stop older versions of git trying to read the repository as they wont be able to locate the gitdir of any submodules added under .git/submodules?

Thanks
Phillip
Show 967 quoted lines
> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
> ---
>   Documentation/fetch-options.adoc           |  2 +-
>   Documentation/git-fetch.adoc               |  2 +-
>   Documentation/git-submodule.adoc           |  2 +-
>   Documentation/gitsubmodules.adoc           |  8 ++--
>   setup.c                                    |  2 +-
>   submodule.c                                | 28 +++++++++---
>   t/lib-submodule-update.sh                  | 50 +++++++++++-----------
>   t/t0035-safe-bare-repository.sh            |  4 +-
>   t/t1600-index.sh                           |  4 +-
>   t/t2405-worktree-submodule.sh              |  8 ++--
>   t/t2501-cwd-empty.sh                       |  2 +-
>   t/t3600-rm.sh                              |  8 ++--
>   t/t5526-fetch-submodules.sh                |  2 +-
>   t/t5619-clone-local-ambiguous-transport.sh |  4 +-
>   t/t6120-describe.sh                        |  4 +-
>   t/t7001-mv.sh                              |  4 +-
>   t/t7400-submodule-basic.sh                 | 18 ++++----
>   t/t7406-submodule-update.sh                | 10 ++---
>   t/t7407-submodule-foreach.sh               |  6 +--
>   t/t7408-submodule-reference.sh             | 22 +++++-----
>   t/t7412-submodule-absorbgitdirs.sh         | 22 +++++-----
>   t/t7423-submodule-symlinks.sh              |  8 ++--
>   t/t7450-bad-git-dotfiles.sh                | 32 +++++++-------
>   t/t7527-builtin-fsmonitor.sh               |  4 +-
>   24 files changed, 136 insertions(+), 120 deletions(-)
> 
> diff --git a/Documentation/fetch-options.adoc b/Documentation/fetch-options.adoc
> index b01372e4b3..f8d3f65009 100644
> --- a/Documentation/fetch-options.adoc
> +++ b/Documentation/fetch-options.adoc
> @@ -210,7 +210,7 @@ ifndef::git-pull[]
>   	submodule that has commits that are referenced by a newly fetched
>   	superproject commit but are missing in the local submodule clone. A
>   	changed submodule can be fetched as long as it is present locally e.g.
> -	in `$GIT_DIR/modules/` (see linkgit:gitsubmodules[7]); if the upstream
> +	in `$GIT_DIR/submodules/` (see linkgit:gitsubmodules[7]); if the upstream
>   	adds a new submodule, that submodule cannot be fetched until it is
>   	cloned e.g. by `git submodule update`.
>   +
> diff --git a/Documentation/git-fetch.adoc b/Documentation/git-fetch.adoc
> index 16f5d9d69a..2923a29bef 100644
> --- a/Documentation/git-fetch.adoc
> +++ b/Documentation/git-fetch.adoc
> @@ -304,7 +304,7 @@ include::config/fetch.adoc[]
>   BUGS
>   ----
>   Using --recurse-submodules can only fetch new commits in submodules that are
> -present locally e.g. in `$GIT_DIR/modules/`. If the upstream adds a new
> +present locally e.g. in `$GIT_DIR/submodules/`. If the upstream adds a new
>   submodule, that submodule cannot be fetched until it is cloned e.g. by `git
>   submodule update`. This is expected to be fixed in a future Git version.
>   
> diff --git a/Documentation/git-submodule.adoc b/Documentation/git-submodule.adoc
> index 503c84a200..9389862208 100644
> --- a/Documentation/git-submodule.adoc
> +++ b/Documentation/git-submodule.adoc
> @@ -266,7 +266,7 @@ registered submodules, and sync any nested submodules within.
>   absorbgitdirs::
>   	If a git directory of a submodule is inside the submodule,
>   	move the git directory of the submodule into its superproject's
> -	`$GIT_DIR/modules` path and then connect the git directory and
> +	`$GIT_DIR/submodules` path and then connect the git directory and
>   	its working directory by setting the `core.worktree` and adding
>   	a .git file pointing to the git directory embedded in the
>   	superprojects git directory.
> diff --git a/Documentation/gitsubmodules.adoc b/Documentation/gitsubmodules.adoc
> index f7b5a25a0c..061e24f316 100644
> --- a/Documentation/gitsubmodules.adoc
> +++ b/Documentation/gitsubmodules.adoc
> @@ -21,12 +21,12 @@ The submodule has its own history; the repository it is embedded
>   in is called a superproject.
>   
>   On the filesystem, a submodule usually (but not always - see FORMS below)
> -consists of (i) a Git directory located under the `$GIT_DIR/modules/`
> +consists of (i) a Git directory located under the `$GIT_DIR/submodules/`
>   directory of its superproject, (ii) a working directory inside the
>   superproject's working directory, and a `.git` file at the root of
>   the submodule's working directory pointing to (i).
>   
> -Assuming the submodule has a Git directory at `$GIT_DIR/modules/foo/`
> +Assuming the submodule has a Git directory at `$GIT_DIR/submodules/foo/`
>   and a working directory at `path/to/bar/`, the superproject tracks the
>   submodule via a `gitlink` entry in the tree at `path/to/bar` and an entry
>   in its `.gitmodules` file (see linkgit:gitmodules[5]) of the form
> @@ -137,7 +137,7 @@ using older versions of Git.
>   It is possible to construct these old form repositories manually.
>   +
>   When deinitialized or deleted (see below), the submodule's Git
> -directory is automatically moved to `$GIT_DIR/modules/<name>/`
> +directory is automatically moved to `$GIT_DIR/submodules/<name>/`
>   of the superproject.
>   
>    * Deinitialized submodule: A `gitlink`, and a `.gitmodules` entry,
> @@ -162,7 +162,7 @@ possible to checkout past commits without requiring fetching
>   from another repository.
>   +
>   To completely remove a submodule, manually delete
> -`$GIT_DIR/modules/<name>/`.
> +`$GIT_DIR/submodules/<name>/`.
>   
>   ACTIVE SUBMODULES
>   -----------------
> diff --git a/setup.c b/setup.c
> index 98ddbf377f..d054dafa6a 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -1416,7 +1416,7 @@ static int is_implicit_bare_repo(const char *path)
>   	 * we are inside $GIT_DIR of a worktree of a non-embedded
>   	 * submodule, whose superproject is not a bare repository.
>   	 */
> -	if (strstr(path, "/.git/modules/"))
> +	if (strstr(path, "/.git/modules/") || strstr(path, "/.git/submodules/"))
>   		return 1;
>   
>   	return 0;
> diff --git a/submodule.c b/submodule.c
> index fff3c75570..dbf2244e60 100644
> --- a/submodule.c
> +++ b/submodule.c
> @@ -1278,22 +1278,29 @@ void check_for_new_submodule_commits(struct object_id *oid)
>   
>   /*
>    * Returns 1 if there is at least one submodule gitdir in
> - * $GIT_DIR/modules and 0 otherwise. This follows
> + * $GIT_DIR/(sub)modules and 0 otherwise. This follows
>    * submodule_name_to_gitdir(), which looks for submodules in
> - * $GIT_DIR/modules, not $GIT_COMMON_DIR.
> + * $GIT_DIR/(sub)modules, not $GIT_COMMON_DIR.
>    *
> - * A submodule can be moved to $GIT_DIR/modules manually by running "git
> - * submodule absorbgitdirs", or it may be initialized there by "git
> - * submodule update".
> + * A submodule can be moved to $GIT_DIR/(sub)modules manually by running
> + * "git submodule absorbgitdirs", or it may be initialized there by
> + * "git submodule update".
>    */
>   static int repo_has_absorbed_submodules(struct repository *r)
>   {
>   	int ret;
>   	struct strbuf buf = STRBUF_INIT;
>   
> +	/* check legacy path */
>   	repo_git_path_append(r, &buf, "modules/");
>   	ret = file_exists(buf.buf) && !is_empty_dir(buf.buf);
> +	strbuf_reset(&buf);
> +
> +	/* new (encoded name) path */
> +	repo_git_path_append(r, &buf, "submodules/");
> +	ret |= file_exists(buf.buf) && !is_empty_dir(buf.buf);
>   	strbuf_release(&buf);
> +
>   	return ret;
>   }
>   
> @@ -2273,7 +2280,7 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
>   	 *
>   	 * Example: having a submodule named `hippo` and another one named
>   	 * `hippo/hooks` would result in the git directories
> -	 * `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
> +	 * `.git/submodules/hippo/` and `.git/submodules/hippo/hooks/`, respectively,
>   	 * but the latter directory is already designated to contain the hooks
>   	 * of the former.
>   	 */
> @@ -2604,6 +2611,15 @@ void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
>   	 * administrators can explicitly set. Nothing has been decided,
>   	 * so for now, just append the name at the end of the path.
>   	 */
> +
> +	/* Legacy behavior: allow existing paths under modules/<name>. */
>   	repo_git_path_append(r, buf, "modules/");
>   	strbuf_addstr(buf, submodule_name);
> +	if (!access(buf->buf, F_OK))
> +		return;
> +
> +	/* New style (encoded) paths go under submodules/<encoded>. */
> +	strbuf_reset(buf);
> +	repo_git_path_append(r, buf, "submodules/");
> +	strbuf_addstr(buf, submodule_name);
>   }
> diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
> index 36f767cb74..b6b2be1df5 100644
> --- a/t/lib-submodule-update.sh
> +++ b/t/lib-submodule-update.sh
> @@ -161,7 +161,7 @@ replace_gitfile_with_git_dir () {
>   }
>   
>   # Test that the .git directory in the submodule is unchanged (except for the
> -# core.worktree setting, which appears only in $GIT_DIR/modules/$1/config).
> +# core.worktree setting, which appears only in $GIT_DIR/submodules/$1/config).
>   # Call this function before test_submodule_content as the latter might
>   # write the index file leading to false positive index differences.
>   #
> @@ -170,23 +170,23 @@ replace_gitfile_with_git_dir () {
>   test_git_directory_is_unchanged () {
>   	# does core.worktree point at the right place?
>   	echo "../../../$1" >expect &&
> -	git -C ".git/modules/$1" config core.worktree >actual &&
> +	git -C ".git/submodules/$1" config core.worktree >actual &&
>   	test_cmp expect actual &&
>   	# remove it temporarily before comparing, as
>   	# "$1/.git/config" lacks it...
> -	git -C ".git/modules/$1" config --unset core.worktree &&
> -	diff -r ".git/modules/$1" "$1/.git" &&
> +	git -C ".git/submodules/$1" config --unset core.worktree &&
> +	diff -r ".git/submodules/$1" "$1/.git" &&
>   	# ... and then restore.
> -	git -C ".git/modules/$1" config core.worktree "../../../$1"
> +	git -C ".git/submodules/$1" config core.worktree "../../../$1"
>   }
>   
>   test_git_directory_exists () {
> -	test -e ".git/modules/$1" &&
> +	test -e ".git/submodules/$1" &&
>   	if test -f sub1/.git
>   	then
>   		# does core.worktree point at the right place?
>   		echo "../../../$1" >expect &&
> -		git -C ".git/modules/$1" config core.worktree >actual &&
> +		git -C ".git/submodules/$1" config core.worktree >actual &&
>   		test_cmp expect actual
>   	fi
>   }
> @@ -225,22 +225,22 @@ reset_work_tree_to () {
>   reset_work_tree_to_interested () {
>   	reset_work_tree_to $1 &&
>   	# make the submodule git dirs available
> -	if ! test -d submodule_update/.git/modules/sub1
> +	if ! test -d submodule_update/.git/submodules/sub1
>   	then
> -		mkdir -p submodule_update/.git/modules &&
> -		cp -r submodule_update_repo/.git/modules/sub1 submodule_update/.git/modules/sub1
> -		GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1 config --unset core.worktree
> +		mkdir -p submodule_update/.git/submodules &&
> +		cp -r submodule_update_repo/.git/submodules/sub1 submodule_update/.git/submodules/sub1
> +		GIT_WORK_TREE=. git -C submodule_update/.git/submodules/sub1 config --unset core.worktree
>   	fi &&
> -	if ! test -d submodule_update/.git/modules/sub1/modules/sub2
> +	if ! test -d submodule_update/.git/submodules/sub1/submodules/sub2
>   	then
> -		mkdir -p submodule_update/.git/modules/sub1/modules &&
> -		cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
> +		mkdir -p submodule_update/.git/submodules/sub1/submodules &&
> +		cp -r submodule_update_repo/.git/submodules/sub1/submodules/sub2 submodule_update/.git/submodules/sub1/submodules/sub2
>   		# core.worktree is unset for sub2 as it is not checked out
>   	fi &&
>   	# indicate we are interested in the submodule:
>   	git -C submodule_update config submodule.sub1.url "bogus" &&
>   	# sub1 might not be checked out, so use the git dir
> -	git -C submodule_update/.git/modules/sub1 config submodule.sub2.url "bogus"
> +	git -C submodule_update/.git/submodules/sub1 config submodule.sub2.url "bogus"
>   }
>   
>   # Test that the superproject contains the content according to commit "$1"
> @@ -742,7 +742,7 @@ test_submodule_recursing_with_args_common () {
>   			$command remove_sub1 &&
>   			test_superproject_content origin/remove_sub1 &&
>   			! test -e sub1 &&
> -			test_must_fail git config -f .git/modules/sub1/config core.worktree
> +			test_must_fail git config -f .git/submodules/sub1/config core.worktree
>   		)
>   	'
>   	# ... absorbing a .git directory along the way.
> @@ -753,7 +753,7 @@ test_submodule_recursing_with_args_common () {
>   			cd submodule_update &&
>   			git branch -t remove_sub1 origin/remove_sub1 &&
>   			replace_gitfile_with_git_dir sub1 &&
> -			rm -rf .git/modules &&
> +			rm -rf .git/submodules &&
>   			$command remove_sub1 &&
>   			test_superproject_content origin/remove_sub1 &&
>   			! test -e sub1 &&
> @@ -803,8 +803,8 @@ test_submodule_recursing_with_args_common () {
>   			$command no_submodule &&
>   			test_superproject_content origin/no_submodule &&
>   			test_path_is_missing sub1 &&
> -			test_must_fail git config -f .git/modules/sub1/config core.worktree &&
> -			test_must_fail git config -f .git/modules/sub1/modules/sub2/config core.worktree
> +			test_must_fail git config -f .git/submodules/sub1/config core.worktree &&
> +			test_must_fail git config -f .git/submodules/sub1/submodules/sub2/config core.worktree
>   		)
>   	'
>   
> @@ -937,7 +937,7 @@ test_submodule_switch_recursing_with_args () {
>   			cd submodule_update &&
>   			git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
>   			replace_gitfile_with_git_dir sub1 &&
> -			rm -rf .git/modules &&
> +			rm -rf .git/submodules &&
>   			$command replace_sub1_with_directory &&
>   			test_superproject_content origin/replace_sub1_with_directory &&
>   			test_git_directory_exists sub1
> @@ -946,15 +946,15 @@ test_submodule_switch_recursing_with_args () {
>   
>   	# ... and ignored files are ignored
>   	test_expect_success "$command: replace submodule with a file works ignores ignored files in submodule" '
> -		test_when_finished "rm submodule_update/.git/modules/sub1/info/exclude" &&
> +		test_when_finished "rm submodule_update/.git/submodules/sub1/info/exclude" &&
>   		prolog &&
>   		reset_work_tree_to_interested add_sub1 &&
>   		(
>   			cd submodule_update &&
> -			rm -rf .git/modules/sub1/info &&
> +			rm -rf .git/submodules/sub1/info &&
>   			git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
> -			mkdir .git/modules/sub1/info &&
> -			echo ignored >.git/modules/sub1/info/exclude &&
> +			mkdir .git/submodules/sub1/info &&
> +			echo ignored >.git/submodules/sub1/info/exclude &&
>   			: >sub1/ignored &&
>   			$command replace_sub1_with_file &&
>   			test_superproject_content origin/replace_sub1_with_file &&
> @@ -1034,7 +1034,7 @@ test_submodule_forced_switch_recursing_with_args () {
>   			cd submodule_update &&
>   			git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
>   			replace_gitfile_with_git_dir sub1 &&
> -			rm -rf .git/modules/sub1 &&
> +			rm -rf .git/submodules/sub1 &&
>   			$command replace_sub1_with_directory &&
>   			test_superproject_content origin/replace_sub1_with_directory &&
>   			test_git_directory_exists sub1
> diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
> index ae7ef092ab..a480ddf8d6 100755
> --- a/t/t0035-safe-bare-repository.sh
> +++ b/t/t0035-safe-bare-repository.sh
> @@ -41,7 +41,7 @@ test_expect_success 'setup an embedded bare repo, secondary worktree and submodu
>   			submodule add --name subn -- ./bare-repo subd
>   	) &&
>   	test_path_is_dir outer-repo/.git/worktrees/outer-secondary &&
> -	test_path_is_dir outer-repo/.git/modules/subn
> +	test_path_is_dir outer-repo/.git/submodules/subn
>   '
>   
>   test_expect_success 'safe.bareRepository unset' '
> @@ -100,7 +100,7 @@ test_expect_success 'no trace in $GIT_DIR of secondary worktree' '
>   '
>   
>   test_expect_success 'no trace in $GIT_DIR of a submodule' '
> -	expect_accepted_implicit -C outer-repo/.git/modules/subn
> +	expect_accepted_implicit -C outer-repo/.git/submodules/subn
>   '
>   
>   test_done
> diff --git a/t/t1600-index.sh b/t/t1600-index.sh
> index 03239e9faa..0e5e8efb20 100755
> --- a/t/t1600-index.sh
> +++ b/t/t1600-index.sh
> @@ -87,10 +87,10 @@ test_expect_success 'index.skipHash config option' '
>   	git -c protocol.file.allow=always submodule add ./ sub &&
>   	git config index.skipHash false &&
>   	git -C sub config index.skipHash true &&
> -	rm -f .git/modules/sub/index &&
> +	rm -f .git/submodules/sub/index &&
>   	>sub/file &&
>   	git -C sub add a &&
> -	test_trailing_hash .git/modules/sub/index >hash &&
> +	test_trailing_hash .git/submodules/sub/index >hash &&
>   	test_cmp expect hash &&
>   	git -C sub fsck
>   '
> diff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh
> index 11018f37c7..c18c2efca5 100755
> --- a/t/t2405-worktree-submodule.sh
> +++ b/t/t2405-worktree-submodule.sh
> @@ -62,7 +62,7 @@ test_expect_success 'submodule is checked out after manually adding submodule wo
>   test_expect_success 'checkout --recurse-submodules uses $GIT_DIR for submodules in a linked worktree' '
>   	git -C main worktree add "$base_path/checkout-recurse" --detach  &&
>   	git -C checkout-recurse submodule update --init &&
> -	echo "gitdir: ../../main/.git/worktrees/checkout-recurse/modules/sub" >expect-gitfile &&
> +	echo "gitdir: ../../main/.git/worktrees/checkout-recurse/submodules/sub" >expect-gitfile &&
>   	cat checkout-recurse/sub/.git >actual-gitfile &&
>   	test_cmp expect-gitfile actual-gitfile &&
>   	git -C main/sub rev-parse HEAD >expect-head-main &&
> @@ -73,7 +73,7 @@ test_expect_success 'checkout --recurse-submodules uses $GIT_DIR for submodules
>   	test_cmp expect-head-main actual-head-main
>   '
>   
> -test_expect_success 'core.worktree is removed in $GIT_DIR/modules/<name>/config, not in $GIT_COMMON_DIR/modules/<name>/config' '
> +test_expect_success 'core.worktree is removed in $GIT_DIR/submodules/<name>/config, not in $GIT_COMMON_DIR/submodules/<name>/config' '
>   	echo "../../../sub" >expect-main &&
>   	git -C main/sub config --get core.worktree >actual-main &&
>   	test_cmp expect-main actual-main &&
> @@ -81,14 +81,14 @@ test_expect_success 'core.worktree is removed in $GIT_DIR/modules/<name>/config,
>   	git -C checkout-recurse/sub config --get core.worktree >actual-linked &&
>   	test_cmp expect-linked actual-linked &&
>   	git -C checkout-recurse checkout --recurse-submodules first &&
> -	test_expect_code 1 git -C main/.git/worktrees/checkout-recurse/modules/sub config --get core.worktree >linked-config &&
> +	test_expect_code 1 git -C main/.git/worktrees/checkout-recurse/submodules/sub config --get core.worktree >linked-config &&
>   	test_must_be_empty linked-config &&
>   	git -C main/sub config --get core.worktree >actual-main &&
>   	test_cmp expect-main actual-main
>   '
>   
>   test_expect_success 'unsetting core.worktree does not prevent running commands directly against the submodule repository' '
> -	git -C main/.git/worktrees/checkout-recurse/modules/sub log
> +	git -C main/.git/worktrees/checkout-recurse/submodules/sub log
>   '
>   
>   test_done
> diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh
> index be9140bbaa..bb8751433f 100755
> --- a/t/t2501-cwd-empty.sh
> +++ b/t/t2501-cwd-empty.sh
> @@ -239,7 +239,7 @@ test_submodule_removal () {
>   	test "$path_status" = dir && test_status=test_must_fail
>   
>   	test_when_finished "git reset --hard HEAD~1" &&
> -	test_when_finished "rm -rf .git/modules/my_submodule" &&
> +	test_when_finished "rm -rf .git/submodules/my_submodule" &&
>   
>   	git checkout foo/bar/baz &&
>   
> diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
> index 1f16e6b522..5b8ed57538 100755
> --- a/t/t3600-rm.sh
> +++ b/t/t3600-rm.sh
> @@ -582,7 +582,7 @@ test_expect_success 'rm of a conflicted populated submodule with a .git director
>   	(
>   		cd submod &&
>   		rm .git &&
> -		cp -R ../.git/modules/sub .git &&
> +		cp -R ../.git/submodules/sub .git &&
>   		GIT_WORK_TREE=. git config --unset core.worktree
>   	) &&
>   	test_must_fail git merge conflict2 &&
> @@ -617,9 +617,9 @@ test_expect_success 'rm of a populated submodule with a .git directory migrates
>   	(
>   		cd submod &&
>   		rm .git &&
> -		cp -R ../.git/modules/sub .git &&
> +		cp -R ../.git/submodules/sub .git &&
>   		GIT_WORK_TREE=. git config --unset core.worktree &&
> -		rm -r ../.git/modules/sub
> +		rm -r ../.git/submodules/sub
>   	) &&
>   	git rm submod 2>output.err &&
>   	test_path_is_missing submod &&
> @@ -709,7 +709,7 @@ test_expect_success "rm absorbs submodule's nested .git directory" '
>   	(
>   		cd submod/subsubmod &&
>   		rm .git &&
> -		mv ../../.git/modules/sub/modules/sub .git &&
> +		mv ../../.git/submodules/sub/submodules/sub .git &&
>   		GIT_WORK_TREE=. git config --unset core.worktree
>   	) &&
>   	git rm submod 2>output.err &&
> diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
> index 5e566205ba..b7385bc088 100755
> --- a/t/t5526-fetch-submodules.sh
> +++ b/t/t5526-fetch-submodules.sh
> @@ -1143,7 +1143,7 @@ test_expect_success 'fetch --recurse-submodules updates name-conflicted, unpopul
>   	head1=$(git -C same-name-1/submodule rev-parse HEAD) &&
>   	head2=$(git -C same-name-2/submodule rev-parse HEAD) &&
>   	(
> -		cd same-name-downstream/.git/modules/submodule &&
> +		cd same-name-downstream/.git/submodules/submodule &&
>   		# The submodule has core.worktree pointing to the "git
>   		# rm"-ed directory, overwrite the invalid value. See
>   		# comment in get_fetch_task_from_changed() for more
> diff --git a/t/t5619-clone-local-ambiguous-transport.sh b/t/t5619-clone-local-ambiguous-transport.sh
> index cce62bf78d..cf2d5e7bfb 100755
> --- a/t/t5619-clone-local-ambiguous-transport.sh
> +++ b/t/t5619-clone-local-ambiguous-transport.sh
> @@ -38,7 +38,7 @@ test_expect_success 'setup' '
>   		ln -s "$(cd .. && pwd)/sensitive" repo/objects &&
>   
>   		mkdir -p "$HTTPD_URL/dumb" &&
> -		ln -s "../../../.git/modules/sub/../../../repo/" "$URI" &&
> +		ln -s "../../../.git/submodules/sub/../../../repo/" "$URI" &&
>   
>   		git add . &&
>   		git commit -m "initial commit"
> @@ -57,7 +57,7 @@ test_expect_success 'ambiguous transport does not lead to arbitrary file-inclusi
>   	git clone malicious clone &&
>   	test_must_fail git -C clone submodule update --init 2>err &&
>   
> -	test_path_is_missing clone/.git/modules/sub/objects/secret &&
> +	test_path_is_missing clone/.git/submodules/sub/objects/secret &&
>   	# We would actually expect "transport .file. not allowed" here,
>   	# but due to quirks of the URL detection in Git, we mis-parse
>   	# the absolute path as a bogus URL and die before that step.
> diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
> index 256ccaefb7..56460ae8b5 100755
> --- a/t/t6120-describe.sh
> +++ b/t/t6120-describe.sh
> @@ -357,7 +357,7 @@ test_expect_success 'setup and absorb a submodule' '
>   '
>   
>   test_expect_success 'describe chokes on severely broken submodules' '
> -	mv .git/modules/sub1/ .git/modules/sub_moved &&
> +	mv .git/submodules/sub1/ .git/submodules/sub_moved &&
>   	test_must_fail git describe --dirty
>   '
>   
> @@ -371,7 +371,7 @@ test_expect_success 'describe with --work-tree ignoring a broken submodule' '
>   		cd "$TEST_DIRECTORY" &&
>   		git --git-dir "$TRASH_DIRECTORY/.git" --work-tree "$TRASH_DIRECTORY" describe --broken >"$TRASH_DIRECTORY/out"
>   	) &&
> -	test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" &&
> +	test_when_finished "mv .git/submodules/sub_moved .git/submodules/sub1" &&
>   	grep broken out
>   '
>   
> diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
> index 920479e925..89b06ae3c1 100755
> --- a/t/t7001-mv.sh
> +++ b/t/t7001-mv.sh
> @@ -360,7 +360,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm
>   	(
>   		cd sub &&
>   		rm -f .git &&
> -		cp -R -P -p ../.git/modules/sub .git &&
> +		cp -R -P -p ../.git/submodules/sub .git &&
>   		GIT_WORK_TREE=. git config --unset core.worktree
>   	) &&
>   	mkdir mod &&
> @@ -380,7 +380,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
>   	(
>   		cd sub &&
>   		rm -f .git &&
> -		cp -R -P -p ../.git/modules/sub .git &&
> +		cp -R -P -p ../.git/submodules/sub .git &&
>   		GIT_WORK_TREE=. git config --unset core.worktree
>   	) &&
>   	mkdir mod &&
> diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
> index fd3e7e355e..178c386212 100755
> --- a/t/t7400-submodule-basic.sh
> +++ b/t/t7400-submodule-basic.sh
> @@ -163,7 +163,7 @@ test_expect_success 'submodule add' '
>   		cd addtest &&
>   		git submodule add -q "$submodurl" submod >actual &&
>   		test_must_be_empty actual &&
> -		echo "gitdir: ../.git/modules/submod" >expect &&
> +		echo "gitdir: ../.git/submodules/submod" >expect &&
>   		test_cmp expect submod/.git &&
>   		(
>   			cd submod &&
> @@ -976,21 +976,21 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano
>   			echo "$submodurl/repo" >expect &&
>   			git config remote.origin.url >actual &&
>   			test_cmp expect actual &&
> -			echo "gitdir: ../.git/modules/repo" >expect &&
> +			echo "gitdir: ../.git/submodules/repo" >expect &&
>   			test_cmp expect .git
>   		) &&
>   		rm -rf repo &&
>   		git rm repo &&
>   		git submodule add -q --name repo_new "$submodurl/bare.git" repo >actual &&
>   		test_must_be_empty actual &&
> -		echo "gitdir: ../.git/modules/submod" >expect &&
> +		echo "gitdir: ../.git/submodules/submod" >expect &&
>   		test_cmp expect submod/.git &&
>   		(
>   			cd repo &&
>   			echo "$submodurl/bare.git" >expect &&
>   			git config remote.origin.url >actual &&
>   			test_cmp expect actual &&
> -			echo "gitdir: ../.git/modules/repo_new" >expect &&
> +			echo "gitdir: ../.git/submodules/repo_new" >expect &&
>   			test_cmp expect .git
>   		) &&
>   		echo "repo" >expect &&
> @@ -1045,8 +1045,8 @@ test_expect_success 'recursive relative submodules stay relative' '
>   	(
>   		cd clone2 &&
>   		git submodule update --init --recursive &&
> -		echo "gitdir: ../.git/modules/sub3" >./sub3/.git_expect &&
> -		echo "gitdir: ../../../.git/modules/sub3/modules/dirdir/subsub" >./sub3/dirdir/subsub/.git_expect
> +		echo "gitdir: ../.git/submodules/sub3" >./sub3/.git_expect &&
> +		echo "gitdir: ../../../.git/submodules/sub3/submodules/dirdir/subsub" >./sub3/dirdir/subsub/.git_expect
>   	) &&
>   	test_cmp clone2/sub3/.git_expect clone2/sub3/.git &&
>   	test_cmp clone2/sub3/dirdir/subsub/.git_expect clone2/sub3/dirdir/subsub/.git
> @@ -1108,8 +1108,8 @@ test_expect_success 'submodule deinit should remove the whole submodule section
>   '
>   
>   test_expect_success 'submodule deinit should unset core.worktree' '
> -	test_path_is_file .git/modules/example/config &&
> -	test_must_fail git config -f .git/modules/example/config core.worktree
> +	test_path_is_file .git/submodules/example/config &&
> +	test_must_fail git config -f .git/submodules/example/config core.worktree
>   '
>   
>   test_expect_success 'submodule deinit from subdirectory' '
> @@ -1231,7 +1231,7 @@ test_expect_success 'submodule deinit absorbs .git directory if .git is a direct
>   	(
>   		cd init &&
>   		rm .git &&
> -		mv ../.git/modules/example .git &&
> +		mv ../.git/submodules/example .git &&
>   		GIT_WORK_TREE=. git config --unset core.worktree
>   	) &&
>   	git submodule deinit init &&
> diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
> index 3adab12091..f0c4da1ffa 100755
> --- a/t/t7406-submodule-update.sh
> +++ b/t/t7406-submodule-update.sh
> @@ -864,7 +864,7 @@ test_expect_success 'submodule add places git-dir in superprojects git-dir' '
>   	 (cd deeper/submodule &&
>   	  git log > ../../expected
>   	 ) &&
> -	 (cd .git/modules/deeper/submodule &&
> +	 (cd .git/submodules/deeper/submodule &&
>   	  git log > ../../../../actual
>   	 ) &&
>   	 test_cmp expected actual
> @@ -882,7 +882,7 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir' '
>   	 (cd deeper/submodule &&
>   	  git log > ../../expected
>   	 ) &&
> -	 (cd .git/modules/deeper/submodule &&
> +	 (cd .git/submodules/deeper/submodule &&
>   	  git log > ../../../../actual
>   	 ) &&
>   	 test_cmp expected actual
> @@ -899,7 +899,7 @@ test_expect_success 'submodule add places git-dir in superprojects git-dir recur
>   	  git commit -m "added subsubmodule" &&
>   	  git push origin :
>   	 ) &&
> -	 (cd .git/modules/deeper/submodule/modules/subsubmodule &&
> +	 (cd .git/submodules/deeper/submodule/submodules/subsubmodule &&
>   	  git log > ../../../../../actual
>   	 ) &&
>   	 git add deeper/submodule &&
> @@ -949,7 +949,7 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re
>   	 (cd submodule/subsubmodule &&
>   	  git log > ../../expected
>   	 ) &&
> -	 (cd .git/modules/submodule/modules/subsubmodule &&
> +	 (cd .git/submodules/submodule/submodules/subsubmodule &&
>   	  git log > ../../../../../actual
>   	 ) &&
>   	 test_cmp expected actual
> @@ -1298,7 +1298,7 @@ test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
>   	git init captain &&
>   	(
>   		cd captain &&
> -		git submodule add --name x/y "$hook_repo_path" A/modules/x &&
> +		git submodule add --name x/y "$hook_repo_path" A/submodules/x &&
>   		test_tick &&
>   		git commit -m add-submodule &&
>   
> diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
> index 77b6d0040e..75ba826968 100755
> --- a/t/t7407-submodule-foreach.sh
> +++ b/t/t7407-submodule-foreach.sh
> @@ -368,9 +368,9 @@ test_expect_success 'test "update --recursive" with a flag with spaces' '
>   		git rev-parse --resolve-git-dir nested1/.git &&
>   		git rev-parse --resolve-git-dir nested1/nested2/.git &&
>   		git rev-parse --resolve-git-dir nested1/nested2/nested3/.git &&
> -		test -f .git/modules/nested1/objects/info/alternates &&
> -		test -f .git/modules/nested1/modules/nested2/objects/info/alternates &&
> -		test -f .git/modules/nested1/modules/nested2/modules/nested3/objects/info/alternates
> +		test -f .git/submodules/nested1/objects/info/alternates &&
> +		test -f .git/submodules/nested1/submodules/nested2/objects/info/alternates &&
> +		test -f .git/submodules/nested1/submodules/nested2/submodules/nested3/objects/info/alternates
>   	)
>   '
>   
> diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
> index f860e7bbf4..25f4aec57e 100755
> --- a/t/t7408-submodule-reference.sh
> +++ b/t/t7408-submodule-reference.sh
> @@ -61,7 +61,7 @@ test_expect_success 'submodule add --reference uses alternates' '
>   		git commit -m B-super-added &&
>   		git repack -ad
>   	) &&
> -	test_alternate_is_used super/.git/modules/sub/objects/info/alternates super/sub
> +	test_alternate_is_used super/.git/submodules/sub/objects/info/alternates super/sub
>   '
>   
>   test_expect_success 'submodule add --reference with --dissociate does not use alternates' '
> @@ -71,7 +71,7 @@ test_expect_success 'submodule add --reference with --dissociate does not use al
>   		git commit -m B-super-added &&
>   		git repack -ad
>   	) &&
> -	test_path_is_missing super/.git/modules/sub-dissociate/objects/info/alternates
> +	test_path_is_missing super/.git/submodules/sub-dissociate/objects/info/alternates
>   '
>   
>   test_expect_success 'that reference gets used with add' '
> @@ -94,14 +94,14 @@ test_expect_success 'updating superproject keeps alternates' '
>   	test_when_finished "rm -rf super-clone" &&
>   	git clone super super-clone &&
>   	git -C super-clone submodule update --init --reference ../B &&
> -	test_alternate_is_used super-clone/.git/modules/sub/objects/info/alternates super-clone/sub
> +	test_alternate_is_used super-clone/.git/submodules/sub/objects/info/alternates super-clone/sub
>   '
>   
>   test_expect_success 'updating superproject with --dissociate does not keep alternates' '
>   	test_when_finished "rm -rf super-clone" &&
>   	git clone super super-clone &&
>   	git -C super-clone submodule update --init --reference ../B --dissociate &&
> -	test_path_is_missing super-clone/.git/modules/sub/objects/info/alternates
> +	test_path_is_missing super-clone/.git/submodules/sub/objects/info/alternates
>   '
>   
>   test_expect_success 'submodules use alternates when cloning a superproject' '
> @@ -112,7 +112,7 @@ test_expect_success 'submodules use alternates when cloning a superproject' '
>   		# test superproject has alternates setup correctly
>   		test_alternate_is_used .git/objects/info/alternates . &&
>   		# test submodule has correct setup
> -		test_alternate_is_used .git/modules/sub/objects/info/alternates sub
> +		test_alternate_is_used .git/submodules/sub/objects/info/alternates sub
>   	)
>   '
>   
> @@ -127,7 +127,7 @@ test_expect_success 'missing submodule alternate fails clone and submodule updat
>   		# update of the submodule succeeds
>   		test_must_fail git submodule update --init &&
>   		# and we have no alternates:
> -		test_path_is_missing .git/modules/sub/objects/info/alternates &&
> +		test_path_is_missing .git/submodules/sub/objects/info/alternates &&
>   		test_path_is_missing sub/file1
>   	)
>   '
> @@ -142,7 +142,7 @@ test_expect_success 'ignoring missing submodule alternates passes clone and subm
>   		# update of the submodule succeeds
>   		git submodule update --init &&
>   		# and we have no alternates:
> -		test_path_is_missing .git/modules/sub/objects/info/alternates &&
> +		test_path_is_missing .git/submodules/sub/objects/info/alternates &&
>   		test_path_is_file sub/file1
>   	)
>   '
> @@ -176,18 +176,18 @@ test_expect_success 'nested submodule alternate in works and is actually used' '
>   		# test superproject has alternates setup correctly
>   		test_alternate_is_used .git/objects/info/alternates . &&
>   		# immediate submodule has alternate:
> -		test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub &&
> +		test_alternate_is_used .git/submodules/subwithsub/objects/info/alternates subwithsub &&
>   		# nested submodule also has alternate:
> -		test_alternate_is_used .git/modules/subwithsub/modules/sub/objects/info/alternates subwithsub/sub
> +		test_alternate_is_used .git/submodules/subwithsub/submodules/sub/objects/info/alternates subwithsub/sub
>   	)
>   '
>   
>   check_that_two_of_three_alternates_are_used() {
>   	test_alternate_is_used .git/objects/info/alternates . &&
>   	# immediate submodule has alternate:
> -	test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub &&
> +	test_alternate_is_used .git/submodules/subwithsub/objects/info/alternates subwithsub &&
>   	# but nested submodule has no alternate:
> -	test_path_is_missing .git/modules/subwithsub/modules/sub/objects/info/alternates
> +	test_path_is_missing .git/submodules/subwithsub/submodules/sub/objects/info/alternates
>   }
>   
>   
> diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh
> index 0490499573..dbaca9c69f 100755
> --- a/t/t7412-submodule-absorbgitdirs.sh
> +++ b/t/t7412-submodule-absorbgitdirs.sh
> @@ -29,13 +29,13 @@ test_expect_success 'absorb the git dir' '
>   	cat >expect <<-EOF &&
>   	Migrating git directory of '\''sub1'\'' from
>   	'\''$cwd/sub1/.git'\'' to
> -	'\''$cwd/.git/modules/sub1'\''
> +	'\''$cwd/.git/submodules/sub1'\''
>   	EOF
>   	git submodule absorbgitdirs 2>actual &&
>   	test_cmp expect actual &&
>   	git fsck &&
>   	test -f sub1/.git &&
> -	test -d .git/modules/sub1 &&
> +	test -d .git/submodules/sub1 &&
>   	git status >actual.1 &&
>   	git -C sub1 rev-parse HEAD >actual.2 &&
>   	test_cmp expect.1 actual.1 &&
> @@ -47,7 +47,7 @@ test_expect_success 'absorbing does not fail for deinitialized submodules' '
>   	git submodule deinit --all &&
>   	git submodule absorbgitdirs 2>err &&
>   	test_must_be_empty err &&
> -	test -d .git/modules/sub1 &&
> +	test -d .git/submodules/sub1 &&
>   	test -d sub1 &&
>   	! test -e sub1/.git
>   '
> @@ -68,12 +68,12 @@ test_expect_success 'absorb the git dir in a nested submodule' '
>   	cat >expect <<-EOF &&
>   	Migrating git directory of '\''sub1/nested'\'' from
>   	'\''$cwd/sub1/nested/.git'\'' to
> -	'\''$cwd/.git/modules/sub1/modules/nested'\''
> +	'\''$cwd/.git/submodules/sub1/submodules/nested'\''
>   	EOF
>   	git submodule absorbgitdirs 2>actual &&
>   	test_cmp expect actual &&
>   	test -f sub1/nested/.git &&
> -	test -d .git/modules/sub1/modules/nested &&
> +	test -d .git/submodules/sub1/submodules/nested &&
>   	git status >actual.1 &&
>   	git -C sub1/nested rev-parse HEAD >actual.2 &&
>   	test_cmp expect.1 actual.1 &&
> @@ -84,11 +84,11 @@ test_expect_success 're-setup nested submodule' '
>   	# un-absorb the direct submodule, to test if the nested submodule
>   	# is still correct (needs a rewrite of the gitfile only)
>   	rm -rf sub1/.git &&
> -	mv .git/modules/sub1 sub1/.git &&
> +	mv .git/submodules/sub1 sub1/.git &&
>   	GIT_WORK_TREE=. git -C sub1 config --unset core.worktree &&
>   	# fixup the nested submodule
> -	echo "gitdir: ../.git/modules/nested" >sub1/nested/.git &&
> -	GIT_WORK_TREE=../../../nested git -C sub1/.git/modules/nested config \
> +	echo "gitdir: ../.git/submodules/nested" >sub1/nested/.git &&
> +	GIT_WORK_TREE=../../../nested git -C sub1/.git/submodules/nested config \
>   		core.worktree "../../../nested" &&
>   	# make sure this re-setup is correct
>   	git status --ignore-submodules=none &&
> @@ -105,13 +105,13 @@ test_expect_success 'absorb the git dir in a nested submodule' '
>   	cat >expect <<-EOF &&
>   	Migrating git directory of '\''sub1'\'' from
>   	'\''$cwd/sub1/.git'\'' to
> -	'\''$cwd/.git/modules/sub1'\''
> +	'\''$cwd/.git/submodules/sub1'\''
>   	EOF
>   	git submodule absorbgitdirs 2>actual &&
>   	test_cmp expect actual &&
>   	test -f sub1/.git &&
>   	test -f sub1/nested/.git &&
> -	test -d .git/modules/sub1/modules/nested &&
> +	test -d .git/submodules/sub1/submodules/nested &&
>   	git status >actual.1 &&
>   	git -C sub1/nested rev-parse HEAD >actual.2 &&
>   	test_cmp expect.1 actual.1 &&
> @@ -133,7 +133,7 @@ test_expect_success 'absorb the git dir outside of primary worktree' '
>   	cat >expect <<-EOF &&
>   	Migrating git directory of '\''sub2'\'' from
>   	'\''$cwd/repo-wt/sub2/.git'\'' to
> -	'\''$cwd/repo-bare.git/worktrees/repo-wt/modules/sub2'\''
> +	'\''$cwd/repo-bare.git/worktrees/repo-wt/submodules/sub2'\''
>   	EOF
>   	git -C repo-wt submodule absorbgitdirs 2>actual &&
>   	test_cmp expect actual
> diff --git a/t/t7423-submodule-symlinks.sh b/t/t7423-submodule-symlinks.sh
> index 3d3c7af3ce..a51235136d 100755
> --- a/t/t7423-submodule-symlinks.sh
> +++ b/t/t7423-submodule-symlinks.sh
> @@ -49,19 +49,19 @@ test_expect_success SYMLINKS 'git restore --recurse-submodules must not be confu
>   
>   test_expect_success SYMLINKS 'git restore --recurse-submodules must not migrate git dir of symlinked repo' '
>   	prepare_symlink_to_repo &&
> -	rm -rf .git/modules &&
> +	rm -rf .git/submodules &&
>   	test_must_fail git restore --recurse-submodules a/sm &&
>   	test_path_is_dir a/target/.git &&
> -	test_path_is_missing .git/modules/a/sm &&
> +	test_path_is_missing .git/submodules/a/sm &&
>   	test_path_is_missing a/target/submodule_file
>   '
>   
>   test_expect_success SYMLINKS 'git checkout -f --recurse-submodules must not migrate git dir of symlinked repo when removing submodule' '
>   	prepare_symlink_to_repo &&
> -	rm -rf .git/modules &&
> +	rm -rf .git/submodules &&
>   	test_must_fail git checkout -f --recurse-submodules initial &&
>   	test_path_is_dir a/target/.git &&
> -	test_path_is_missing .git/modules/a/sm
> +	test_path_is_missing .git/submodules/a/sm
>   '
>   
>   test_done
> diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
> index f512eed278..4e2ced3636 100755
> --- a/t/t7450-bad-git-dotfiles.sh
> +++ b/t/t7450-bad-git-dotfiles.sh
> @@ -77,28 +77,28 @@ test_expect_success 'create innocent subrepo' '
>   
>   test_expect_success 'submodule add refuses invalid names' '
>   	test_must_fail \
> -		git submodule add --name ../../modules/evil "$PWD/innocent" evil
> +		git submodule add --name ../../submodules/evil "$PWD/innocent" evil
>   '
>   
>   test_expect_success 'add evil submodule' '
>   	git submodule add "$PWD/innocent" evil &&
>   
> -	mkdir modules &&
> -	cp -r .git/modules/evil modules &&
> -	write_script modules/evil/hooks/post-checkout <<-\EOF &&
> +	mkdir submodules &&
> +	cp -r .git/submodules/evil submodules &&
> +	write_script submodules/evil/hooks/post-checkout <<-\EOF &&
>   	echo >&2 "RUNNING POST CHECKOUT"
>   	EOF
>   
>   	git config -f .gitmodules submodule.evil.update checkout &&
>   	git config -f .gitmodules --rename-section \
> -		submodule.evil submodule.../../modules/evil &&
> -	git add modules &&
> +		submodule.evil submodule.../../submodules/evil &&
> +	git add submodules &&
>   	git commit -am evil
>   '
>   
>   # This step seems like it shouldn't be necessary, since the payload is
>   # contained entirely in the evil submodule. But due to the vagaries of the
> -# submodule code, checking out the evil module will fail unless ".git/modules"
> +# submodule code, checking out the evil module will fail unless ".git/submodules"
>   # exists. Adding another submodule (with a name that sorts before "evil") is an
>   # easy way to make sure this is the case in the victim clone.
>   test_expect_success 'add other submodule' '
> @@ -350,8 +350,8 @@ test_expect_success 'submodule git dir nesting detection must work with parallel
>   	cat err &&
>   	grep -E "(already exists|is inside git dir|not a git repository)" err &&
>   	{
> -		test_path_is_missing .git/modules/hippo/HEAD ||
> -		test_path_is_missing .git/modules/hippo/hooks/HEAD
> +		test_path_is_missing .git/submodules/hippo/HEAD ||
> +		test_path_is_missing .git/submodules/hippo/hooks/HEAD
>   	}
>   '
>   
> @@ -361,10 +361,10 @@ test_expect_success 'checkout -f --recurse-submodules must not use a nested gitd
>   		cd nested_checkout &&
>   		git submodule init &&
>   		git submodule update thing1 &&
> -		mkdir -p .git/modules/hippo/hooks/refs &&
> -		mkdir -p .git/modules/hippo/hooks/objects/info &&
> -		echo "../../../../objects" >.git/modules/hippo/hooks/objects/info/alternates &&
> -		echo "ref: refs/heads/master" >.git/modules/hippo/hooks/HEAD
> +		mkdir -p .git/submodules/hippo/hooks/refs &&
> +		mkdir -p .git/submodules/hippo/hooks/objects/info &&
> +		echo "../../../../objects" >.git/submodules/hippo/hooks/objects/info/alternates &&
> +		echo "ref: refs/heads/master" >.git/submodules/hippo/hooks/HEAD
>   	) &&
>   	test_must_fail git -C nested_checkout checkout -f --recurse-submodules HEAD 2>err &&
>   	cat err &&
> @@ -390,13 +390,13 @@ test_expect_success SYMLINKS,!WINDOWS,!MINGW 'submodule must not checkout into d
>   	git config unset -f repo/.gitmodules submodule.sub.path &&
>   	printf "\tpath = \"sub\r\"\n" >>repo/.gitmodules &&
>   
> -	git config unset -f repo/.git/modules/sub/config core.worktree &&
> +	git config unset -f repo/.git/submodules/sub/config core.worktree &&
>   	{
>   		printf "[core]\n" &&
>   		printf "\tworktree = \"../../../sub\r\"\n"
> -	} >>repo/.git/modules/sub/config &&
> +	} >>repo/.git/submodules/sub/config &&
>   
> -	ln -s .git/modules/sub/hooks repo/sub &&
> +	ln -s .git/submodules/sub/hooks repo/sub &&
>   	git -C repo add -A &&
>   	git -C repo commit -m submodule &&
>   
> diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
> index 409cd0cd12..ded482fdf2 100755
> --- a/t/t7527-builtin-fsmonitor.sh
> +++ b/t/t7527-builtin-fsmonitor.sh
> @@ -866,7 +866,7 @@ test_expect_success 'submodule always visited' '
>   '
>   
>   # If a submodule has a `sub/.git/` directory (rather than a file
> -# pointing to the super's `.git/modules/sub`) and `core.fsmonitor`
> +# pointing to the super's `.git/submodules/sub`) and `core.fsmonitor`
>   # turned on in the submodule and the daemon is not yet started in
>   # the submodule, and someone does a `git submodule absorbgitdirs`
>   # in the super, Git will recursively invoke `git submodule--helper`
> @@ -895,7 +895,7 @@ test_expect_success "submodule absorbgitdirs implicitly starts daemon" '
>   	cat >expect <<-EOF &&
>   	Migrating git directory of '\''dir_1/dir_2/sub'\'' from
>   	'\''$cwd/dir_1/dir_2/sub/.git'\'' to
> -	'\''$cwd/.git/modules/dir_1/dir_2/sub'\''
> +	'\''$cwd/.git/submodules/dir_1/dir_2/sub'\''
>   	EOF
>   	GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
>   		git -C super submodule absorbgitdirs >out 2>actual &&
Previous: Adrian RatiuNext: Adrian Ratiu
Message 6 of 219 in “Encode submodule gitdir names to avoid conflicts”
  1. 0/9 Encode submodule gitdir names to avoid conflictsAdrian Ratiu, Aug 16, 2025
  2. 1/9 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Aug 16, 2025
  3. Josh SteadmonAug 20, 2025
  4. Adrian RatiuAug 21, 2025
  5. 2/9 submodule: create new gitdirs under submodules pathAdrian Ratiu, Aug 16, 2025
  6. Phillip WoodSep 8, 2025
  7. Adrian RatiuSep 8, 2025
  8. Phillip WoodSep 9, 2025
  9. Adrian RatiuSep 9, 2025
  10. 3/9 submodule: add gitdir path config overrideAdrian Ratiu, Aug 16, 2025
  11. Josh SteadmonAug 20, 2025
  12. Adrian RatiuAug 21, 2025
  13. Josh SteadmonAug 20, 2025
  14. Adrian RatiuAug 21, 2025
  15. Josh SteadmonAug 20, 2025
  16. Adrian RatiuAug 21, 2025
  17. Phillip WoodSep 8, 2025
  18. Adrian RatiuSep 9, 2025
  19. 4/9 t: submodules: add basic mixed gitdir path testsAdrian Ratiu, Aug 16, 2025
  20. Josh SteadmonAug 20, 2025
  21. Junio C HamanoSep 2, 2025
  22. 5/9 strbuf: bring back is_rfc3986_unreservedAdrian Ratiu, Aug 16, 2025
  23. Ben KnobleAug 16, 2025
  24. Adrian RatiuAug 21, 2025
  25. 6/9 submodule: encode gitdir paths to avoid conflictsAdrian Ratiu, Aug 16, 2025
  26. Jeff KingAug 20, 2025
  27. Adrian RatiuAug 21, 2025
  28. 7/9 submodule: remove validate_submodule_git_dir()Adrian Ratiu, Aug 16, 2025
  29. Phillip WoodSep 8, 2025
  30. 8/9 t: move nested gitdir tests to proper locationAdrian Ratiu, Aug 16, 2025
  31. 9/9 t: add gitdir encoding testsAdrian Ratiu, Aug 16, 2025
  32. Junio C HamanoAug 18, 2025
  33. Adrian RatiuAug 21, 2025
  34. Adrian RatiuAug 17, 2025
  35. 00/10 Encode submodule gitdir names to avoid conflictsAdrian Ratiu, Sep 8, 2025
  36. 01/10 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Sep 8, 2025
  37. Kristoffer HaugsbakkSep 30, 2025
  38. 03/10 submodule: add gitdir path config overrideAdrian Ratiu, Sep 8, 2025
  39. Patrick SteinhardtSep 9, 2025
  40. Adrian RatiuSep 9, 2025
  41. 02/10 submodule: create new gitdirs under submodules pathAdrian Ratiu, Sep 8, 2025
  42. Patrick SteinhardtSep 9, 2025
  43. Adrian RatiuSep 9, 2025
  44. 05/10 strbuf: bring back is_rfc3986_unreservedAdrian Ratiu, Sep 8, 2025
  45. 04/10 t7425: add basic mixed submodule gitdir path testsAdrian Ratiu, Sep 8, 2025
  46. 06/10 submodule: encode gitdir paths to avoid conflictsAdrian Ratiu, Sep 8, 2025
  47. SZEDER GáborSep 10, 2025
  48. Adrian RatiuSep 10, 2025
  49. Kristoffer HaugsbakkSep 10, 2025
  50. Kristoffer HaugsbakkSep 30, 2025
  51. 07/10 submodule: error out if gitdir name is too longAdrian Ratiu, Sep 8, 2025
  52. Jeff KingSep 8, 2025
  53. Adrian RatiuSep 8, 2025
  54. Kristoffer HaugsbakkSep 30, 2025
  55. 08/10 submodule: remove validate_submodule_git_dir()Adrian Ratiu, Sep 8, 2025
  56. Kristoffer HaugsbakkSep 30, 2025
  57. Adrian RatiuOct 3, 2025
  58. 09/10 t7450: move nested gitdir tests to t7425Adrian Ratiu, Sep 8, 2025
  59. 10/10 t7425: add gitdir encoding testsAdrian Ratiu, Sep 8, 2025
  60. 0/5 Encode submodule gitdir names to avoid conflictsAdrian Ratiu, Oct 6, 2025
  61. 2/5 submodule: add gitdir path config overrideAdrian Ratiu, Oct 6, 2025
  62. Junio C HamanoOct 6, 2025
  63. Junio C HamanoOct 7, 2025
  64. Patrick SteinhardtOct 21, 2025
  65. Adrian RatiuOct 21, 2025
  66. Patrick SteinhardtOct 21, 2025
  67. Adrian RatiuOct 21, 2025
  68. 1/5 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Oct 6, 2025
  69. Junio C HamanoOct 6, 2025
  70. Adrian RatiuOct 7, 2025
  71. 4/5 submodule: encode gitdir paths to avoid conflictsAdrian Ratiu, Oct 6, 2025
  72. Junio C HamanoOct 6, 2025
  73. Adrian RatiuOct 7, 2025
  74. Junio C HamanoOct 7, 2025
  75. Adrian RatiuOct 7, 2025
  76. Junio C HamanoOct 7, 2025
  77. 5/5 submodule: error out if gitdir name is too longAdrian Ratiu, Oct 6, 2025
  78. Junio C HamanoOct 6, 2025
  79. Adrian RatiuOct 7, 2025
  80. Junio C HamanoOct 7, 2025
  81. Patrick SteinhardtOct 21, 2025
  82. Adrian RatiuOct 21, 2025
  83. 3/5 strbuf: bring back is_rfc3986_unreservedAdrian Ratiu, Oct 6, 2025
  84. Junio C HamanoOct 6, 2025
  85. Junio C HamanoOct 6, 2025
  86. Adrian RatiuOct 7, 2025
  87. Patrick SteinhardtOct 21, 2025
  88. Junio C HamanoOct 6, 2025
  89. Adrian RatiuOct 7, 2025
  90. Junio C HamanoOct 7, 2025
  91. Adrian RatiuOct 7, 2025
  92. Junio C HamanoOct 7, 2025
  93. Junio C HamanoOct 7, 2025
  94. Adrian RatiuOct 7, 2025
  95. 0/4 Encode submodule gitdir names to avoid conflictsAdrian Ratiu, Nov 7, 2025
  96. 3/4 submodule: add extension to encode gitdir pathsAdrian Ratiu, Nov 7, 2025
  97. 2/4 builtin/credential-store: move is_rfc3986_unreserved to url.[ch]Adrian Ratiu, Nov 7, 2025
  98. 1/4 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Nov 7, 2025
  99. 4/4 submodule: fix case-folding gitdir filesystem colisionsAdrian Ratiu, Nov 7, 2025
  100. Aaron SchrabNov 8, 2025
  101. Adrian RatiuNov 10, 2025
  102. Aaron SchrabNov 10, 2025
  103. Adrian RatiuNov 10, 2025
  104. Junio C HamanoNov 10, 2025
  105. Adrian RatiuNov 10, 2025
  106. Junio C HamanoNov 10, 2025
  107. Adrian RatiuNov 11, 2025
  108. Adrian RatiuNov 12, 2025
  109. Josh SteadmonNov 14, 2025
  110. Adrian RatiuNov 17, 2025
  111. 0/7 Encode submodule gitdir names to avoid conflictsAdrian Ratiu, Nov 19, 2025
  112. 2/7 builtin/credential-store: move is_rfc3986_unreserved to url.[ch]Adrian Ratiu, Nov 19, 2025
  113. Patrick SteinhardtDec 5, 2025
  114. Adrian RatiuDec 5, 2025
  115. 1/7 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Nov 19, 2025
  116. 3/7 submodule: always validate gitdirs inside submodule_name_to_gitdirAdrian Ratiu, Nov 19, 2025
  117. Patrick SteinhardtDec 5, 2025
  118. Adrian RatiuDec 5, 2025
  119. 4/7 submodule: add extension to encode gitdir pathsAdrian Ratiu, Nov 19, 2025
  120. Patrick SteinhardtDec 5, 2025
  121. Adrian RatiuDec 5, 2025
  122. Junio C HamanoDec 5, 2025
  123. Patrick SteinhardtDec 6, 2025
  124. Junio C HamanoDec 6, 2025
  125. Adrian RatiuDec 8, 2025
  126. Patrick SteinhardtDec 8, 2025
  127. Adrian RatiuDec 8, 2025
  128. Adrian RatiuDec 8, 2025
  129. 5/7 submodule: fix case-folding gitdir filesystem colisionsAdrian Ratiu, Nov 19, 2025
  130. 6/7 submodule: use hashed name for gitdirAdrian Ratiu, Nov 19, 2025
  131. 7/7 meson/Makefile: allow setting submodule encoding at build timeAdrian Ratiu, Nov 19, 2025
  132. Patrick SteinhardtDec 5, 2025
  133. Adrian RatiuDec 5, 2025
  134. Junio C HamanoDec 5, 2025
  135. Patrick SteinhardtDec 6, 2025
  136. Junio C HamanoDec 6, 2025
  137. Adrian RatiuDec 8, 2025
  138. Adrian RatiuDec 8, 2025
  139. 00/10 Add submodulePathConfig extension and gitdir encodingAdrian Ratiu, Dec 13, 2025
  140. 02/10 submodule: always validate gitdirs inside submodule_name_to_gitdirAdrian Ratiu, Dec 13, 2025
  141. Patrick SteinhardtDec 16, 2025
  142. 03/10 builtin/submodule--helper: add gitdir commandAdrian Ratiu, Dec 13, 2025
  143. 01/10 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Dec 13, 2025
  144. 04/10 submodule: introduce extensions.submodulePathConfigAdrian Ratiu, Dec 13, 2025
  145. Patrick SteinhardtDec 16, 2025
  146. Adrian RatiuDec 16, 2025
  147. Josh SteadmonDec 16, 2025
  148. Adrian RatiuDec 17, 2025
  149. 05/10 submodule: allow runtime enabling extensions.submodulePathConfigAdrian Ratiu, Dec 13, 2025
  150. Patrick SteinhardtDec 16, 2025
  151. Adrian RatiuDec 16, 2025
  152. 06/10 submodule--helper: add gitdir migration commandAdrian Ratiu, Dec 13, 2025
  153. Patrick SteinhardtDec 16, 2025
  154. Adrian RatiuDec 16, 2025
  155. 07/10 builtin/credential-store: move is_rfc3986_unreserved to url.[ch]Adrian Ratiu, Dec 13, 2025
  156. 08/10 submodule--helper: fix filesystem collisions by encoding gitdir pathsAdrian Ratiu, Dec 13, 2025
  157. 09/10 submodule: fix case-folding gitdir filesystem collisionsAdrian Ratiu, Dec 13, 2025
  158. 10/10 submodule: hash the submodule name for the gitdir pathAdrian Ratiu, Dec 13, 2025
  159. Ben KnobleDec 13, 2025
  160. Adrian RatiuDec 15, 2025
  161. Junio C HamanoDec 16, 2025
  162. Ben KnobleDec 18, 2025
  163. Josh SteadmonDec 16, 2025
  164. Adrian RatiuDec 17, 2025
  165. 00/11 Add submodulePathConfig extension and gitdir encodingAdrian Ratiu, Dec 20, 2025
  166. 01/11 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Dec 20, 2025
  167. 02/11 submodule: always validate gitdirs inside submodule_name_to_gitdirAdrian Ratiu, Dec 20, 2025
  168. 03/11 builtin/submodule--helper: add gitdir commandAdrian Ratiu, Dec 20, 2025
  169. 04/11 submodule: introduce extensions.submodulePathConfigAdrian Ratiu, Dec 20, 2025
  170. Junio C HamanoDec 21, 2025
  171. Adrian RatiuDec 23, 2025
  172. Patrick SteinhardtJan 6, 2026
  173. Adrian RatiuJan 7, 2026
  174. 05/11 submodule: allow runtime enabling extensions.submodulePathConfigAdrian Ratiu, Dec 20, 2025
  175. Patrick SteinhardtJan 6, 2026
  176. Adrian RatiuJan 7, 2026
  177. 06/11 submodule--helper: add gitdir migration commandAdrian Ratiu, Dec 20, 2025
  178. Patrick SteinhardtJan 6, 2026
  179. Adrian RatiuJan 7, 2026
  180. 07/11 builtin/credential-store: move is_rfc3986_unreserved to url.[ch]Adrian Ratiu, Dec 20, 2025
  181. 08/11 submodule--helper: fix filesystem collisions by encoding gitdir pathsAdrian Ratiu, Dec 20, 2025
  182. 09/11 submodule: fix case-folding gitdir filesystem collisionsAdrian Ratiu, Dec 20, 2025
  183. 10/11 submodule: hash the submodule name for the gitdir pathAdrian Ratiu, Dec 20, 2025
  184. 11/11 submodule: detect conflicts with existing gitdir configsAdrian Ratiu, Dec 20, 2025
  185. Patrick SteinhardtJan 6, 2026
  186. Junio C HamanoDec 21, 2025
  187. Patrick SteinhardtJan 6, 2026
  188. 00/11 Add submodulePathConfig extension and gitdir encodingAdrian Ratiu, Jan 7, 2026
  189. 01/11 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Jan 7, 2026
  190. 02/11 submodule: always validate gitdirs inside submodule_name_to_gitdirAdrian Ratiu, Jan 7, 2026
  191. 03/11 builtin/submodule--helper: add gitdir commandAdrian Ratiu, Jan 7, 2026
  192. 04/11 submodule: introduce extensions.submodulePathConfigAdrian Ratiu, Jan 7, 2026
  193. 05/11 submodule: allow runtime enabling extensions.submodulePathConfigAdrian Ratiu, Jan 7, 2026
  194. Patrick SteinhardtJan 8, 2026
  195. 06/11 submodule--helper: add gitdir migration commandAdrian Ratiu, Jan 7, 2026
  196. Patrick SteinhardtJan 8, 2026
  197. 07/11 builtin/credential-store: move is_rfc3986_unreserved to url.[ch]Adrian Ratiu, Jan 7, 2026
  198. 08/11 submodule--helper: fix filesystem collisions by encoding gitdir pathsAdrian Ratiu, Jan 7, 2026
  199. 09/11 submodule: fix case-folding gitdir filesystem collisionsAdrian Ratiu, Jan 7, 2026
  200. 10/11 submodule: hash the submodule name for the gitdir pathAdrian Ratiu, Jan 7, 2026
  201. 11/11 submodule: detect conflicts with existing gitdir configsAdrian Ratiu, Jan 7, 2026
  202. Patrick SteinhardtJan 8, 2026
  203. Josh SteadmonJan 8, 2026
  204. Junio C HamanoJan 11, 2026
  205. 00/11 Add submodulePathConfig extension and gitdir encodingAdrian Ratiu, Jan 12, 2026
  206. 02/11 submodule: always validate gitdirs inside submodule_name_to_gitdirAdrian Ratiu, Jan 12, 2026
  207. 01/11 submodule--helper: use submodule_name_to_gitdir in add_submoduleAdrian Ratiu, Jan 12, 2026
  208. 03/11 builtin/submodule--helper: add gitdir commandAdrian Ratiu, Jan 12, 2026
  209. 04/11 submodule: introduce extensions.submodulePathConfigAdrian Ratiu, Jan 12, 2026
  210. 05/11 submodule: allow runtime enabling extensions.submodulePathConfigAdrian Ratiu, Jan 12, 2026
  211. 06/11 submodule--helper: add gitdir migration commandAdrian Ratiu, Jan 12, 2026
  212. 07/11 builtin/credential-store: move is_rfc3986_unreserved to url.[ch]Adrian Ratiu, Jan 12, 2026
  213. 08/11 submodule--helper: fix filesystem collisions by encoding gitdir pathsAdrian Ratiu, Jan 12, 2026
  214. 09/11 submodule: fix case-folding gitdir filesystem collisionsAdrian Ratiu, Jan 12, 2026
  215. 10/11 submodule: hash the submodule name for the gitdir pathAdrian Ratiu, Jan 12, 2026
  216. 11/11 submodule: detect conflicts with existing gitdir configsAdrian Ratiu, Jan 12, 2026
  217. Junio C HamanoJan 12, 2026
  218. Adrian RatiuJan 12, 2026
  219. Patrick SteinhardtJan 13, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.