about summary refs log tree commit diff
path: root/third_party/git/submodule.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/git/submodule.c392
1 files changed, 227 insertions, 165 deletions
diff --git a/third_party/git/submodule.c b/third_party/git/submodule.c
index 0f199c5137..b3bb59f066 100644
--- a/third_party/git/submodule.c
+++ b/third_party/git/submodule.c
@@ -12,8 +12,8 @@
 #include "diffcore.h"
 #include "refs.h"
 #include "string-list.h"
-#include "sha1-array.h"
-#include "argv-array.h"
+#include "oid-array.h"
+#include "strvec.h"
 #include "blob.h"
 #include "thread-utils.h"
 #include "quote.h"
@@ -82,7 +82,7 @@ int is_staging_gitmodules_ok(struct index_state *istate)
 	if ((pos >= 0) && (pos < istate->cache_nr)) {
 		struct stat st;
 		if (lstat(GITMODULES_FILE, &st) == 0 &&
-		    ie_match_stat(istate, istate->cache[pos], &st, 0) & DATA_CHANGED)
+		    ie_modified(istate, istate->cache[pos], &st, 0) & DATA_CHANGED)
 			return 0;
 	}
 
@@ -194,7 +194,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
 		char *key;
 
 		key = xstrfmt("submodule.%s.ignore", submodule->name);
-		if (repo_config_get_string_const(the_repository, key, &ignore))
+		if (repo_config_get_string_tmp(the_repository, key, &ignore))
 			ignore = submodule->ignore;
 		free(key);
 
@@ -262,17 +262,17 @@ int is_submodule_active(struct repository *repo, const char *path)
 	sl = repo_config_get_value_multi(repo, "submodule.active");
 	if (sl) {
 		struct pathspec ps;
-		struct argv_array args = ARGV_ARRAY_INIT;
+		struct strvec args = STRVEC_INIT;
 		const struct string_list_item *item;
 
 		for_each_string_list_item(item, sl) {
-			argv_array_push(&args, item->string);
+			strvec_push(&args, item->string);
 		}
 
-		parse_pathspec(&ps, 0, 0, NULL, args.argv);
+		parse_pathspec(&ps, 0, 0, NULL, args.v);
 		ret = match_pathspec(repo->index, &ps, path, strlen(path), 0, NULL, 1);
 
-		argv_array_clear(&args);
+		strvec_clear(&args);
 		clear_pathspec(&ps);
 		return ret;
 	}
@@ -431,20 +431,21 @@ void handle_ignore_submodules_arg(struct diff_options *diffopt,
 	else if (!strcmp(arg, "dirty"))
 		diffopt->flags.ignore_dirty_submodules = 1;
 	else if (strcmp(arg, "none"))
-		die("bad --ignore-submodules argument: %s", arg);
+		die(_("bad --ignore-submodules argument: %s"), arg);
 	/*
 	 * Please update _git_status() in git-completion.bash when you
 	 * add new options
 	 */
 }
 
-static int prepare_submodule_summary(struct rev_info *rev, const char *path,
-		struct commit *left, struct commit *right,
-		struct commit_list *merge_bases)
+static int prepare_submodule_diff_summary(struct repository *r, struct rev_info *rev,
+					  const char *path,
+					  struct commit *left, struct commit *right,
+					  struct commit_list *merge_bases)
 {
 	struct commit_list *list;
 
-	repo_init_revisions(the_repository, rev, NULL);
+	repo_init_revisions(r, rev, NULL);
 	setup_revisions(0, NULL, rev, NULL);
 	rev->left_right = 1;
 	rev->first_parent_only = 1;
@@ -459,7 +460,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
 	return prepare_revision_walk(rev);
 }
 
-static void print_submodule_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
+static void print_submodule_diff_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
 {
 	static const char format[] = "  %m %s";
 	struct strbuf sb = STRBUF_INIT;
@@ -481,27 +482,27 @@ static void print_submodule_summary(struct repository *r, struct rev_info *rev,
 	strbuf_release(&sb);
 }
 
-static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out)
+static void prepare_submodule_repo_env_no_git_dir(struct strvec *out)
 {
 	const char * const *var;
 
 	for (var = local_repo_env; *var; var++) {
 		if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
-			argv_array_push(out, *var);
+			strvec_push(out, *var);
 	}
 }
 
-void prepare_submodule_repo_env(struct argv_array *out)
+void prepare_submodule_repo_env(struct strvec *out)
 {
 	prepare_submodule_repo_env_no_git_dir(out);
-	argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
-			 DEFAULT_GIT_DIR_ENVIRONMENT);
+	strvec_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
+		     DEFAULT_GIT_DIR_ENVIRONMENT);
 }
 
-static void prepare_submodule_repo_env_in_gitdir(struct argv_array *out)
+static void prepare_submodule_repo_env_in_gitdir(struct strvec *out)
 {
 	prepare_submodule_repo_env_no_git_dir(out);
-	argv_array_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT);
+	strvec_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT);
 }
 
 /*
@@ -610,7 +611,7 @@ output_header:
 	strbuf_release(&sb);
 }
 
-void show_submodule_summary(struct diff_options *o, const char *path,
+void show_submodule_diff_summary(struct diff_options *o, const char *path,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule)
 {
@@ -632,12 +633,12 @@ void show_submodule_summary(struct diff_options *o, const char *path,
 		goto out;
 
 	/* Treat revision walker failure the same as missing commits */
-	if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
+	if (prepare_submodule_diff_summary(sub, &rev, path, left, right, merge_bases)) {
 		diff_emit_submodule_error(o, "(revision walker failed)\n");
 		goto out;
 	}
 
-	print_submodule_summary(sub, &rev, o);
+	print_submodule_diff_summary(sub, &rev, o);
 
 out:
 	if (merge_bases)
@@ -681,22 +682,22 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
 	cp.no_stdin = 1;
 
 	/* TODO: other options may need to be passed here. */
-	argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL);
-	argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
+	strvec_pushl(&cp.args, "diff", "--submodule=diff", NULL);
+	strvec_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
 			 "always" : "never");
 
 	if (o->flags.reverse_diff) {
-		argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
-				 o->b_prefix, path);
-		argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
-				 o->a_prefix, path);
+		strvec_pushf(&cp.args, "--src-prefix=%s%s/",
+			     o->b_prefix, path);
+		strvec_pushf(&cp.args, "--dst-prefix=%s%s/",
+			     o->a_prefix, path);
 	} else {
-		argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
-				 o->a_prefix, path);
-		argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
-				 o->b_prefix, path);
+		strvec_pushf(&cp.args, "--src-prefix=%s%s/",
+			     o->a_prefix, path);
+		strvec_pushf(&cp.args, "--dst-prefix=%s%s/",
+			     o->b_prefix, path);
 	}
-	argv_array_push(&cp.args, oid_to_hex(old_oid));
+	strvec_push(&cp.args, oid_to_hex(old_oid));
 	/*
 	 * If the submodule has modified content, we will diff against the
 	 * work tree, under the assumption that the user has asked for the
@@ -704,7 +705,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
 	 * haven't yet been committed to the submodule yet.
 	 */
 	if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
-		argv_array_push(&cp.args, oid_to_hex(new_oid));
+		strvec_push(&cp.args, oid_to_hex(new_oid));
 
 	prepare_submodule_repo_env(&cp.env_array);
 	if (start_command(&cp))
@@ -812,9 +813,9 @@ static void collect_changed_submodules_cb(struct diff_queue_struct *q,
 				submodule = submodule_from_name(me->repo,
 								commit_oid, name);
 			if (submodule) {
-				warning("Submodule in commit %s at path: "
+				warning(_("Submodule in commit %s at path: "
 					"'%s' collides with a submodule named "
-					"the same. Skipping it.",
+					"the same. Skipping it."),
 					oid_to_hex(commit_oid), p->two->path);
 				name = NULL;
 			}
@@ -836,15 +837,22 @@ static void collect_changed_submodules_cb(struct diff_queue_struct *q,
  */
 static void collect_changed_submodules(struct repository *r,
 				       struct string_list *changed,
-				       struct argv_array *argv)
+				       struct strvec *argv)
 {
 	struct rev_info rev;
 	const struct commit *commit;
+	int save_warning;
+	struct setup_revision_opt s_r_opt = {
+		.assume_dashdash = 1,
+	};
 
+	save_warning = warn_on_object_refname_ambiguity;
+	warn_on_object_refname_ambiguity = 0;
 	repo_init_revisions(r, &rev, NULL);
-	setup_revisions(argv->argc, argv->argv, &rev, NULL);
+	setup_revisions(argv->nr, argv->v, &rev, &s_r_opt);
+	warn_on_object_refname_ambiguity = save_warning;
 	if (prepare_revision_walk(&rev))
-		die("revision walk setup failed");
+		die(_("revision walk setup failed"));
 
 	while ((commit = get_revision(&rev))) {
 		struct rev_info diff_rev;
@@ -857,7 +865,8 @@ static void collect_changed_submodules(struct repository *r,
 		diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
 		diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
 		diff_rev.diffopt.format_callback_data = &data;
-		diff_tree_combined_merge(commit, 1, &diff_rev);
+		diff_rev.dense_combined_merges = 1;
+		diff_tree_combined_merge(commit, &diff_rev);
 	}
 
 	reset_revision_walk();
@@ -879,8 +888,8 @@ static int has_remote(const char *refname, const struct object_id *oid,
 
 static int append_oid_to_argv(const struct object_id *oid, void *data)
 {
-	struct argv_array *argv = data;
-	argv_array_push(argv, oid_to_hex(oid));
+	struct strvec *argv = data;
+	strvec_push(argv, oid_to_hex(oid));
 	return 0;
 }
 
@@ -941,9 +950,9 @@ static int submodule_has_commits(struct repository *r,
 		struct child_process cp = CHILD_PROCESS_INIT;
 		struct strbuf out = STRBUF_INIT;
 
-		argv_array_pushl(&cp.args, "rev-list", "-n", "1", NULL);
+		strvec_pushl(&cp.args, "rev-list", "-n", "1", NULL);
 		oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args);
-		argv_array_pushl(&cp.args, "--not", "--all", NULL);
+		strvec_pushl(&cp.args, "--not", "--all", NULL);
 
 		prepare_submodule_repo_env(&cp.env_array);
 		cp.git_cmd = 1;
@@ -982,9 +991,9 @@ static int submodule_needs_pushing(struct repository *r,
 		struct strbuf buf = STRBUF_INIT;
 		int needs_pushing = 0;
 
-		argv_array_push(&cp.args, "rev-list");
+		strvec_push(&cp.args, "rev-list");
 		oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args);
-		argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL);
+		strvec_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL);
 
 		prepare_submodule_repo_env(&cp.env_array);
 		cp.git_cmd = 1;
@@ -992,7 +1001,7 @@ static int submodule_needs_pushing(struct repository *r,
 		cp.out = -1;
 		cp.dir = path;
 		if (start_command(&cp))
-			die("Could not run 'git rev-list <commits> --not --remotes -n 1' command in submodule %s",
+			die(_("Could not run 'git rev-list <commits> --not --remotes -n 1' command in submodule %s"),
 					path);
 		if (strbuf_read(&buf, cp.out, the_hash_algo->hexsz + 1))
 			needs_pushing = 1;
@@ -1012,13 +1021,13 @@ int find_unpushed_submodules(struct repository *r,
 {
 	struct string_list submodules = STRING_LIST_INIT_DUP;
 	struct string_list_item *name;
-	struct argv_array argv = ARGV_ARRAY_INIT;
+	struct strvec argv = STRVEC_INIT;
 
-	/* argv.argv[0] will be ignored by setup_revisions */
-	argv_array_push(&argv, "find_unpushed_submodules");
+	/* argv.v[0] will be ignored by setup_revisions */
+	strvec_push(&argv, "find_unpushed_submodules");
 	oid_array_for_each_unique(commits, append_oid_to_argv, &argv);
-	argv_array_push(&argv, "--not");
-	argv_array_pushf(&argv, "--remotes=%s", remotes_name);
+	strvec_push(&argv, "--not");
+	strvec_pushf(&argv, "--remotes=%s", remotes_name);
 
 	collect_changed_submodules(r, &submodules, &argv);
 
@@ -1041,7 +1050,7 @@ int find_unpushed_submodules(struct repository *r,
 	}
 
 	free_submodules_oids(&submodules);
-	argv_array_clear(&argv);
+	strvec_clear(&argv);
 
 	return needs_pushing->nr;
 }
@@ -1054,22 +1063,22 @@ static int push_submodule(const char *path,
 {
 	if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
 		struct child_process cp = CHILD_PROCESS_INIT;
-		argv_array_push(&cp.args, "push");
+		strvec_push(&cp.args, "push");
 		if (dry_run)
-			argv_array_push(&cp.args, "--dry-run");
+			strvec_push(&cp.args, "--dry-run");
 
 		if (push_options && push_options->nr) {
 			const struct string_list_item *item;
 			for_each_string_list_item(item, push_options)
-				argv_array_pushf(&cp.args, "--push-option=%s",
-						 item->string);
+				strvec_pushf(&cp.args, "--push-option=%s",
+					     item->string);
 		}
 
 		if (remote->origin != REMOTE_UNCONFIGURED) {
 			int i;
-			argv_array_push(&cp.args, remote->name);
+			strvec_push(&cp.args, remote->name);
 			for (i = 0; i < rs->raw_nr; i++)
-				argv_array_push(&cp.args, rs->raw[i]);
+				strvec_push(&cp.args, rs->raw[i]);
 		}
 
 		prepare_submodule_repo_env(&cp.env_array);
@@ -1095,13 +1104,13 @@ static void submodule_push_check(const char *path, const char *head,
 	struct child_process cp = CHILD_PROCESS_INIT;
 	int i;
 
-	argv_array_push(&cp.args, "submodule--helper");
-	argv_array_push(&cp.args, "push-check");
-	argv_array_push(&cp.args, head);
-	argv_array_push(&cp.args, remote->name);
+	strvec_push(&cp.args, "submodule--helper");
+	strvec_push(&cp.args, "push-check");
+	strvec_push(&cp.args, head);
+	strvec_push(&cp.args, remote->name);
 
 	for (i = 0; i < rs->raw_nr; i++)
-		argv_array_push(&cp.args, rs->raw[i]);
+		strvec_push(&cp.args, rs->raw[i]);
 
 	prepare_submodule_repo_env(&cp.env_array);
 	cp.git_cmd = 1;
@@ -1115,7 +1124,7 @@ static void submodule_push_check(const char *path, const char *head,
 	 * child process.
 	 */
 	if (run_command(&cp))
-		die("process for submodule '%s' failed", path);
+		die(_("process for submodule '%s' failed"), path);
 }
 
 int push_unpushed_submodules(struct repository *r,
@@ -1155,10 +1164,10 @@ int push_unpushed_submodules(struct repository *r,
 	/* Actually push the submodules */
 	for (i = 0; i < needs_pushing.nr; i++) {
 		const char *path = needs_pushing.items[i].string;
-		fprintf(stderr, "Pushing submodule '%s'\n", path);
+		fprintf(stderr, _("Pushing submodule '%s'\n"), path);
 		if (!push_submodule(path, remote, rs,
 				    push_options, dry_run)) {
-			fprintf(stderr, "Unable to push submodule '%s'\n", path);
+			fprintf(stderr, _("Unable to push submodule '%s'\n"), path);
 			ret = 0;
 		}
 	}
@@ -1189,17 +1198,17 @@ void check_for_new_submodule_commits(struct object_id *oid)
 static void calculate_changed_submodule_paths(struct repository *r,
 		struct string_list *changed_submodule_names)
 {
-	struct argv_array argv = ARGV_ARRAY_INIT;
+	struct strvec argv = STRVEC_INIT;
 	struct string_list_item *name;
 
 	/* No need to check if there are no submodules configured */
 	if (!submodule_from_path(r, NULL, NULL))
 		return;
 
-	argv_array_push(&argv, "--"); /* argv[0] program name */
+	strvec_push(&argv, "--"); /* argv[0] program name */
 	oid_array_for_each_unique(&ref_tips_after_fetch,
 				   append_oid_to_argv, &argv);
-	argv_array_push(&argv, "--not");
+	strvec_push(&argv, "--not");
 	oid_array_for_each_unique(&ref_tips_before_fetch,
 				   append_oid_to_argv, &argv);
 
@@ -1231,7 +1240,7 @@ static void calculate_changed_submodule_paths(struct repository *r,
 
 	string_list_remove_empty_items(changed_submodule_names, 1);
 
-	argv_array_clear(&argv);
+	strvec_clear(&argv);
 	oid_array_clear(&ref_tips_before_fetch);
 	oid_array_clear(&ref_tips_after_fetch);
 	initialized_fetch_ref_tips = 0;
@@ -1242,24 +1251,24 @@ int submodule_touches_in_range(struct repository *r,
 			       struct object_id *incl_oid)
 {
 	struct string_list subs = STRING_LIST_INIT_DUP;
-	struct argv_array args = ARGV_ARRAY_INIT;
+	struct strvec args = STRVEC_INIT;
 	int ret;
 
 	/* No need to check if there are no submodules configured */
 	if (!submodule_from_path(r, NULL, NULL))
 		return 0;
 
-	argv_array_push(&args, "--"); /* args[0] program name */
-	argv_array_push(&args, oid_to_hex(incl_oid));
+	strvec_push(&args, "--"); /* args[0] program name */
+	strvec_push(&args, oid_to_hex(incl_oid));
 	if (!is_null_oid(excl_oid)) {
-		argv_array_push(&args, "--not");
-		argv_array_push(&args, oid_to_hex(excl_oid));
+		strvec_push(&args, "--not");
+		strvec_push(&args, oid_to_hex(excl_oid));
 	}
 
 	collect_changed_submodules(r, &subs, &args);
 	ret = subs.nr;
 
-	argv_array_clear(&args);
+	strvec_clear(&args);
 
 	free_submodules_oids(&subs);
 	return ret;
@@ -1267,7 +1276,7 @@ int submodule_touches_in_range(struct repository *r,
 
 struct submodule_parallel_fetch {
 	int count;
-	struct argv_array args;
+	struct strvec args;
 	struct repository *r;
 	const char *prefix;
 	int command_line_option;
@@ -1280,10 +1289,12 @@ struct submodule_parallel_fetch {
 	/* Pending fetches by OIDs */
 	struct fetch_task **oid_fetch_tasks;
 	int oid_fetch_tasks_nr, oid_fetch_tasks_alloc;
+
+	struct strbuf submodules_with_errors;
 };
-#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0, \
+#define SPF_INIT {0, STRVEC_INIT, NULL, NULL, 0, 0, 0, 0, \
 		  STRING_LIST_INIT_DUP, \
-		  NULL, 0, 0}
+		  NULL, 0, 0, STRBUF_INIT}
 
 static int get_fetch_recurse_config(const struct submodule *submodule,
 				    struct submodule_parallel_fetch *spf)
@@ -1297,7 +1308,7 @@ static int get_fetch_recurse_config(const struct submodule *submodule,
 
 		int fetch_recurse = submodule->fetch_recurse;
 		key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
-		if (!repo_config_get_string_const(spf->r, key, &value)) {
+		if (!repo_config_get_string_tmp(spf->r, key, &value)) {
 			fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
 		}
 		free(key);
@@ -1448,17 +1459,17 @@ static int get_next_submodule(struct child_process *cp,
 			prepare_submodule_repo_env_in_gitdir(&cp->env_array);
 			cp->git_cmd = 1;
 			if (!spf->quiet)
-				strbuf_addf(err, "Fetching submodule %s%s\n",
+				strbuf_addf(err, _("Fetching submodule %s%s\n"),
 					    spf->prefix, ce->name);
-			argv_array_init(&cp->args);
-			argv_array_pushv(&cp->args, spf->args.argv);
-			argv_array_push(&cp->args, default_argv);
-			argv_array_push(&cp->args, "--submodule-prefix");
+			strvec_init(&cp->args);
+			strvec_pushv(&cp->args, spf->args.v);
+			strvec_push(&cp->args, default_argv);
+			strvec_push(&cp->args, "--submodule-prefix");
 
 			strbuf_addf(&submodule_prefix, "%s%s/",
 						       spf->prefix,
 						       task->sub->path);
-			argv_array_push(&cp->args, submodule_prefix.buf);
+			strvec_push(&cp->args, submodule_prefix.buf);
 
 			spf->count++;
 			*task_cb = task;
@@ -1478,7 +1489,7 @@ static int get_next_submodule(struct child_process *cp,
 			    !is_empty_dir(ce->name)) {
 				spf->result = 1;
 				strbuf_addf(err,
-					    _("Could not access submodule '%s'"),
+					    _("Could not access submodule '%s'\n"),
 					    ce->name);
 			}
 		}
@@ -1498,14 +1509,14 @@ static int get_next_submodule(struct child_process *cp,
 		cp->git_cmd = 1;
 		cp->dir = task->repo->gitdir;
 
-		argv_array_init(&cp->args);
-		argv_array_pushv(&cp->args, spf->args.argv);
-		argv_array_push(&cp->args, "on-demand");
-		argv_array_push(&cp->args, "--submodule-prefix");
-		argv_array_push(&cp->args, submodule_prefix.buf);
+		strvec_init(&cp->args);
+		strvec_pushv(&cp->args, spf->args.v);
+		strvec_push(&cp->args, "on-demand");
+		strvec_push(&cp->args, "--submodule-prefix");
+		strvec_push(&cp->args, submodule_prefix.buf);
 
 		/* NEEDSWORK: have get_default_remote from submodule--helper */
-		argv_array_push(&cp->args, "origin");
+		strvec_push(&cp->args, "origin");
 		oid_array_for_each_unique(task->commits,
 					  append_oid_to_argv, &cp->args);
 
@@ -1547,7 +1558,10 @@ static int fetch_finish(int retvalue, struct strbuf *err,
 	struct string_list_item *it;
 	struct oid_array *commits;
 
-	if (retvalue)
+	if (!task || !task->sub)
+		BUG("callback cookie bogus");
+
+	if (retvalue) {
 		/*
 		 * NEEDSWORK: This indicates that the overall fetch
 		 * failed, even though there may be a subsequent fetch
@@ -1557,8 +1571,9 @@ static int fetch_finish(int retvalue, struct strbuf *err,
 		 */
 		spf->result = 1;
 
-	if (!task || !task->sub)
-		BUG("callback cookie bogus");
+		strbuf_addf(&spf->submodules_with_errors, "\t%s\n",
+			    task->sub->name);
+	}
 
 	/* Is this the second time we process this submodule? */
 	if (task->commits)
@@ -1592,7 +1607,7 @@ out:
 }
 
 int fetch_populated_submodules(struct repository *r,
-			       const struct argv_array *options,
+			       const struct strvec *options,
 			       const char *prefix, int command_line_option,
 			       int default_option,
 			       int quiet, int max_parallel_jobs)
@@ -1610,12 +1625,12 @@ int fetch_populated_submodules(struct repository *r,
 		goto out;
 
 	if (repo_read_index(r) < 0)
-		die("index file corrupt");
+		die(_("index file corrupt"));
 
-	argv_array_push(&spf.args, "fetch");
-	for (i = 0; i < options->argc; i++)
-		argv_array_push(&spf.args, options->argv[i]);
-	argv_array_push(&spf.args, "--recurse-submodules-default");
+	strvec_push(&spf.args, "fetch");
+	for (i = 0; i < options->nr; i++)
+		strvec_push(&spf.args, options->v[i]);
+	strvec_push(&spf.args, "--recurse-submodules-default");
 	/* default value, "--submodule-prefix" and its value are added later */
 
 	calculate_changed_submodule_paths(r, &spf.changed_submodule_names);
@@ -1627,7 +1642,12 @@ int fetch_populated_submodules(struct repository *r,
 				   &spf,
 				   "submodule", "parallel/fetch");
 
-	argv_array_clear(&spf.args);
+	if (spf.submodules_with_errors.len > 0)
+		fprintf(stderr, _("Errors during submodule fetch:\n%s"),
+			spf.submodules_with_errors.buf);
+
+
+	strvec_clear(&spf.args);
 out:
 	free_submodules_oids(&spf.changed_submodule_names);
 	return spf.result;
@@ -1655,9 +1675,9 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
 	}
 	strbuf_reset(&buf);
 
-	argv_array_pushl(&cp.args, "status", "--porcelain=2", NULL);
+	strvec_pushl(&cp.args, "status", "--porcelain=2", NULL);
 	if (ignore_untracked)
-		argv_array_push(&cp.args, "-uno");
+		strvec_push(&cp.args, "-uno");
 
 	prepare_submodule_repo_env(&cp.env_array);
 	cp.git_cmd = 1;
@@ -1665,7 +1685,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
 	cp.out = -1;
 	cp.dir = path;
 	if (start_command(&cp))
-		die("Could not run 'git status --porcelain=2' in submodule %s", path);
+		die(_("Could not run 'git status --porcelain=2' in submodule %s"), path);
 
 	fp = xfdopen(cp.out, "r");
 	while (strbuf_getwholeline(&buf, fp, '\n') != EOF) {
@@ -1706,7 +1726,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
 	fclose(fp);
 
 	if (finish_command(&cp) && !ignore_cp_exit_code)
-		die("'git status --porcelain=2' failed in submodule %s", path);
+		die(_("'git status --porcelain=2' failed in submodule %s"), path);
 
 	strbuf_release(&buf);
 	return dirty_submodule;
@@ -1715,14 +1735,6 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
 int submodule_uses_gitfile(const char *path)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
-	const char *argv[] = {
-		"submodule",
-		"foreach",
-		"--quiet",
-		"--recursive",
-		"test -f .git",
-		NULL,
-	};
 	struct strbuf buf = STRBUF_INIT;
 	const char *git_dir;
 
@@ -1735,7 +1747,10 @@ int submodule_uses_gitfile(const char *path)
 	strbuf_release(&buf);
 
 	/* Now test that all nested submodules use a gitfile too */
-	cp.argv = argv;
+	strvec_pushl(&cp.args,
+		     "submodule", "foreach", "--quiet",	"--recursive",
+		     "test -f .git", NULL);
+
 	prepare_submodule_repo_env(&cp.env_array);
 	cp.git_cmd = 1;
 	cp.no_stdin = 1;
@@ -1768,16 +1783,16 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
 	if (!submodule_uses_gitfile(path))
 		return 1;
 
-	argv_array_pushl(&cp.args, "status", "--porcelain",
-				   "--ignore-submodules=none", NULL);
+	strvec_pushl(&cp.args, "status", "--porcelain",
+		     "--ignore-submodules=none", NULL);
 
 	if (flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED)
-		argv_array_push(&cp.args, "-uno");
+		strvec_push(&cp.args, "-uno");
 	else
-		argv_array_push(&cp.args, "-uall");
+		strvec_push(&cp.args, "-uall");
 
 	if (!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED))
-		argv_array_push(&cp.args, "--ignored");
+		strvec_push(&cp.args, "--ignored");
 
 	prepare_submodule_repo_env(&cp.env_array);
 	cp.git_cmd = 1;
@@ -1811,7 +1826,7 @@ out:
 void submodule_unset_core_worktree(const struct submodule *sub)
 {
 	char *config_path = xstrfmt("%s/modules/%s/config",
-				    get_git_common_dir(), sub->name);
+				    get_git_dir(), sub->name);
 
 	if (git_config_set_in_file_gently(config_path, "core.worktree", NULL))
 		warning(_("Could not unset core.worktree setting in submodule '%s'"),
@@ -1835,13 +1850,13 @@ static int submodule_has_dirty_index(const struct submodule *sub)
 	prepare_submodule_repo_env(&cp.env_array);
 
 	cp.git_cmd = 1;
-	argv_array_pushl(&cp.args, "diff-index", "--quiet",
-				   "--cached", "HEAD", NULL);
+	strvec_pushl(&cp.args, "diff-index", "--quiet",
+		     "--cached", "HEAD", NULL);
 	cp.no_stdin = 1;
 	cp.no_stdout = 1;
 	cp.dir = sub->path;
 	if (start_command(&cp))
-		die("could not recurse into submodule '%s'", sub->path);
+		die(_("could not recurse into submodule '%s'"), sub->path);
 
 	return finish_command(&cp);
 }
@@ -1855,14 +1870,14 @@ static void submodule_reset_index(const char *path)
 	cp.no_stdin = 1;
 	cp.dir = path;
 
-	argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
-				   get_super_prefix_or_empty(), path);
-	argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
+	strvec_pushf(&cp.args, "--super-prefix=%s%s/",
+		     get_super_prefix_or_empty(), path);
+	strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
 
-	argv_array_push(&cp.args, empty_tree_oid_hex());
+	strvec_push(&cp.args, empty_tree_oid_hex());
 
 	if (run_command(&cp))
-		die("could not reset submodule index");
+		die(_("could not reset submodule index"));
 }
 
 /**
@@ -1914,7 +1929,7 @@ int submodule_move_head(const char *path,
 					ABSORB_GITDIR_RECURSE_SUBMODULES);
 		} else {
 			char *gitdir = xstrfmt("%s/modules/%s",
-				    get_git_common_dir(), sub->name);
+				    get_git_dir(), sub->name);
 			connect_work_tree_and_git_dir(path, gitdir, 0);
 			free(gitdir);
 
@@ -1924,7 +1939,7 @@ int submodule_move_head(const char *path,
 
 		if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
 			char *gitdir = xstrfmt("%s/modules/%s",
-				    get_git_common_dir(), sub->name);
+				    get_git_dir(), sub->name);
 			connect_work_tree_and_git_dir(path, gitdir, 1);
 			free(gitdir);
 		}
@@ -1936,24 +1951,24 @@ int submodule_move_head(const char *path,
 	cp.no_stdin = 1;
 	cp.dir = path;
 
-	argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
-			get_super_prefix_or_empty(), path);
-	argv_array_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
+	strvec_pushf(&cp.args, "--super-prefix=%s%s/",
+		     get_super_prefix_or_empty(), path);
+	strvec_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
 
 	if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
-		argv_array_push(&cp.args, "-n");
+		strvec_push(&cp.args, "-n");
 	else
-		argv_array_push(&cp.args, "-u");
+		strvec_push(&cp.args, "-u");
 
 	if (flags & SUBMODULE_MOVE_HEAD_FORCE)
-		argv_array_push(&cp.args, "--reset");
+		strvec_push(&cp.args, "--reset");
 	else
-		argv_array_push(&cp.args, "-m");
+		strvec_push(&cp.args, "-m");
 
 	if (!(flags & SUBMODULE_MOVE_HEAD_FORCE))
-		argv_array_push(&cp.args, old_head ? old_head : empty_tree_oid_hex());
+		strvec_push(&cp.args, old_head ? old_head : empty_tree_oid_hex());
 
-	argv_array_push(&cp.args, new_head ? new_head : empty_tree_oid_hex());
+	strvec_push(&cp.args, new_head ? new_head : empty_tree_oid_hex());
 
 	if (run_command(&cp)) {
 		ret = error(_("Submodule '%s' could not be updated."), path);
@@ -1969,8 +1984,8 @@ int submodule_move_head(const char *path,
 			cp.dir = path;
 
 			prepare_submodule_repo_env(&cp.env_array);
-			argv_array_pushl(&cp.args, "update-ref", "HEAD",
-					 "--no-deref", new_head, NULL);
+			strvec_pushl(&cp.args, "update-ref", "HEAD",
+				     "--no-deref", new_head, NULL);
 
 			if (run_command(&cp)) {
 				ret = -1;
@@ -1993,6 +2008,47 @@ out:
 	return ret;
 }
 
+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;
+	int ret = 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'",
+		    submodule_name, git_dir);
+
+	/*
+	 * We prevent the contents of sibling submodules' git directories to
+	 * clash.
+	 *
+	 * Example: having a submodule named `hippo` and another one named
+	 * `hippo/hooks` would result in the git directories
+	 * `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
+	 * but the latter directory is already designated to contain the hooks
+	 * of the former.
+	 */
+	for (; *p; p++) {
+		if (is_dir_sep(*p)) {
+			char c = *p;
+
+			*p = '\0';
+			if (is_git_directory(git_dir))
+				ret = -1;
+			*p = c;
+
+			if (ret < 0)
+				return error(_("submodule git dir '%s' is "
+					       "inside git dir '%.*s'"),
+					     git_dir,
+					     (int)(p - git_dir), git_dir);
+		}
+	}
+
+	return 0;
+}
+
 /*
  * Embeds a single submodules git directory into the superprojects git dir,
  * non recursively.
@@ -2000,7 +2056,7 @@ out:
 static void relocate_single_git_dir_into_superproject(const char *path)
 {
 	char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
-	const char *new_git_dir;
+	char *new_git_dir;
 	const struct submodule *sub;
 
 	if (submodule_uses_worktrees(path))
@@ -2018,10 +2074,14 @@ static void relocate_single_git_dir_into_superproject(const char *path)
 	if (!sub)
 		die(_("could not lookup name for submodule '%s'"), path);
 
-	new_git_dir = git_path("modules/%s", sub->name);
+	new_git_dir = git_pathdup("modules/%s", sub->name);
+	if (validate_submodule_git_dir(new_git_dir, sub->name) < 0)
+		die(_("refusing to move '%s' into an existing git dir"),
+		    real_old_git_dir);
 	if (safe_create_leading_directories_const(new_git_dir) < 0)
 		die(_("could not create directory '%s'"), new_git_dir);
 	real_new_git_dir = real_pathdup(new_git_dir, 1);
+	free(new_git_dir);
 
 	fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
 		get_super_prefix_or_empty(), path,
@@ -2101,9 +2161,9 @@ void absorb_git_dir_into_superproject(const char *path,
 		cp.dir = path;
 		cp.git_cmd = 1;
 		cp.no_stdin = 1;
-		argv_array_pushl(&cp.args, "--super-prefix", sb.buf,
-					   "submodule--helper",
-					   "absorb-git-dirs", NULL);
+		strvec_pushl(&cp.args, "--super-prefix", sb.buf,
+			     "submodule--helper",
+			     "absorb-git-dirs", NULL);
 		prepare_submodule_repo_env(&cp.env_array);
 		if (run_command(&cp))
 			die(_("could not recurse into submodule '%s'"), path);
@@ -2112,13 +2172,13 @@ void absorb_git_dir_into_superproject(const char *path,
 	}
 }
 
-const char *get_superproject_working_tree(void)
+int get_superproject_working_tree(struct strbuf *buf)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct strbuf sb = STRBUF_INIT;
-	const char *one_up = real_path_if_valid("../");
+	struct strbuf one_up = STRBUF_INIT;
 	const char *cwd = xgetcwd();
-	const char *ret = NULL;
+	int ret = 0;
 	const char *subpath;
 	int code;
 	ssize_t len;
@@ -2129,19 +2189,20 @@ const char *get_superproject_working_tree(void)
 		 * We might have a superproject, but it is harder
 		 * to determine.
 		 */
-		return NULL;
+		return 0;
 
-	if (!one_up)
-		return NULL;
+	if (!strbuf_realpath(&one_up, "../", 0))
+		return 0;
 
-	subpath = relative_path(cwd, one_up, &sb);
+	subpath = relative_path(cwd, one_up.buf, &sb);
+	strbuf_release(&one_up);
 
 	prepare_submodule_repo_env(&cp.env_array);
-	argv_array_pop(&cp.env_array);
+	strvec_pop(&cp.env_array);
 
-	argv_array_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
-			"ls-files", "-z", "--stage", "--full-name", "--",
-			subpath, NULL);
+	strvec_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
+		     "ls-files", "-z", "--stage", "--full-name", "--",
+		     subpath, NULL);
 	strbuf_reset(&sb);
 
 	cp.no_stdin = 1;
@@ -2175,7 +2236,8 @@ const char *get_superproject_working_tree(void)
 		super_wt = xstrdup(cwd);
 		super_wt[cwd_len - super_sub_len] = '\0';
 
-		ret = real_path(super_wt);
+		strbuf_realpath(buf, super_wt, 1);
+		ret = 1;
 		free(super_wt);
 	}
 	strbuf_release(&sb);
@@ -2184,10 +2246,10 @@ const char *get_superproject_working_tree(void)
 
 	if (code == 128)
 		/* '../' is not a git repository */
-		return NULL;
+		return 0;
 	if (code == 0 && len == 0)
 		/* There is an unrelated git repository at '../' */
-		return NULL;
+		return 0;
 	if (code)
 		die(_("ls-tree returned unexpected return code %d"), code);