Skip to content

Commit c23322c

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 65fb2f7 + 1c1c416 commit c23322c

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
@@ -2268,6 +2268,11 @@ ifndef HAVE_PLATFORM_PROCINFO
22682268
COMPAT_OBJS += compat/stub/procinfo.o
22692269
endif
22702270

2271+
ifdef NO_PATHCONF
2272+
COMPAT_CFLAGS += -DNO_PATHCONF
2273+
COMPAT_OBJS += compat/pathconf.o
2274+
endif
2275+
22712276
ifdef RUNTIME_PREFIX
22722277

22732278
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
@@ -1208,6 +1208,22 @@ static int module_summary(int argc, const char **argv, const char *prefix,
12081208
return ret;
12091209
}
12101210

1211+
static int module_gitdir(int argc, const char **argv, const char *prefix UNUSED,
1212+
struct repository *repo)
1213+
{
1214+
struct strbuf gitdir = STRBUF_INIT;
1215+
1216+
if (argc != 2)
1217+
usage(_("git submodule--helper gitdir <name>"));
1218+
1219+
submodule_name_to_gitdir(&gitdir, repo, argv[1]);
1220+
1221+
printf("%s\n", gitdir.buf);
1222+
1223+
strbuf_release(&gitdir);
1224+
return 0;
1225+
}
1226+
12111227
struct sync_cb {
12121228
const char *prefix;
12131229
const char *super_prefix;
@@ -3187,13 +3203,13 @@ static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path)
31873203

31883204
static int add_submodule(const struct add_data *add_data)
31893205
{
3190-
char *submod_gitdir_path;
31913206
struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
31923207
struct string_list reference = STRING_LIST_INIT_NODUP;
31933208
int ret = -1;
31943209

31953210
/* perhaps the path already exists and is already a git repo, else clone it */
31963211
if (is_directory(add_data->sm_path)) {
3212+
char *submod_gitdir_path;
31973213
struct strbuf sm_path = STRBUF_INIT;
31983214
strbuf_addstr(&sm_path, add_data->sm_path);
31993215
submod_gitdir_path = xstrfmt("%s/.git", add_data->sm_path);
@@ -3207,10 +3223,11 @@ static int add_submodule(const struct add_data *add_data)
32073223
free(submod_gitdir_path);
32083224
} else {
32093225
struct child_process cp = CHILD_PROCESS_INIT;
3226+
struct strbuf submod_gitdir = STRBUF_INIT;
32103227

3211-
submod_gitdir_path = xstrfmt(".git/modules/%s", add_data->sm_name);
3228+
submodule_name_to_gitdir(&submod_gitdir, the_repository, add_data->sm_name);
32123229

3213-
if (is_directory(submod_gitdir_path)) {
3230+
if (is_directory(submod_gitdir.buf)) {
32143231
if (!add_data->force) {
32153232
struct strbuf msg = STRBUF_INIT;
32163233
char *die_msg;
@@ -3219,8 +3236,8 @@ static int add_submodule(const struct add_data *add_data)
32193236
"locally with remote(s):\n"),
32203237
add_data->sm_name);
32213238

3222-
append_fetch_remotes(&msg, submod_gitdir_path);
3223-
free(submod_gitdir_path);
3239+
append_fetch_remotes(&msg, submod_gitdir.buf);
3240+
strbuf_release(&submod_gitdir);
32243241

32253242
strbuf_addf(&msg, _("If you want to reuse this local git "
32263243
"directory instead of cloning again from\n"
@@ -3238,7 +3255,7 @@ static int add_submodule(const struct add_data *add_data)
32383255
"submodule '%s'\n"), add_data->sm_name);
32393256
}
32403257
}
3241-
free(submod_gitdir_path);
3258+
strbuf_release(&submod_gitdir);
32423259

32433260
clone_data.prefix = add_data->prefix;
32443261
clone_data.path = add_data->sm_path;
@@ -3590,6 +3607,7 @@ int cmd_submodule__helper(int argc,
35903607
NULL
35913608
};
35923609
struct option options[] = {
3610+
OPT_SUBCOMMAND("gitdir", &fn, module_gitdir),
35933611
OPT_SUBCOMMAND("clone", &fn, module_clone),
35943612
OPT_SUBCOMMAND("add", &fn, module_add),
35953613
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
@@ -1396,6 +1396,7 @@ checkfuncs = {
13961396
'initgroups' : [],
13971397
'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
13981398
'pread' : ['pread.c'],
1399+
'pathconf' : ['pathconf.c'],
13991400
}
14001401

14011402
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)