From: "Yoichi NAKAYAMA via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>,
Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>
Subject: [PATCH 1/3] submodule: make submodule.<name>.gitdir relative for worktrees
Date: Thu, 12 Nov 2026 12:03:03 +0000 [thread overview]
Message-ID: <f9adc6c85c4b098a0df8cf18783d1969a68fc048.1794484985.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2306.git.1794484985.gitgitgadget@gmail.com>
From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>
For a linked worktree, always keep `submodule.<name>.gitdir` relative
to ensure it continues to work when the worktree is moved along with
the main worktree. (This benefit also requires
worktree.useRelativePaths to be set to true.)
Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>
---
builtin/submodule--helper.c | 68 +++++++++++++---------
t/t7425-submodule-gitdir-path-extension.sh | 19 ++++++
2 files changed, 61 insertions(+), 26 deletions(-)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 461b682d0d..56d7720e93 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -37,6 +37,7 @@
#include "wildmatch.h"
#include "strbuf.h"
#include "url.h"
+#include "worktree.h"
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
@@ -507,50 +508,60 @@ static int validate_and_set_submodule_gitdir(struct strbuf *gitdir_path,
static void create_default_gitdir_config(const char *submodule_name)
{
+ struct worktree *wt = get_current_worktree(the_repository);
+ const char *modules_dir;
+ struct strbuf tmp = STRBUF_INIT;
struct strbuf gitdir_path = STRBUF_INIT;
struct git_hash_ctx ctx;
char hex_name_hash[GIT_MAX_HEXSZ + 1], header[128];
unsigned char raw_name_hash[GIT_MAX_RAWSZ];
int header_len;
- /* Case 1: try the plain module name */
- repo_git_path_append(the_repository, &gitdir_path, "modules/%s", submodule_name);
- if (!validate_and_set_submodule_gitdir(&gitdir_path, submodule_name)) {
- strbuf_release(&gitdir_path);
- return;
+ modules_dir = worktree_git_path(wt, "modules/");
+ if (is_absolute_path(modules_dir)) {
+ modules_dir = relative_path(modules_dir, wt->path, &tmp);
}
+ /* Case 1: try the plain module name */
+ strbuf_addstr(&gitdir_path, modules_dir);
+ strbuf_addstr(&gitdir_path, submodule_name);
+ if (!validate_and_set_submodule_gitdir(&gitdir_path,
+ submodule_name))
+ goto cleanup;
+
/* Case 2.1: Try URI-safe (RFC3986) encoding first, this fixes nested gitdirs */
strbuf_reset(&gitdir_path);
- repo_git_path_append(the_repository, &gitdir_path, "modules/");
+ strbuf_addstr(&gitdir_path, modules_dir);
strbuf_addstr_urlencode(&gitdir_path, submodule_name, is_rfc3986_unreserved);
- if (!validate_and_set_submodule_gitdir(&gitdir_path, submodule_name)) {
- strbuf_release(&gitdir_path);
- return;
- }
+ if (!validate_and_set_submodule_gitdir(&gitdir_path,
+ submodule_name))
+ goto cleanup;
/* Case 2.2: Try extended uppercase URI (RFC3986) encoding, to fix case-folding */
strbuf_reset(&gitdir_path);
- repo_git_path_append(the_repository, &gitdir_path, "modules/");
+ strbuf_addstr(&gitdir_path, modules_dir);
strbuf_addstr_urlencode(&gitdir_path, submodule_name, is_casefolding_rfc3986_unreserved);
- if (!validate_and_set_submodule_gitdir(&gitdir_path, submodule_name))
- return;
+ if (!validate_and_set_submodule_gitdir(&gitdir_path,
+ submodule_name))
+ goto cleanup;
/* Case 2.3: Try some derived gitdir names, see if one sticks */
for (char c = '0'; c <= '9'; c++) {
strbuf_reset(&gitdir_path);
- repo_git_path_append(the_repository, &gitdir_path, "modules/");
+ strbuf_addstr(&gitdir_path, modules_dir);
strbuf_addstr_urlencode(&gitdir_path, submodule_name, is_rfc3986_unreserved);
strbuf_addch(&gitdir_path, c);
- if (!validate_and_set_submodule_gitdir(&gitdir_path, submodule_name))
- return;
+ if (!validate_and_set_submodule_gitdir(&gitdir_path,
+ submodule_name))
+ goto cleanup;
strbuf_reset(&gitdir_path);
- repo_git_path_append(the_repository, &gitdir_path, "modules/");
+ strbuf_addstr(&gitdir_path, modules_dir);
strbuf_addstr_urlencode(&gitdir_path, submodule_name, is_casefolding_rfc3986_unreserved);
strbuf_addch(&gitdir_path, c);
- if (!validate_and_set_submodule_gitdir(&gitdir_path, submodule_name))
- return;
+ if (!validate_and_set_submodule_gitdir(&gitdir_path,
+ submodule_name))
+ goto cleanup;
}
/* Case 2.4: If all the above failed, try a hash of the name as a last resort */
@@ -563,17 +574,22 @@ static void create_default_gitdir_config(const char *submodule_name)
git_hash_final(raw_name_hash, &ctx);
hash_to_hex_algop_r(hex_name_hash, raw_name_hash, the_hash_algo);
strbuf_reset(&gitdir_path);
- repo_git_path_append(the_repository, &gitdir_path, "modules/%s", hex_name_hash);
- if (!validate_and_set_submodule_gitdir(&gitdir_path, submodule_name)) {
- strbuf_release(&gitdir_path);
- return;
- }
+ strbuf_addstr(&gitdir_path, modules_dir);
+ strbuf_addstr(&gitdir_path, hex_name_hash);
+ if (!validate_and_set_submodule_gitdir(&gitdir_path,
+ submodule_name))
+ goto cleanup;
/* Case 3: nothing worked, error out */
die(_("failed to set a valid default config for 'submodule.%s.gitdir'. "
"Please ensure it is set, for example by running something like: "
- "'git config submodule.%s.gitdir .git/modules/%s'"),
- submodule_name, submodule_name, submodule_name);
+ "'git config submodule.%s.gitdir %s%s'"),
+ submodule_name, submodule_name, modules_dir, submodule_name);
+
+cleanup:
+ strbuf_release(&gitdir_path);
+ strbuf_release(&tmp);
+ free_worktree(wt);
}
static void init_submodule(const char *path, const char *prefix,
diff --git a/t/t7425-submodule-gitdir-path-extension.sh b/t/t7425-submodule-gitdir-path-extension.sh
index ea86ecf7ee..02123b8468 100755
--- a/t/t7425-submodule-gitdir-path-extension.sh
+++ b/t/t7425-submodule-gitdir-path-extension.sh
@@ -279,6 +279,25 @@ test_expect_success '`git clone --recurse-submodules` respects init.defaultSubmo
)
'
+test_expect_success 'git submodule init make gitdir relative' '
+ test_when_finished "rm -rf repo-clone-init-wt" &&
+ git clone upstream repo-clone-init-wt &&
+ (
+ cd repo-clone-init-wt &&
+ git config core.repositoryformatversion 1 &&
+ git config extensions.submodulePathConfig true &&
+ git worktree add wt &&
+ cd wt &&
+ git submodule init &&
+ git config submodule.sub1.gitdir >actual &&
+ echo "../.git/worktrees/wt/modules/sub1" >expect &&
+ test_cmp expect actual &&
+ git config submodule.sub2.gitdir >actual &&
+ echo "../.git/worktrees/wt/modules/sub2" >expect &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'submodule--helper migrates legacy modules' '
(
cd upstream &&
--
gitgitgadget
next prev parent reply other threads:[~2026-11-12 12:03 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 12:03 [PATCH 0/3] submodule: support worktree-specific configuration Yoichi NAKAYAMA via GitGitGadget
2026-11-12 12:03 ` Yoichi NAKAYAMA via GitGitGadget [this message]
2026-11-12 12:03 ` [PATCH 2/3] submodule: store settings in worktree config when enabled Yoichi NAKAYAMA via GitGitGadget
2026-11-12 12:03 ` [PATCH 3/3] submodule init: copy submodule.<name>.gitdir to config.worktree Yoichi NAKAYAMA via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f9adc6c85c4b098a0df8cf18783d1969a68fc048.1794484985.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=yoichi.nakayama@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.