Skip to content

Commit 807fa7f

Browse files
committed
Merge branch 'ar/submodule-gitdir-tweak' into seen
Avoid local submodule repository directory paths overlapping with each other by encoding submodule names before using them as path components. * ar/submodule-gitdir-tweak: submodule: error out if gitdir name is too long submodule: encode gitdir paths to avoid conflicts strbuf: bring back is_rfc3986_unreserved submodule: add gitdir path config override submodule--helper: use submodule_name_to_gitdir in add_submodule
2 parents 158d602 + 1c1c416 commit 807fa7f

20 files changed

+340
-32
lines changed

Documentation/config/extensions.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ relativeWorktrees:::
7373
repaired with either the `--relative-paths` option or with the
7474
`worktree.useRelativePaths` config set to `true`.
7575
76+
submoduleEncoding:::
77+
If enabled, submodule gitdir paths are encoded to avoid filesystem
78+
conflicts due to nested gitdirs or case insensitivity. For now, only
79+
url-encoding (rfc3986) is available, with a small addition to encode
80+
uppercase to lowercase letters (`A -> _a`, `B -> _b` and so on).
81+
Other encoding or hashing methods may be added in the future.
82+
Any preexisting non-encoded submodule gitdirs are used as-is, to
83+
ease migration and reduce risk of gitdirs not being recognized.
84+
7685
worktreeConfig:::
7786
If enabled, then worktrees will load config settings from the
7887
`$GIT_DIR/config.worktree` file in addition to the

Documentation/config/submodule.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ submodule.<name>.active::
5252
submodule.active config option. See linkgit:gitsubmodules[7] for
5353
details.
5454

55+
submodule.<name>.gitdir::
56+
This option sets the gitdir path for submodule <name>, allowing users
57+
to override the default path or change the default path name encoding.
58+
Submodule gitdir encoding is enabled via `extensions.submoduleEncoding`
59+
(see linkgit:git-config[1]). This config works both with the extension
60+
enabled or disabled.
61+
5562
submodule.active::
5663
A repeated field which contains a pathspec used to match against a
5764
submodule's path to determine if the submodule is of interest to git

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,6 +2282,11 @@ ifndef HAVE_PLATFORM_PROCINFO
22822282
COMPAT_OBJS += compat/stub/procinfo.o
22832283
endif
22842284

2285+
ifdef NO_PATHCONF
2286+
COMPAT_CFLAGS += -DNO_PATHCONF
2287+
COMPAT_OBJS += compat/pathconf.o
2288+
endif
2289+
22852290
ifdef RUNTIME_PREFIX
22862291

22872292
ifdef HAVE_BSD_KERN_PROC_SYSCTL

builtin/credential-store.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,6 @@ static void rewrite_credential_file(const char *fn, struct credential *c,
7676
die_errno("unable to write credential store");
7777
}
7878

79-
static int is_rfc3986_unreserved(char ch)
80-
{
81-
return isalnum(ch) ||
82-
ch == '-' || ch == '_' || ch == '.' || ch == '~';
83-
}
84-
8579
static int is_rfc3986_reserved_or_unreserved(char ch)
8680
{
8781
if (is_rfc3986_unreserved(ch))

builtin/submodule--helper.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,22 @@ static int module_summary(int argc, const char **argv, const char *prefix,
12041204
return ret;
12051205
}
12061206

1207+
static int module_gitdir(int argc, const char **argv, const char *prefix UNUSED,
1208+
struct repository *repo)
1209+
{
1210+
struct strbuf gitdir = STRBUF_INIT;
1211+
1212+
if (argc != 2)
1213+
usage(_("git submodule--helper gitdir <name>"));
1214+
1215+
submodule_name_to_gitdir(&gitdir, repo, argv[1]);
1216+
1217+
printf("%s\n", gitdir.buf);
1218+
1219+
strbuf_release(&gitdir);
1220+
return 0;
1221+
}
1222+
12071223
struct sync_cb {
12081224
const char *prefix;
12091225
const char *super_prefix;
@@ -3183,13 +3199,13 @@ static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path)
31833199

31843200
static int add_submodule(const struct add_data *add_data)
31853201
{
3186-
char *submod_gitdir_path;
31873202
struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
31883203
struct string_list reference = STRING_LIST_INIT_NODUP;
31893204
int ret = -1;
31903205

31913206
/* perhaps the path already exists and is already a git repo, else clone it */
31923207
if (is_directory(add_data->sm_path)) {
3208+
char *submod_gitdir_path;
31933209
struct strbuf sm_path = STRBUF_INIT;
31943210
strbuf_addstr(&sm_path, add_data->sm_path);
31953211
submod_gitdir_path = xstrfmt("%s/.git", add_data->sm_path);
@@ -3203,10 +3219,11 @@ static int add_submodule(const struct add_data *add_data)
32033219
free(submod_gitdir_path);
32043220
} else {
32053221
struct child_process cp = CHILD_PROCESS_INIT;
3222+
struct strbuf submod_gitdir = STRBUF_INIT;
32063223

3207-
submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name);
3224+
submodule_name_to_gitdir(&submod_gitdir, the_repository, add_data->sm_name);
32083225

3209-
if (is_directory(submod_gitdir_path)) {
3226+
if (is_directory(submod_gitdir.buf)) {
32103227
if (!add_data->force) {
32113228
struct strbuf msg = STRBUF_INIT;
32123229
char *die_msg;
@@ -3215,8 +3232,8 @@ static int add_submodule(const struct add_data *add_data)
32153232
"locally with remote(s):\n"),
32163233
add_data->sm_name);
32173234

3218-
append_fetch_remotes(&msg, submod_gitdir_path);
3219-
free(submod_gitdir_path);
3235+
append_fetch_remotes(&msg, submod_gitdir.buf);
3236+
strbuf_release(&submod_gitdir);
32203237

32213238
strbuf_addf(&msg, _("If you want to reuse this local git "
32223239
"directory instead of cloning again from\n"
@@ -3234,7 +3251,7 @@ static int add_submodule(const struct add_data *add_data)
32343251
"submodule '%s'\n"), add_data->sm_name);
32353252
}
32363253
}
3237-
free(submod_gitdir_path);
3254+
strbuf_release(&submod_gitdir);
32383255

32393256
clone_data.prefix = add_data->prefix;
32403257
clone_data.path = add_data->sm_path;
@@ -3586,6 +3603,7 @@ int cmd_submodule__helper(int argc,
35863603
NULL
35873604
};
35883605
struct option options[] = {
3606+
OPT_SUBCOMMAND("gitdir", &fn, module_gitdir),
35893607
OPT_SUBCOMMAND("clone", &fn, module_clone),
35903608
OPT_SUBCOMMAND("add", &fn, module_add),
35913609
OPT_SUBCOMMAND("update", &fn, module_update),

compat/pathconf.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include "git-compat-util.h"
2+
3+
/*
4+
* Minimal stub for platforms without pathconf() (e.g. Windows),
5+
* to fall back to NAME_MAX from limits.h or compat/posix.h.
6+
*/
7+
long git_pathconf(const char *path UNUSED, int name UNUSED)
8+
{
9+
return -1;
10+
}

compat/posix.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ char *gitdirname(char *);
250250
#define NAME_MAX 255
251251
#endif
252252

253+
#ifdef NO_PATHCONF
254+
#ifndef _PC_NAME_MAX
255+
#define _PC_NAME_MAX 1 /* dummy value, only used for git_pathconf */
256+
#endif
257+
#define pathconf(a,b) git_pathconf(a,b)
258+
long git_pathconf(const char *path, int name);
259+
#endif
260+
253261
typedef uintmax_t timestamp_t;
254262
#define PRItime PRIuMAX
255263
#define parse_timestamp strtoumax

config.mak.uname

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ ifeq ($(uname_S),Windows)
473473
NEEDS_CRYPTO_WITH_SSL = YesPlease
474474
NO_LIBGEN_H = YesPlease
475475
NO_POLL = YesPlease
476+
NO_PATHCONF = YesPlease
476477
NO_SYMLINK_HEAD = YesPlease
477478
NO_IPV6 = YesPlease
478479
NO_SETENV = YesPlease
@@ -688,6 +689,7 @@ ifeq ($(uname_S),MINGW)
688689
NEEDS_CRYPTO_WITH_SSL = YesPlease
689690
NO_LIBGEN_H = YesPlease
690691
NO_POLL = YesPlease
692+
NO_PATHCONF = YesPlease
691693
NO_SYMLINK_HEAD = YesPlease
692694
NO_SETENV = YesPlease
693695
NO_STRCASESTR = YesPlease

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,7 @@ checkfuncs = {
14051405
'initgroups' : [],
14061406
'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
14071407
'pread' : ['pread.c'],
1408+
'pathconf' : ['pathconf.c'],
14081409
}
14091410

14101411
if host_machine.system() == 'windows'

repository.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ struct repository {
158158
int repository_format_worktree_config;
159159
int repository_format_relative_worktrees;
160160
int repository_format_precious_objects;
161+
int repository_format_submodule_encoding;
161162

162163
/* Indicate if a repository has a different 'commondir' from 'gitdir' */
163164
unsigned different_commondir:1;

0 commit comments

Comments
 (0)