[PATCH v5 6/7] submodule: use hashed name for gitdir
- From
Adrian Ratiu <adrian.ratiu@collabora.com>
- Date
- Nov 19, 2025, 21:10 UTC
- Message-ID
- <20251119211030.2008441-7-adrian.ratiu@collabora.com>
- In-Reply-To
- <20251119211030.2008441-1-adrian.ratiu@collabora.com>
If none of the previous steps work and we reach case 2.4, try to hash the submodule name and see if that can be a valid gitdir before giving up and throwing an error.
This is a "last resort" type of measure to avoid conflicts since it loses the gitdir human readability. Itis not such a big deal because users are now supposed to use the submodule.<name>.gitdir config as the single source of truth for gitdir paths.
This logic will be reached in very rare cases, as can be seen in the test we added.
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> --- submodule.c | 20 +++++++++++- t/t7425-submodule-encoding.sh | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-)
diff --git a/submodule.c b/submodule.c index b3f74f7e3c..2c0df96d55 100644 --- a/submodule.c +++ b/submodule.c @@ -2660,8 +2660,12 @@ static int validate_and_set_submodule_gitdir(struct strbuf *gitdir_path, void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r, const char *submodule_name) { + unsigned char raw_name_hash[GIT_MAX_RAWSZ]; + char hex_name_hash[GIT_MAX_HEXSZ + 1]; + struct git_hash_ctx ctx; const char *gitdir; - char *key; + char *key, header[128]; + int header_len; repo_git_path_append(r, buf, "modules/"); strbuf_addstr(buf, submodule_name); @@ -2732,6 +2736,20 @@ void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r, return; } + /* Case 2.4: If all the above failed, try a hash of the name as a last resort */ + header_len = snprintf(header, sizeof(header), "blob %zu", strlen(submodule_name)); + the_hash_algo->init_fn(&ctx); + the_hash_algo->update_fn(&ctx, header, header_len); + the_hash_algo->update_fn(&ctx, "\0", 1); + the_hash_algo->update_fn(&ctx, submodule_name, strlen(submodule_name)); + the_hash_algo->final_fn(raw_name_hash, &ctx); + hash_to_hex_algop_r(hex_name_hash, raw_name_hash, the_hash_algo); + strbuf_reset(buf); + repo_git_path_append(r, buf, "modules/"); + strbuf_addstr(buf, hex_name_hash); + if (!validate_and_set_submodule_gitdir(buf, submodule_name)) + return; + /* Case 3: Nothing worked: error out */ die(_("Cannot construct a valid gitdir path for submodule '%s': " "please set a unique git config for 'submodule.%s.gitdir'."), diff --git a/t/t7425-submodule-encoding.sh b/t/t7425-submodule-encoding.sh index 093238939a..10703b34c8 100755 --- a/t/t7425-submodule-encoding.sh +++ b/t/t7425-submodule-encoding.sh @@ -196,4 +196,63 @@ test_expect_success CASE_INSENSITIVE_FS 'verify case-folding conflicts are corre verify_submodule_gitdir_path cloned-folding "fooBar" "modules/fooBar0" ' +test_expect_success CASE_INSENSITIVE_FS 'verify hashing conflict resolution as a last resort' ' + git clone -c extensions.submoduleEncoding=true main cloned-hash && + ( + cd cloned-hash && + + # conflict: add all submodule conflicting variants until we reach the + # final hashing conflict resolution for submodule "foo" + git submodule add ../new-sub "foo" && + git submodule add ../new-sub "foo0" && + git submodule add ../new-sub "foo1" && + git submodule add ../new-sub "foo2" && + git submodule add ../new-sub "foo3" && + git submodule add ../new-sub "foo4" && + git submodule add ../new-sub "foo5" && + git submodule add ../new-sub "foo6" && + git submodule add ../new-sub "foo7" && + git submodule add ../new-sub "foo8" && + git submodule add ../new-sub "foo9" && + git submodule add ../new-sub "%46oo" && + git submodule add ../new-sub "%46oo0" && + git submodule add ../new-sub "%46oo1" && + git submodule add ../new-sub "%46oo2" && + git submodule add ../new-sub "%46oo3" && + git submodule add ../new-sub "%46oo4" && + git submodule add ../new-sub "%46oo5" && + git submodule add ../new-sub "%46oo6" && + git submodule add ../new-sub "%46oo7" && + git submodule add ../new-sub "%46oo8" && + git submodule add ../new-sub "%46oo9" && + test_commit add-foo-variants && + git submodule add ../new-sub "Foo" && + test_commit add-uppercase-foo + ) && + verify_submodule_gitdir_path cloned-hash "foo" "modules/foo" && + verify_submodule_gitdir_path cloned-hash "foo0" "modules/foo0" && + verify_submodule_gitdir_path cloned-hash "foo1" "modules/foo1" && + verify_submodule_gitdir_path cloned-hash "foo2" "modules/foo2" && + verify_submodule_gitdir_path cloned-hash "foo3" "modules/foo3" && + verify_submodule_gitdir_path cloned-hash "foo4" "modules/foo4" && + verify_submodule_gitdir_path cloned-hash "foo5" "modules/foo5" && + verify_submodule_gitdir_path cloned-hash "foo6" "modules/foo6" && + verify_submodule_gitdir_path cloned-hash "foo7" "modules/foo7" && + verify_submodule_gitdir_path cloned-hash "foo8" "modules/foo8" && + verify_submodule_gitdir_path cloned-hash "foo9" "modules/foo9" && + verify_submodule_gitdir_path cloned-hash "%46oo" "modules/%46oo" && + verify_submodule_gitdir_path cloned-hash "%46oo0" "modules/%46oo0" && + verify_submodule_gitdir_path cloned-hash "%46oo1" "modules/%46oo1" && + verify_submodule_gitdir_path cloned-hash "%46oo2" "modules/%46oo2" && + verify_submodule_gitdir_path cloned-hash "%46oo3" "modules/%46oo3" && + verify_submodule_gitdir_path cloned-hash "%46oo4" "modules/%46oo4" && + verify_submodule_gitdir_path cloned-hash "%46oo5" "modules/%46oo5" && + verify_submodule_gitdir_path cloned-hash "%46oo6" "modules/%46oo6" && + verify_submodule_gitdir_path cloned-hash "%46oo7" "modules/%46oo7" && + verify_submodule_gitdir_path cloned-hash "%46oo8" "modules/%46oo8" && + verify_submodule_gitdir_path cloned-hash "%46oo9" "modules/%46oo9" && + hash=$(printf "Foo" | git hash-object --stdin) && + verify_submodule_gitdir_path cloned-hash "Foo" "modules/${hash}" +' + test_done
-- 2.51.0