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

[PATCH v4 0/4] Encode submodule gitdir names to avoid conflicts

From
Adrian Ratiu <adrian.ratiu@collabora.com>
Date
Nov 7, 2025, 15:05 UTC
Message-ID
<20251107150547.3272180-1-adrian.ratiu@collabora.com>
In-Reply-To
<20250816213642.3517822-1-adrian.ratiu@collabora.com>
Hello everyone,

For those new to this series, we are adding an extension to encode submodule gitdir paths to avoid filesystem conflicts.

v4 continues with the simplifications started in v3, based on reviewer feedback (many thanks to everyone, especially to Junio and Patrick).

The biggest design change in v4 is that config submodule.<name>.gitdir becomes mandatory when extensions.submoduleEncoding is enabled, so instead of being just an optional override like in previous iterations, in v4 it's the centerpiece of design, the single point of truth from which submodule gitdir paths are located.

This allows users to not care about the specific encoding being used and also allows git to improveme the encoding implementation over time: everyone just gets the submodule gitdirs from the config after the extension is enabled.

Another important change is that we do not encode everything by default when the extension is enabled: submodule_name_to_gitdir() validates multiple candidates and picks the best one and encoding only when required.

As always this is based on latest master branch, I've merged into next/seen for any conflicts, pushed to GitHub [1] and ran the CI with all tests passing [2].

I also added a rangediff between v3 and v4, which might help some reviewers, though it's rather big because we changed the config/encoding design like I mentioned above so it might be easier to give it a full review.

Changes between v3 -> v4:
* Replaced bmwill@google.com -> bwilliams.eng@gmail.com (Kristoffer)
* Made the gitdir config the authoritative source of truth for all submodule
  git dirs when the extension is enabled (Junio, Patrick)
* Replaced the "encode everything by default" design with a stepwise/retry
  validation in submodule_name_to_gitdir() to pick the best canditate (Junio)
* Moved is_rfc3986_unreserved() to url.[ch] instead of strbuf (Junio)
* Fixed a parallel job execution bug I introduced with the extension in v3 (Adrian)
* Improved lib-verify-submodule-gitdir-path.sh to handle relative/abs paths (Adrian)
* Fixed submodule.<name>.gitdir documentation vs code mismatch (Junio)
* Defined the config together with the extension by squashing commits (Patrick)
* Removed the A -> _a, B -> _b custom encoding in favor of a simplified percent
  encodin which is only done when case-folding to allow uppercase chars (Junio)
* Dropped patch with pathconf wrapper, as it's not required for basic encoding,
  it can be added back if we implement sharding (Junio, Peff, Patrick)
* Used repo_config_get_string_tmp to avoid a char* free call (Patrick)
* Added comment to document is_rfc3986_unreserved() (Patrick)
* Fixed heredoc body indentation (Patrick)
* Fixed trivial doc markup conflict with upstream commit (Adrian)
* Reworded multiple commits for clarity (Junio)
Please let me know if you have any questions or other feedback.

Thank you, Adrian

[1] https://github.com/10ne1/git/tree/dev/aratiu/encoding-v4 [2] https://github.com/10ne1/git/actions/runs/19170390360

Range-diff against v3:
1:  b2e317a8f6 ! 1:  7d34507692 submodule--helper: use submodule_name_to_gitdir in add_submodule
    @@ Commit message
         submodule--helper: use submodule_name_to_gitdir in add_submodule
     
         While testing submodule gitdir path encoding, I noticed submodule--helper
    -    is still using a hardcoded name-based path leading to test failures, so
    -    convert it to the common helper function introduced by commit ce125d431a
    -    (submodule: extract path to submodule gitdir func, 2021-09-15)  and used
    -    in other locations across the source tree.
    +    is still using a hardcoded modules gitdir path leading to test failures.
    +
    +    Call the submodule_name_to_gitdir() helper instead, which was invented
    +    exactly for this purpose and is already used by all the other locations
    +    which work on gitdirs.
    +
    +    Also narrow the scope of the submod_gitdir_path variable which is not
    +    used anymore in the updated "else" branch.
     
         Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
     
2:  17838ad13f < -:  ---------- submodule: add gitdir path config override
3:  4fc7020f26 < -:  ---------- strbuf: bring back is_rfc3986_unreserved
-:  ---------- > 2:  1e609bdd1a builtin/credential-store: move is_rfc3986_unreserved to url.[ch]
4:  dc6d5069ff ! 3:  c51fd58669 submodule: encode gitdir paths to avoid conflicts
    @@ Metadata
     Author: Adrian Ratiu <adrian.ratiu@collabora.com>
     
      ## Commit message ##
    -    submodule: encode gitdir paths to avoid conflicts
    +    submodule: add extension to encode gitdir paths
     
    -    This adds a new submoduleEncoding extension which encodes gitdir names
    -    to avoid collisions due to nested gitdirs or case insensitive filesystems.
    +    Add a submoduleEncoding extension which fixes filesystem collisions by
    +    encoding gitdir paths. At a high level, this implements a mechanism to
    +    encode -> validate -> retry until a working gitdir path is found.
     
    -    A custom encoding can become unnecessarily complex, while url-encoding is
    -    relatively well-known, however it needs some extending to support case
    -    insensitive filesystems, hence why A is encoded as _a, B as _b and so on.
    +    Credit goes to Junio for coming up with this design: encoding is only
    +    applied when necessary, e.g. uppercase characters are encoded only on
    +    case-folding filesystems and only if a real conflict is detected.
     
    -    Unfortunately encoding A -> _a (...) is not enough to fix the reserved
    -    Windows file names (e.g. COM1) because worktrees still use names like COM1
    -    even if the gitdirs paths are encoded, so future work is needed to fully
    -    address Windows reserved names.
    +    To make this work, we rely on the submodule.<name>.gitdir config as the
    +    single source of truth for gitidir paths: the config is always set when
    +    the extension is enabled. Users who care about gitdir paths are expected
    +    to get/set the config and not the underlying encoding implementation.
     
    -    For now url-encoding is the only option, however in the future we may
    -    add alternatives (other encodings, hashes or even hash_name).
    +    This commit adds the basic encoding logic which addresses nested gitdirs.
    +    The next commit fixes case-folding, the next commit fixes names longer
    +    than NAME_MAX. The idea is the encoding can be improved over time in a
    +    way which is transparent to users.
     
    +    Suggested-by: Junio C Hamano <gitster@pobox.com>
         Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
         Suggested-by: Patrick Steinhardt <ps@pks.im>
    +    Based-on-patch-by: Brandon Williams <bwilliams.eng@gmail.com>
         Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
     
      ## Documentation/config/extensions.adoc ##
    -@@ Documentation/config/extensions.adoc: relativeWorktrees::
    +@@ Documentation/config/extensions.adoc: relativeWorktrees:::
      	repaired with either the `--relative-paths` option or with the
      	`worktree.useRelativePaths` config set to `true`.
      
    -+submoduleEncoding::
    ++submoduleEncoding:::
     +	If enabled, submodule gitdir paths are encoded to avoid filesystem
    -+	conflicts due to nested gitdirs or case insensitivity. For now, only
    -+	url-encoding (rfc3986) is available, with a small addition to encode
    -+	uppercase to lowercase letters (`A  -> _a`, `B -> _b` and so on).
    -+	Other encoding or hashing methods may be added in the future.
    -+	Any preexisting non-encoded submodule gitdirs are used as-is, to
    -+	ease migration and reduce risk of gitdirs not being recognized.
    -+
    - worktreeConfig::
    ++	conflicts due to nested gitdirs, case insensitivity or other issues
    ++	When enabled, the submodule.<name>.gitdir config is always set for
    ++	all submodulesand is the single point of authority for gitdir paths.
    ++
    + worktreeConfig:::
      	If enabled, then worktrees will load config settings from the
      	`$GIT_DIR/config.worktree` file in addition to the
     
      ## Documentation/config/submodule.adoc ##
     @@ Documentation/config/submodule.adoc: submodule.<name>.active::
    - submodule.<name>.gitdir::
    - 	This option sets the gitdir path for submodule <name>, allowing users
    - 	to override the default path or change the default path name encoding.
    -+	Submodule gitdir encoding is enabled via `extensions.submoduleEncoding`
    -+	(see linkgit:git-config[1]). This config works both with the extension
    -+	enabled or disabled.
    + 	submodule.active config option. See linkgit:gitsubmodules[7] for
    + 	details.
      
    ++submodule.<name>.gitdir::
    ++	This option sets the gitdir path for submodule <name>, allowing users to
    ++	override the default path. Only works when `extensions.submoduleEncoding`
    ++	is enabled, otherwise does nothing. See linkgit:git-config[1] for details.
    ++
      submodule.active::
      	A repeated field which contains a pathspec used to match against a
    + 	submodule's path to determine if the submodule is of interest to git
    +
    + ## builtin/submodule--helper.c ##
    +@@ builtin/submodule--helper.c: static int module_summary(int argc, const char **argv, const char *prefix,
    + 	return ret;
    + }
    + 
    ++static int module_gitdir(int argc, const char **argv, const char *prefix UNUSED,
    ++			 struct repository *repo)
    ++{
    ++	struct strbuf gitdir = STRBUF_INIT;
    ++
    ++	if (argc != 2)
    ++		usage(_("git submodule--helper gitdir <name>"));
    ++
    ++	submodule_name_to_gitdir(&gitdir, repo, argv[1]);
    ++
    ++	printf("%s\n", gitdir.buf);
    ++
    ++	strbuf_release(&gitdir);
    ++	return 0;
    ++}
    ++
    + struct sync_cb {
    + 	const char *prefix;
    + 	const char *super_prefix;
    +@@ builtin/submodule--helper.c: int cmd_submodule__helper(int argc,
    + 		NULL
    + 	};
    + 	struct option options[] = {
    ++		OPT_SUBCOMMAND("gitdir", &fn, module_gitdir),
    + 		OPT_SUBCOMMAND("clone", &fn, module_clone),
    + 		OPT_SUBCOMMAND("add", &fn, module_add),
    + 		OPT_SUBCOMMAND("update", &fn, module_update),
    +
    + ## repository.c ##
    +@@ repository.c: int repo_init(struct repository *repo,
    + 	repo->repository_format_worktree_config = format.worktree_config;
    + 	repo->repository_format_relative_worktrees = format.relative_worktrees;
    + 	repo->repository_format_precious_objects = format.precious_objects;
    ++	repo->repository_format_submodule_encoding = format.submodule_encoding;
    + 
    + 	/* take ownership of format.partial_clone */
    + 	repo->repository_format_partial_clone = format.partial_clone;
     
      ## repository.h ##
     @@ repository.h: struct repository {
    @@ setup.h: struct repository_format {
      	int compat_hash_algo;
     
      ## submodule.c ##
    -@@ submodule.c: int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
    - 	char *p;
    +@@
    + #include "commit-reach.h"
    + #include "read-cache-ll.h"
    + #include "setup.h"
    ++#include "url.h"
    + 
    + static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
    + static int initialized_fetch_ref_tips;
    +@@ submodule.c: int submodule_move_head(const char *path, const char *super_prefix,
    + 	return ret;
    + }
    + 
    ++/*
    ++ * Find the last submodule name in the gitdir path (modules can be nested).
    ++ * Returns a pointer into `path` to the beginning of the name or NULL if not found.
    ++ */
    ++static char *find_last_submodule_name(char *git_dir_path)
    ++{
    ++	const char *modules_marker = "/modules/";
    ++	char *p = git_dir_path;
    ++	char *last = NULL;
    ++
    ++	while ((p = strstr(p, modules_marker))) {
    ++		last = p + strlen(modules_marker);
    ++		p++;
    ++	}
    ++
    ++	return last;
    ++}
    ++
    + int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
    + {
    + 	size_t len = strlen(git_dir), suffix_len = strlen(submodule_name);
    +-	char *p;
    ++	char *p = git_dir + len - suffix_len;
    ++	bool suffixes_match = !strcmp(p, submodule_name);
      	int ret = 0;
      
    -+	/*
    -+	 * Skip these checks when extensions.submoduleEncoding is enabled because
    -+	 * it fixes the nesting issues and the suffixes will not match by design.
    -+	 */
    -+	if (the_repository->repository_format_submodule_encoding)
    +-	if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
    +-	    strcmp(p, submodule_name))
    +-		BUG("submodule name '%s' not a suffix of git dir '%s'",
    +-		    submodule_name, git_dir);
    +-
    + 	/*
    + 	 * We prevent the contents of sibling submodules' git directories to
    + 	 * clash.
    +@@ submodule.c: int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
    + 	 * but the latter directory is already designated to contain the hooks
    + 	 * of the former.
    + 	 */
    +-	for (; *p; p++) {
    ++	for (; *p && suffixes_match; p++) {
    + 		if (is_dir_sep(*p)) {
    + 			char c = *p;
    + 
    +@@ submodule.c: int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
    + 		}
    + 	}
    + 
    ++	/* tests after this check are only for encoded names, when the extension is enabled */
    ++	if (!the_repository->repository_format_submodule_encoding)
     +		return 0;
     +
    - 	if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
    - 	    strcmp(p, submodule_name))
    - 		BUG("submodule name '%s' not a suffix of git dir '%s'",
    ++	/* Prevent the use of '/' in names */
    ++	p = find_last_submodule_name(git_dir);
    ++	if (p && strchr(p, '/') != NULL)
    ++		return error("submodule gitdir name '%s' contains unexpected '/'", p);
    ++
    + 	return 0;
    + }
    + 
     @@ submodule.c: int submodule_to_gitdir(struct repository *repo,
      	return ret;
      }
      
    -+static void strbuf_addstr_case_encode(struct strbuf *dst, const char *src)
    ++static int validate_and_set_submodule_gitdir(struct strbuf *gitdir_path,
    ++					     const char *submodule_name)
     +{
    -+	for (; *src; src++) {
    -+		unsigned char c = *src;
    -+		if (c >= 'A' && c <= 'Z') {
    -+			strbuf_addch(dst, '_');
    -+			strbuf_addch(dst, c - 'A' + 'a');
    -+		} else {
    -+			strbuf_addch(dst, c);
    -+		}
    -+	}
    ++	char *key;
    ++
    ++	if (validate_submodule_git_dir(gitdir_path->buf, submodule_name))
    ++		return -1;
    ++
    ++	key = xstrfmt("submodule.%s.gitdir", submodule_name);
    ++	repo_config_set_gently(the_repository, key, gitdir_path->buf);
    ++	FREE_AND_NULL(key);
    ++
    ++	return 0;
    ++
     +}
     +
      void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
      			      const char *submodule_name)
      {
    --	/*
    ++	const char *gitdir;
    ++	char *key;
    ++
    ++	repo_git_path_append(r, buf, "modules/");
    ++	strbuf_addstr(buf, submodule_name);
    ++
    ++	/* If extensions.submoduleEncoding is disabled, use the plain path set above */
    ++	if (!r->repository_format_submodule_encoding)
    ++		return;
    ++
    ++	/* Extension is enabled: use the gitdir config if it exists */
    ++	key = xstrfmt("submodule.%s.gitdir", submodule_name);
    ++	if (!repo_config_get_string_tmp(r, key, &gitdir)) {
    ++		strbuf_reset(buf);
    ++		strbuf_addstr(buf, gitdir);
    ++		FREE_AND_NULL(key);
    ++		return;
    ++	}
    ++	FREE_AND_NULL(key);
    ++
    + 	/*
     -	 * NEEDSWORK: The current way of mapping a submodule's name to
     -	 * its location in .git/modules/ has problems with some naming
     -	 * schemes. For example, if a submodule is named "foo" and
    @@ submodule.c: int submodule_to_gitdir(struct repository *repo,
     -	 * submodule.<name>.gitdir config in .gitmodules that repo
     -	 * administrators can explicitly set. Nothing has been decided,
     -	 * so for now, just append the name at the end of the path.
    --	 */
    - 	char *gitdir_path, *key;
    - 
    - 	/* Allow config override. */
    -@@ submodule.c: void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
    - 
    - 	repo_git_path_append(r, buf, "modules/");
    - 	strbuf_addstr(buf, submodule_name);
    ++	 * The gitdir config does not exist, even though the extension is enabled.
    ++	 * Therefore we are in one of the following cases:
    + 	 */
     +
    -+	/* Existing legacy non-encoded names are used as-is */
    -+	if (is_git_directory(buf->buf))
    ++	/* Case 1: legacy migration of valid plain submodule names */
    ++	if (!validate_and_set_submodule_gitdir(buf, submodule_name))
     +		return;
     +
    -+	if (the_repository->repository_format_submodule_encoding) {
    -+		struct strbuf tmp = STRBUF_INIT;
    ++	/* Case 2: Try URI-safe (RFC3986) encoding first, this fixes nested gitdirs */
    ++	strbuf_reset(buf);
    + 	repo_git_path_append(r, buf, "modules/");
    +-	strbuf_addstr(buf, submodule_name);
    ++	strbuf_addstr_urlencode(buf, submodule_name, is_rfc3986_unreserved);
    ++	if (!validate_and_set_submodule_gitdir(buf, submodule_name))
    ++		return;
     +
    -+		strbuf_reset(buf);
    -+		repo_git_path_append(r, buf, "modules/");
    ++	/* Case 3: error out */
    ++	die(_("Cannot construct a valid gitdir path for submodule '%s': "
    ++	      "please set a unique git config for 'submodule.%s.gitdir'."),
    ++	    submodule_name, submodule_name);
    + }
    +
    + ## t/lib-verify-submodule-gitdir-path.sh (new) ##
    +@@
    ++# Helper to verify if repo $1 contains a submodule named $2 with gitdir path $3
     +
    -+		strbuf_addstr_urlencode(&tmp, submodule_name, is_rfc3986_unreserved);
    -+		strbuf_addstr_case_encode(buf, tmp.buf);
    ++# This does not check filesystem existence. That is done in submodule.c via the
    ++# submodule_name_to_gitdir() API which this helper ends up calling. The gitdirs
    ++# might or might not exist (e.g. when adding a new submodule), so this only
    ++# checks the expected configuration path, which might be overridden by the user.
     +
    -+		strbuf_release(&tmp);
    -+	}
    - }
    ++verify_submodule_gitdir_path() {
    ++	repo="$1" &&
    ++	name="$2" &&
    ++	path="$3" &&
    ++	(
    ++		cd "$repo" &&
    ++		# Compute expected absolute path
    ++		expected="$(git rev-parse --git-common-dir)/$path" &&
    ++		expected="$(test-tool path-utils real_path "$expected")" &&
    ++		# Compute actual absolute path
    ++		actual="$(git submodule--helper gitdir "$name")" &&
    ++		actual="$(test-tool path-utils real_path "$actual")" &&
    ++		echo "$expected" >expect &&
    ++		echo "$actual" >actual &&
    ++		test_cmp expect actual
    ++	)
    ++}
     
      ## t/meson.build ##
     @@ t/meson.build: integration_tests = [
    @@ t/t7425-submodule-encoding.sh (new)
     +
     +test_expect_success 'verify submodule name is properly encoded' '
     +	verify_submodule_gitdir_path main legacy modules/legacy &&
    -+	verify_submodule_gitdir_path main "New Sub" modules/_new%20_sub
    ++	verify_submodule_gitdir_path main "New Sub" "modules/New Sub"
     +'
     +
     +test_expect_success 'clone from repo with both legacy and new-style submodules' '
    @@ t/t7425-submodule-encoding.sh (new)
     +		cd cloned-encoding &&
     +
     +		test_path_is_dir .git/modules/legacy &&
    -+		test_path_is_dir .git/modules/_new%20_sub &&
    ++		test_path_is_dir ".git/modules/New Sub" &&
     +
     +		git submodule status >list &&
     +		test_grep "$legacy_rev legacy" list &&
    @@ t/t7425-submodule-encoding.sh (new)
     +		git submodule update --init --recursive &&
     +
     +		test_path_is_dir .git/modules/legacy &&
    -+		test_path_is_dir .git/modules/_new%20_sub &&
    ++		test_path_is_dir ".git/modules/New Sub" &&
     +
     +		# Verify both submodules are at the expected commits
     +		git submodule status >list &&
    @@ t/t7425-submodule-encoding.sh (new)
     +'
     +
     +test_done
    +
    + ## t/t9902-completion.sh ##
    +@@ t/t9902-completion.sh: test_expect_success 'git config set - variable name - __git_compute_second_level
    + 	submodule.sub.fetchRecurseSubmodules Z
    + 	submodule.sub.ignore Z
    + 	submodule.sub.active Z
    ++	submodule.sub.gitdir Z
    + 	EOF
    + '
    + 
5:  775cf131bf < -:  ---------- submodule: error out if gitdir name is too long
-:  ---------- > 4:  01a5b10d5a submodule: fix case-folding gitdir filesystem colisions
Adrian Ratiu (4):
  submodule--helper: use submodule_name_to_gitdir in add_submodule
  builtin/credential-store: move is_rfc3986_unreserved to url.[ch]
  submodule: add extension to encode gitdir paths
  submodule: fix case-folding gitdir filesystem colisions
 Documentation/config/extensions.adoc  |   6 +
 Documentation/config/submodule.adoc   |   5 +
 builtin/credential-store.c            |   7 +-
 builtin/submodule--helper.c           |  30 ++++-
 repository.c                          |   1 +
 repository.h                          |   1 +
 setup.c                               |   7 ++
 setup.h                               |   1 +
 submodule.c                           | 155 ++++++++++++++++++++-----
 t/lib-verify-submodule-gitdir-path.sh |  24 ++++
 t/meson.build                         |   1 +
 t/t7425-submodule-encoding.sh         | 161 ++++++++++++++++++++++++++
 t/t9902-completion.sh                 |   1 +
 url.c                                 |  23 ++++
 url.h                                 |   3 +
 15 files changed, 387 insertions(+), 39 deletions(-)
 create mode 100644 t/lib-verify-submodule-gitdir-path.sh
 create mode 100755 t/t7425-submodule-encoding.sh
-- 
2.51.0
Previous: Adrian RatiuNext: Adrian Ratiu
Message 95 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.