Skip to content

Commit abb4824

Browse files
committed
Merge branch 'ao/submodule-wo-gitmodules-checked-out'
The submodule support has been updated to read from the blob at HEAD:.gitmodules when the .gitmodules file is missing from the working tree. * ao/submodule-wo-gitmodules-checked-out: t/helper: add test-submodule-nested-repo-config submodule: support reading .gitmodules when it's not in the working tree submodule: add a helper to check if it is safe to write to .gitmodules t7506: clean up .gitmodules properly before setting up new scenario submodule: use the 'submodule--helper config' command submodule--helper: add a new 'config' subcommand t7411: be nicer to future tests and really clean things up t7411: merge tests 5 and 6 submodule: factor out a config_set_in_gitmodules_file_gently function submodule: add a print_config_from_gitmodules() helper
2 parents 504bdc5 + 2b1257e commit abb4824

16 files changed

+454
-32
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,7 @@ TEST_BUILTINS_OBJS += test-sigchain.o
751751
TEST_BUILTINS_OBJS += test-strcmp-offset.o
752752
TEST_BUILTINS_OBJS += test-string-list.o
753753
TEST_BUILTINS_OBJS += test-submodule-config.o
754+
TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
754755
TEST_BUILTINS_OBJS += test-subprocess.o
755756
TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
756757
TEST_BUILTINS_OBJS += test-wildmatch.o

builtin/grep.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,11 +422,23 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
422422
struct repository submodule;
423423
int hit;
424424

425-
if (!is_submodule_active(superproject, path))
425+
/*
426+
* NEEDSWORK: submodules functions need to be protected because they
427+
* access the object store via config_from_gitmodules(): the latter
428+
* uses get_oid() which, for now, relies on the global the_repository
429+
* object.
430+
*/
431+
grep_read_lock();
432+
433+
if (!is_submodule_active(superproject, path)) {
434+
grep_read_unlock();
426435
return 0;
436+
}
427437

428-
if (repo_submodule_init(&submodule, superproject, path))
438+
if (repo_submodule_init(&submodule, superproject, path)) {
439+
grep_read_unlock();
429440
return 0;
441+
}
430442

431443
repo_read_gitmodules(&submodule);
432444

@@ -440,7 +452,6 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
440452
* store is no longer global and instead is a member of the repository
441453
* object.
442454
*/
443-
grep_read_lock();
444455
add_to_alternates_memory(submodule.objects->objectdir);
445456
grep_read_unlock();
446457

builtin/submodule--helper.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2141,6 +2141,45 @@ static int check_name(int argc, const char **argv, const char *prefix)
21412141
return 0;
21422142
}
21432143

2144+
static int module_config(int argc, const char **argv, const char *prefix)
2145+
{
2146+
enum {
2147+
CHECK_WRITEABLE = 1
2148+
} command = 0;
2149+
2150+
struct option module_config_options[] = {
2151+
OPT_CMDMODE(0, "check-writeable", &command,
2152+
N_("check if it is safe to write to the .gitmodules file"),
2153+
CHECK_WRITEABLE),
2154+
OPT_END()
2155+
};
2156+
const char *const git_submodule_helper_usage[] = {
2157+
N_("git submodule--helper config name [value]"),
2158+
N_("git submodule--helper config --check-writeable"),
2159+
NULL
2160+
};
2161+
2162+
argc = parse_options(argc, argv, prefix, module_config_options,
2163+
git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
2164+
2165+
if (argc == 1 && command == CHECK_WRITEABLE)
2166+
return is_writing_gitmodules_ok() ? 0 : -1;
2167+
2168+
/* Equivalent to ACTION_GET in builtin/config.c */
2169+
if (argc == 2)
2170+
return print_config_from_gitmodules(the_repository, argv[1]);
2171+
2172+
/* Equivalent to ACTION_SET in builtin/config.c */
2173+
if (argc == 3) {
2174+
if (!is_writing_gitmodules_ok())
2175+
die(_("please make sure that the .gitmodules file is in the working tree"));
2176+
2177+
return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
2178+
}
2179+
2180+
usage_with_options(git_submodule_helper_usage, module_config_options);
2181+
}
2182+
21442183
#define SUPPORT_SUPER_PREFIX (1<<0)
21452184

21462185
struct cmd_struct {
@@ -2170,6 +2209,7 @@ static struct cmd_struct commands[] = {
21702209
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
21712210
{"is-active", is_active, 0},
21722211
{"check-name", check_name, 0},
2212+
{"config", module_config, 0},
21732213
};
21742214

21752215
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,8 @@ static inline enum object_type object_type(unsigned int mode)
486486
#define INFOATTRIBUTES_FILE "info/attributes"
487487
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
488488
#define GITMODULES_FILE ".gitmodules"
489+
#define GITMODULES_INDEX ":.gitmodules"
490+
#define GITMODULES_HEAD "HEAD:.gitmodules"
489491
#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
490492
#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
491493
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"

git-submodule.sh

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ get_submodule_config () {
7272
value=$(git config submodule."$name"."$option")
7373
if test -z "$value"
7474
then
75-
value=$(git config -f .gitmodules submodule."$name"."$option")
75+
value=$(git submodule--helper config submodule."$name"."$option")
7676
fi
7777
printf '%s' "${value:-$default}"
7878
}
@@ -164,6 +164,11 @@ cmd_add()
164164
shift
165165
done
166166

167+
if ! git submodule--helper config --check-writeable >/dev/null 2>&1
168+
then
169+
die "$(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
170+
fi
171+
167172
if test -n "$reference_path"
168173
then
169174
is_absolute_path "$reference_path" ||
@@ -288,11 +293,11 @@ or you are unsure what this means choose another name with the '--name' option."
288293
git add --no-warn-embedded-repo $force "$sm_path" ||
289294
die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
290295

291-
git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&
292-
git config -f .gitmodules submodule."$sm_name".url "$repo" &&
296+
git submodule--helper config submodule."$sm_name".path "$sm_path" &&
297+
git submodule--helper config submodule."$sm_name".url "$repo" &&
293298
if test -n "$branch"
294299
then
295-
git config -f .gitmodules submodule."$sm_name".branch "$branch"
300+
git submodule--helper config submodule."$sm_name".branch "$branch"
296301
fi &&
297302
git add --force .gitmodules ||
298303
die "$(eval_gettext "Failed to register submodule '\$sm_path'")"

submodule-config.c

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "cache.h"
2+
#include "dir.h"
23
#include "repository.h"
34
#include "config.h"
45
#include "submodule-config.h"
@@ -613,8 +614,34 @@ static void submodule_cache_check_init(struct repository *repo)
613614
static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void *data)
614615
{
615616
if (repo->worktree) {
616-
char *file = repo_worktree_path(repo, GITMODULES_FILE);
617-
git_config_from_file(fn, file, data);
617+
struct git_config_source config_source = { 0 };
618+
const struct config_options opts = { 0 };
619+
struct object_id oid;
620+
char *file;
621+
622+
file = repo_worktree_path(repo, GITMODULES_FILE);
623+
if (file_exists(file)) {
624+
config_source.file = file;
625+
} else if (repo->submodule_prefix) {
626+
/*
627+
* When get_oid and config_with_options, used below,
628+
* become able to work on a specific repository, this
629+
* warning branch can be removed.
630+
*/
631+
warning("nested submodules without %s in the working tree are not supported yet",
632+
GITMODULES_FILE);
633+
goto out;
634+
} else if (get_oid(GITMODULES_INDEX, &oid) >= 0) {
635+
config_source.blob = GITMODULES_INDEX;
636+
} else if (get_oid(GITMODULES_HEAD, &oid) >= 0) {
637+
config_source.blob = GITMODULES_HEAD;
638+
} else {
639+
goto out;
640+
}
641+
642+
config_with_options(fn, data, &config_source, &opts);
643+
644+
out:
618645
free(file);
619646
}
620647
}
@@ -692,6 +719,43 @@ void submodule_free(struct repository *r)
692719
submodule_cache_clear(r->submodule_cache);
693720
}
694721

722+
static int config_print_callback(const char *var, const char *value, void *cb_data)
723+
{
724+
char *wanted_key = cb_data;
725+
726+
if (!strcmp(wanted_key, var))
727+
printf("%s\n", value);
728+
729+
return 0;
730+
}
731+
732+
int print_config_from_gitmodules(struct repository *repo, const char *key)
733+
{
734+
int ret;
735+
char *store_key;
736+
737+
ret = git_config_parse_key(key, &store_key, NULL);
738+
if (ret < 0)
739+
return CONFIG_INVALID_KEY;
740+
741+
config_from_gitmodules(config_print_callback, repo, store_key);
742+
743+
free(store_key);
744+
return 0;
745+
}
746+
747+
int config_set_in_gitmodules_file_gently(const char *key, const char *value)
748+
{
749+
int ret;
750+
751+
ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value);
752+
if (ret < 0)
753+
/* Maybe the user already did that, don't error out here */
754+
warning(_("Could not update .gitmodules entry %s"), key);
755+
756+
return ret;
757+
}
758+
695759
struct fetch_config {
696760
int *max_children;
697761
int *recurse_submodules;

submodule-config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ const struct submodule *submodule_from_path(struct repository *r,
4848
const struct object_id *commit_or_tree,
4949
const char *path);
5050
void submodule_free(struct repository *r);
51+
int print_config_from_gitmodules(struct repository *repo, const char *key);
52+
int config_set_in_gitmodules_file_gently(const char *key, const char *value);
5153

5254
/*
5355
* Returns 0 if the name is syntactically acceptable as a submodule "name"

submodule.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,24 @@ int is_gitmodules_unmerged(const struct index_state *istate)
5151
return 0;
5252
}
5353

54+
/*
55+
* Check if the .gitmodules file is safe to write.
56+
*
57+
* Writing to the .gitmodules file requires that the file exists in the
58+
* working tree or, if it doesn't, that a brand new .gitmodules file is going
59+
* to be created (i.e. it's neither in the index nor in the current branch).
60+
*
61+
* It is not safe to write to .gitmodules if it's not in the working tree but
62+
* it is in the index or in the current branch, because writing new values
63+
* (and staging them) would blindly overwrite ALL the old content.
64+
*/
65+
int is_writing_gitmodules_ok(void)
66+
{
67+
struct object_id oid;
68+
return file_exists(GITMODULES_FILE) ||
69+
(get_oid(GITMODULES_INDEX, &oid) < 0 && get_oid(GITMODULES_HEAD, &oid) < 0);
70+
}
71+
5472
/*
5573
* Check if the .gitmodules file has unstaged modifications. This must be
5674
* checked before allowing modifications to the .gitmodules file with the
@@ -89,6 +107,7 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
89107
{
90108
struct strbuf entry = STRBUF_INIT;
91109
const struct submodule *submodule;
110+
int ret;
92111

93112
if (!file_exists(GITMODULES_FILE)) /* Do nothing without .gitmodules */
94113
return -1;
@@ -104,14 +123,9 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
104123
strbuf_addstr(&entry, "submodule.");
105124
strbuf_addstr(&entry, submodule->name);
106125
strbuf_addstr(&entry, ".path");
107-
if (git_config_set_in_file_gently(GITMODULES_FILE, entry.buf, newpath) < 0) {
108-
/* Maybe the user already did that, don't error out here */
109-
warning(_("Could not update .gitmodules entry %s"), entry.buf);
110-
strbuf_release(&entry);
111-
return -1;
112-
}
126+
ret = config_set_in_gitmodules_file_gently(entry.buf, newpath);
113127
strbuf_release(&entry);
114-
return 0;
128+
return ret;
115129
}
116130

117131
/*

submodule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct submodule_update_strategy {
4040
#define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}
4141

4242
int is_gitmodules_unmerged(const struct index_state *istate);
43+
int is_writing_gitmodules_ok(void);
4344
int is_staging_gitmodules_ok(struct index_state *istate);
4445
int update_path_in_gitmodules(const char *oldpath, const char *newpath);
4546
int remove_path_from_gitmodules(const char *path);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "test-tool.h"
2+
#include "submodule-config.h"
3+
4+
static void die_usage(int argc, const char **argv, const char *msg)
5+
{
6+
fprintf(stderr, "%s\n", msg);
7+
fprintf(stderr, "Usage: %s <submodulepath> <config name>\n", argv[0]);
8+
exit(1);
9+
}
10+
11+
int cmd__submodule_nested_repo_config(int argc, const char **argv)
12+
{
13+
struct repository submodule;
14+
15+
if (argc < 3)
16+
die_usage(argc, argv, "Wrong number of arguments.");
17+
18+
setup_git_directory();
19+
20+
if (repo_submodule_init(&submodule, the_repository, argv[1])) {
21+
die_usage(argc, argv, "Submodule not found.");
22+
}
23+
24+
/* Read the config of _child_ submodules. */
25+
print_config_from_gitmodules(&submodule, argv[2]);
26+
27+
submodule_free(the_repository);
28+
29+
return 0;
30+
}

0 commit comments

Comments
 (0)