Skip to content

Commit

Permalink
Merge branch 'ao/submodule-wo-gitmodules-checked-out'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Nov 13, 2018
2 parents 504bdc5 + 2b1257e commit abb4824
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 32 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ TEST_BUILTINS_OBJS += test-sigchain.o
TEST_BUILTINS_OBJS += test-strcmp-offset.o
TEST_BUILTINS_OBJS += test-string-list.o
TEST_BUILTINS_OBJS += test-submodule-config.o
TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
TEST_BUILTINS_OBJS += test-subprocess.o
TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
TEST_BUILTINS_OBJS += test-wildmatch.o
Expand Down
17 changes: 14 additions & 3 deletions builtin/grep.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,11 +422,23 @@ static int grep_submodule(struct grep_opt *opt, struct repository *superproject,
struct repository submodule;
int hit;

if (!is_submodule_active(superproject, path))
/*
* NEEDSWORK: submodules functions need to be protected because they
* access the object store via config_from_gitmodules(): the latter
* uses get_oid() which, for now, relies on the global the_repository
* object.
*/
grep_read_lock();

if (!is_submodule_active(superproject, path)) {
grep_read_unlock();
return 0;
}

if (repo_submodule_init(&submodule, superproject, path))
if (repo_submodule_init(&submodule, superproject, path)) {
grep_read_unlock();
return 0;
}

repo_read_gitmodules(&submodule);

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

Expand Down
40 changes: 40 additions & 0 deletions builtin/submodule--helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -2141,6 +2141,45 @@ static int check_name(int argc, const char **argv, const char *prefix)
return 0;
}

static int module_config(int argc, const char **argv, const char *prefix)
{
enum {
CHECK_WRITEABLE = 1
} command = 0;

struct option module_config_options[] = {
OPT_CMDMODE(0, "check-writeable", &command,
N_("check if it is safe to write to the .gitmodules file"),
CHECK_WRITEABLE),
OPT_END()
};
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper config name [value]"),
N_("git submodule--helper config --check-writeable"),
NULL
};

argc = parse_options(argc, argv, prefix, module_config_options,
git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);

if (argc == 1 && command == CHECK_WRITEABLE)
return is_writing_gitmodules_ok() ? 0 : -1;

/* Equivalent to ACTION_GET in builtin/config.c */
if (argc == 2)
return print_config_from_gitmodules(the_repository, argv[1]);

/* Equivalent to ACTION_SET in builtin/config.c */
if (argc == 3) {
if (!is_writing_gitmodules_ok())
die(_("please make sure that the .gitmodules file is in the working tree"));

return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
}

usage_with_options(git_submodule_helper_usage, module_config_options);
}

#define SUPPORT_SUPER_PREFIX (1<<0)

struct cmd_struct {
Expand Down Expand Up @@ -2170,6 +2209,7 @@ static struct cmd_struct commands[] = {
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
{"is-active", is_active, 0},
{"check-name", check_name, 0},
{"config", module_config, 0},
};

int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
Expand Down
2 changes: 2 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,8 @@ static inline enum object_type object_type(unsigned int mode)
#define INFOATTRIBUTES_FILE "info/attributes"
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
#define GITMODULES_FILE ".gitmodules"
#define GITMODULES_INDEX ":.gitmodules"
#define GITMODULES_HEAD "HEAD:.gitmodules"
#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
Expand Down
13 changes: 9 additions & 4 deletions git-submodule.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ get_submodule_config () {
value=$(git config submodule."$name"."$option")
if test -z "$value"
then
value=$(git config -f .gitmodules submodule."$name"."$option")
value=$(git submodule--helper config submodule."$name"."$option")
fi
printf '%s' "${value:-$default}"
}
Expand Down Expand Up @@ -164,6 +164,11 @@ cmd_add()
shift
done

if ! git submodule--helper config --check-writeable >/dev/null 2>&1
then
die "$(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
fi

if test -n "$reference_path"
then
is_absolute_path "$reference_path" ||
Expand Down Expand Up @@ -288,11 +293,11 @@ or you are unsure what this means choose another name with the '--name' option."
git add --no-warn-embedded-repo $force "$sm_path" ||
die "$(eval_gettext "Failed to add submodule '\$sm_path'")"

git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&
git config -f .gitmodules submodule."$sm_name".url "$repo" &&
git submodule--helper config submodule."$sm_name".path "$sm_path" &&
git submodule--helper config submodule."$sm_name".url "$repo" &&
if test -n "$branch"
then
git config -f .gitmodules submodule."$sm_name".branch "$branch"
git submodule--helper config submodule."$sm_name".branch "$branch"
fi &&
git add --force .gitmodules ||
die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
Expand Down
68 changes: 66 additions & 2 deletions submodule-config.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "cache.h"
#include "dir.h"
#include "repository.h"
#include "config.h"
#include "submodule-config.h"
Expand Down Expand Up @@ -613,8 +614,34 @@ static void submodule_cache_check_init(struct repository *repo)
static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void *data)
{
if (repo->worktree) {
char *file = repo_worktree_path(repo, GITMODULES_FILE);
git_config_from_file(fn, file, data);
struct git_config_source config_source = { 0 };
const struct config_options opts = { 0 };
struct object_id oid;
char *file;

file = repo_worktree_path(repo, GITMODULES_FILE);
if (file_exists(file)) {
config_source.file = file;
} else if (repo->submodule_prefix) {
/*
* When get_oid and config_with_options, used below,
* become able to work on a specific repository, this
* warning branch can be removed.
*/
warning("nested submodules without %s in the working tree are not supported yet",
GITMODULES_FILE);
goto out;
} else if (get_oid(GITMODULES_INDEX, &oid) >= 0) {
config_source.blob = GITMODULES_INDEX;
} else if (get_oid(GITMODULES_HEAD, &oid) >= 0) {
config_source.blob = GITMODULES_HEAD;
} else {
goto out;
}

config_with_options(fn, data, &config_source, &opts);

out:
free(file);
}
}
Expand Down Expand Up @@ -692,6 +719,43 @@ void submodule_free(struct repository *r)
submodule_cache_clear(r->submodule_cache);
}

static int config_print_callback(const char *var, const char *value, void *cb_data)
{
char *wanted_key = cb_data;

if (!strcmp(wanted_key, var))
printf("%s\n", value);

return 0;
}

int print_config_from_gitmodules(struct repository *repo, const char *key)
{
int ret;
char *store_key;

ret = git_config_parse_key(key, &store_key, NULL);
if (ret < 0)
return CONFIG_INVALID_KEY;

config_from_gitmodules(config_print_callback, repo, store_key);

free(store_key);
return 0;
}

int config_set_in_gitmodules_file_gently(const char *key, const char *value)
{
int ret;

ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value);
if (ret < 0)
/* Maybe the user already did that, don't error out here */
warning(_("Could not update .gitmodules entry %s"), key);

return ret;
}

struct fetch_config {
int *max_children;
int *recurse_submodules;
Expand Down
2 changes: 2 additions & 0 deletions submodule-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ const struct submodule *submodule_from_path(struct repository *r,
const struct object_id *commit_or_tree,
const char *path);
void submodule_free(struct repository *r);
int print_config_from_gitmodules(struct repository *repo, const char *key);
int config_set_in_gitmodules_file_gently(const char *key, const char *value);

/*
* Returns 0 if the name is syntactically acceptable as a submodule "name"
Expand Down
28 changes: 21 additions & 7 deletions submodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ int is_gitmodules_unmerged(const struct index_state *istate)
return 0;
}

/*
* Check if the .gitmodules file is safe to write.
*
* Writing to the .gitmodules file requires that the file exists in the
* working tree or, if it doesn't, that a brand new .gitmodules file is going
* to be created (i.e. it's neither in the index nor in the current branch).
*
* It is not safe to write to .gitmodules if it's not in the working tree but
* it is in the index or in the current branch, because writing new values
* (and staging them) would blindly overwrite ALL the old content.
*/
int is_writing_gitmodules_ok(void)
{
struct object_id oid;
return file_exists(GITMODULES_FILE) ||
(get_oid(GITMODULES_INDEX, &oid) < 0 && get_oid(GITMODULES_HEAD, &oid) < 0);
}

/*
* Check if the .gitmodules file has unstaged modifications. This must be
* checked before allowing modifications to the .gitmodules file with the
Expand Down Expand Up @@ -89,6 +107,7 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
{
struct strbuf entry = STRBUF_INIT;
const struct submodule *submodule;
int ret;

if (!file_exists(GITMODULES_FILE)) /* Do nothing without .gitmodules */
return -1;
Expand All @@ -104,14 +123,9 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
strbuf_addstr(&entry, "submodule.");
strbuf_addstr(&entry, submodule->name);
strbuf_addstr(&entry, ".path");
if (git_config_set_in_file_gently(GITMODULES_FILE, entry.buf, newpath) < 0) {
/* Maybe the user already did that, don't error out here */
warning(_("Could not update .gitmodules entry %s"), entry.buf);
strbuf_release(&entry);
return -1;
}
ret = config_set_in_gitmodules_file_gently(entry.buf, newpath);
strbuf_release(&entry);
return 0;
return ret;
}

/*
Expand Down
1 change: 1 addition & 0 deletions submodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct submodule_update_strategy {
#define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}

int is_gitmodules_unmerged(const struct index_state *istate);
int is_writing_gitmodules_ok(void);
int is_staging_gitmodules_ok(struct index_state *istate);
int update_path_in_gitmodules(const char *oldpath, const char *newpath);
int remove_path_from_gitmodules(const char *path);
Expand Down
30 changes: 30 additions & 0 deletions t/helper/test-submodule-nested-repo-config.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "test-tool.h"
#include "submodule-config.h"

static void die_usage(int argc, const char **argv, const char *msg)
{
fprintf(stderr, "%s\n", msg);
fprintf(stderr, "Usage: %s <submodulepath> <config name>\n", argv[0]);
exit(1);
}

int cmd__submodule_nested_repo_config(int argc, const char **argv)
{
struct repository submodule;

if (argc < 3)
die_usage(argc, argv, "Wrong number of arguments.");

setup_git_directory();

if (repo_submodule_init(&submodule, the_repository, argv[1])) {
die_usage(argc, argv, "Submodule not found.");
}

/* Read the config of _child_ submodules. */
print_config_from_gitmodules(&submodule, argv[2]);

submodule_free(the_repository);

return 0;
}
1 change: 1 addition & 0 deletions t/helper/test-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ static struct test_cmd cmds[] = {
{ "strcmp-offset", cmd__strcmp_offset },
{ "string-list", cmd__string_list },
{ "submodule-config", cmd__submodule_config },
{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
{ "subprocess", cmd__subprocess },
{ "urlmatch-normalization", cmd__urlmatch_normalization },
{ "wildmatch", cmd__wildmatch },
Expand Down
1 change: 1 addition & 0 deletions t/helper/test-tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ int cmd__sigchain(int argc, const char **argv);
int cmd__strcmp_offset(int argc, const char **argv);
int cmd__string_list(int argc, const char **argv);
int cmd__submodule_config(int argc, const char **argv);
int cmd__submodule_nested_repo_config(int argc, const char **argv);
int cmd__subprocess(int argc, const char **argv);
int cmd__urlmatch_normalization(int argc, const char **argv);
int cmd__wildmatch(int argc, const char **argv);
Expand Down
Loading

0 comments on commit abb4824

Please sign in to comment.