summaryrefslogtreecommitdiff
path: root/builtin/submodule--helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/submodule--helper.c')
-rw-r--r--builtin/submodule--helper.c269
1 files changed, 242 insertions, 27 deletions
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6ba8587b6d..a3c4564c6c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -12,10 +12,12 @@
#include "run-command.h"
#include "remote.h"
#include "refs.h"
+#include "refspec.h"
#include "connect.h"
#include "revision.h"
#include "diffcore.h"
#include "diff.h"
+#include "object-store.h"
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
@@ -53,7 +55,7 @@ static char *get_default_remote(void)
static int print_default_remote(int argc, const char **argv, const char *prefix)
{
- const char *remote;
+ char *remote;
if (argc != 1)
die(_("submodule--helper print-default-remote takes no arguments"));
@@ -62,6 +64,7 @@ static int print_default_remote(int argc, const char **argv, const char *prefix)
if (remote)
printf("%s\n", remote);
+ free(remote);
return 0;
}
@@ -438,6 +441,149 @@ static void for_each_listed_submodule(const struct module_list *list,
fn(list->entries[i], cb_data);
}
+struct cb_foreach {
+ int argc;
+ const char **argv;
+ const char *prefix;
+ int quiet;
+ int recursive;
+};
+#define CB_FOREACH_INIT { 0 }
+
+static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
+ void *cb_data)
+{
+ struct cb_foreach *info = cb_data;
+ const char *path = list_item->name;
+ const struct object_id *ce_oid = &list_item->oid;
+
+ const struct submodule *sub;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ char *displaypath;
+
+ displaypath = get_submodule_displaypath(path, info->prefix);
+
+ sub = submodule_from_path(the_repository, &null_oid, path);
+
+ if (!sub)
+ die(_("No url found for submodule path '%s' in .gitmodules"),
+ displaypath);
+
+ if (!is_submodule_populated_gently(path, NULL))
+ goto cleanup;
+
+ prepare_submodule_repo_env(&cp.env_array);
+
+ /*
+ * For the purpose of executing <command> in the submodule,
+ * separate shell is used for the purpose of running the
+ * child process.
+ */
+ cp.use_shell = 1;
+ cp.dir = path;
+
+ /*
+ * NEEDSWORK: the command currently has access to the variables $name,
+ * $sm_path, $displaypath, $sha1 and $toplevel only when the command
+ * contains a single argument. This is done for maintaining a faithful
+ * translation from shell script.
+ */
+ if (info->argc == 1) {
+ char *toplevel = xgetcwd();
+ struct strbuf sb = STRBUF_INIT;
+
+ argv_array_pushf(&cp.env_array, "name=%s", sub->name);
+ argv_array_pushf(&cp.env_array, "sm_path=%s", path);
+ argv_array_pushf(&cp.env_array, "displaypath=%s", displaypath);
+ argv_array_pushf(&cp.env_array, "sha1=%s",
+ oid_to_hex(ce_oid));
+ argv_array_pushf(&cp.env_array, "toplevel=%s", toplevel);
+
+ /*
+ * Since the path variable was accessible from the script
+ * before porting, it is also made available after porting.
+ * The environment variable "PATH" has a very special purpose
+ * on windows. And since environment variables are
+ * case-insensitive in windows, it interferes with the
+ * existing PATH variable. Hence, to avoid that, we expose
+ * path via the args argv_array and not via env_array.
+ */
+ sq_quote_buf(&sb, path);
+ argv_array_pushf(&cp.args, "path=%s; %s",
+ sb.buf, info->argv[0]);
+ strbuf_release(&sb);
+ free(toplevel);
+ } else {
+ argv_array_pushv(&cp.args, info->argv);
+ }
+
+ if (!info->quiet)
+ printf(_("Entering '%s'\n"), displaypath);
+
+ if (info->argv[0] && run_command(&cp))
+ die(_("run_command returned non-zero status for %s\n."),
+ displaypath);
+
+ if (info->recursive) {
+ struct child_process cpr = CHILD_PROCESS_INIT;
+
+ cpr.git_cmd = 1;
+ cpr.dir = path;
+ prepare_submodule_repo_env(&cpr.env_array);
+
+ argv_array_pushl(&cpr.args, "--super-prefix", NULL);
+ argv_array_pushf(&cpr.args, "%s/", displaypath);
+ argv_array_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
+ NULL);
+
+ if (info->quiet)
+ argv_array_push(&cpr.args, "--quiet");
+
+ argv_array_pushv(&cpr.args, info->argv);
+
+ if (run_command(&cpr))
+ die(_("run_command returned non-zero status while"
+ "recursing in the nested submodules of %s\n."),
+ displaypath);
+ }
+
+cleanup:
+ free(displaypath);
+}
+
+static int module_foreach(int argc, const char **argv, const char *prefix)
+{
+ struct cb_foreach info = CB_FOREACH_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+
+ struct option module_foreach_options[] = {
+ OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")),
+ OPT_BOOL(0, "recursive", &info.recursive,
+ N_("Recurse into nested submodules")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper foreach [--quiet] [--recursive] <command>"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_foreach_options,
+ git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
+
+ if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.argc = argc;
+ info.argv = argv;
+ info.prefix = prefix;
+
+ for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info);
+
+ return 0;
+}
+
struct init_cb {
const char *prefix;
unsigned int flags;
@@ -454,7 +600,7 @@ static void init_submodule(const char *path, const char *prefix,
displaypath = get_submodule_displaypath(path, prefix);
- sub = submodule_from_path(&null_oid, path);
+ sub = submodule_from_path(the_repository, &null_oid, path);
if (!sub)
die(_("No url found for submodule path '%s' in .gitmodules"),
@@ -595,8 +741,12 @@ static void print_status(unsigned int flags, char state, const char *path,
printf("%c%s %s", state, oid_to_hex(oid), displaypath);
- if (state == ' ' || state == '+')
- printf(" (%s)", compute_rev_name(path, oid_to_hex(oid)));
+ if (state == ' ' || state == '+') {
+ const char *name = compute_rev_name(path, oid_to_hex(oid));
+
+ if (name)
+ printf(" (%s)", name);
+ }
printf("\n");
}
@@ -621,7 +771,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
struct rev_info rev;
int diff_files_result;
- if (!submodule_from_path(&null_oid, path))
+ if (!submodule_from_path(the_repository, &null_oid, path))
die(_("no submodule mapping found in .gitmodules for path '%s'"),
path);
@@ -654,9 +804,13 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
displaypath);
} else if (!(flags & OPT_CACHED)) {
struct object_id oid;
+ struct ref_store *refs = get_submodule_ref_store(path);
- if (refs_head_ref(get_submodule_ref_store(path),
- handle_submodule_head_ref, &oid))
+ if (!refs) {
+ print_status(flags, '-', path, ce_oid, displaypath);
+ goto cleanup;
+ }
+ if (refs_head_ref(refs, handle_submodule_head_ref, &oid))
die(_("could not resolve HEAD ref inside the "
"submodule '%s'"), path);
@@ -741,7 +895,7 @@ static int module_name(int argc, const char **argv, const char *prefix)
if (argc != 2)
usage(_("git submodule--helper name <path>"));
- sub = submodule_from_path(&null_oid, argv[1]);
+ sub = submodule_from_path(the_repository, &null_oid, argv[1]);
if (!sub)
die(_("no submodule mapping found in .gitmodules for path '%s'"),
@@ -772,7 +926,7 @@ static void sync_submodule(const char *path, const char *prefix,
if (!is_submodule_active(the_repository, path))
return;
- sub = submodule_from_path(&null_oid, path);
+ sub = submodule_from_path(the_repository, &null_oid, path);
if (sub && sub->url) {
if (starts_with_dot_dot_slash(sub->url) ||
@@ -925,7 +1079,7 @@ static void deinit_submodule(const char *path, const char *prefix,
struct strbuf sb_config = STRBUF_INIT;
char *sub_git_dir = xstrfmt("%s/.git", path);
- sub = submodule_from_path(&null_oid, path);
+ sub = submodule_from_path(the_repository, &null_oid, path);
if (!sub || !sub->name)
goto cleanup;
@@ -970,6 +1124,8 @@ static void deinit_submodule(const char *path, const char *prefix,
if (!(flags & OPT_QUIET))
printf(format, displaypath);
+ submodule_unset_core_worktree(sub);
+
strbuf_release(&sb_rm);
}
@@ -1056,7 +1212,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix)
}
static int clone_submodule(const char *path, const char *gitdir, const char *url,
- const char *depth, struct string_list *reference,
+ const char *depth, struct string_list *reference, int dissociate,
int quiet, int progress)
{
struct child_process cp = CHILD_PROCESS_INIT;
@@ -1075,6 +1231,8 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
argv_array_pushl(&cp.args, "--reference",
item->string, NULL);
}
+ if (dissociate)
+ argv_array_push(&cp.args, "--dissociate");
if (gitdir && *gitdir)
argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
@@ -1190,6 +1348,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
char *p, *path = NULL, *sm_gitdir;
struct strbuf sb = STRBUF_INIT;
struct string_list reference = STRING_LIST_INIT_NODUP;
+ int dissociate = 0;
char *sm_alternate = NULL, *error_strategy = NULL;
struct option module_clone_options[] = {
@@ -1208,6 +1367,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
OPT_STRING_LIST(0, "reference", &reference,
N_("repo"),
N_("reference repository")),
+ OPT_BOOL(0, "dissociate", &dissociate,
+ N_("use --reference only while cloning")),
OPT_STRING(0, "depth", &depth,
N_("string"),
N_("depth for shallow clones")),
@@ -1247,7 +1408,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
prepare_possible_alternates(name, &reference);
- if (clone_submodule(path, sm_gitdir, url, depth, &reference,
+ if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate,
quiet, progress))
die(_("clone of '%s' into submodule path '%s' failed"),
url, path);
@@ -1259,8 +1420,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
strbuf_reset(&sb);
}
- /* Connect module worktree and git dir */
- connect_work_tree_and_git_dir(path, sm_gitdir);
+ connect_work_tree_and_git_dir(path, sm_gitdir, 0);
p = git_pathdup_submodule(path, "config");
if (!p)
@@ -1300,6 +1460,7 @@ struct submodule_update_clone {
int quiet;
int recommend_shallow;
struct string_list references;
+ int dissociate;
const char *depth;
const char *recursive_prefix;
const char *prefix;
@@ -1315,7 +1476,7 @@ struct submodule_update_clone {
int failed_clones_nr, failed_clones_alloc;
};
#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
- SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \
+ SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
NULL, NULL, NULL, \
STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
@@ -1367,7 +1528,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
goto cleanup;
}
- sub = submodule_from_path(&null_oid, ce->name);
+ sub = submodule_from_path(the_repository, &null_oid, ce->name);
if (suc->recursive_prefix)
displaypath = relative_path(suc->recursive_prefix,
@@ -1442,6 +1603,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
for_each_string_list_item(item, &suc->references)
argv_array_pushl(&child->args, "--reference", item->string, NULL);
}
+ if (suc->dissociate)
+ argv_array_push(&child->args, "--dissociate");
if (suc->depth)
argv_array_push(&child->args, suc->depth);
@@ -1545,8 +1708,8 @@ static int update_clone_task_finished(int result,
return 0;
}
-static int gitmodules_update_clone_config(const char *var, const char *value,
- void *cb)
+static int git_update_clone_config(const char *var, const char *value,
+ void *cb)
{
int *max_jobs = cb;
if (!strcmp(var, "submodule.fetchjobs"))
@@ -1575,6 +1738,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
N_("rebase, merge, checkout or none")),
OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"),
N_("reference repository")),
+ OPT_BOOL(0, "dissociate", &suc.dissociate,
+ N_("use --reference only while cloning")),
OPT_STRING(0, "depth", &suc.depth, "<depth>",
N_("Create a shallow clone truncated to the "
"specified number of revisions")),
@@ -1594,8 +1759,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
};
suc.prefix = prefix;
- config_from_gitmodules(gitmodules_update_clone_config, &max_jobs);
- git_config(gitmodules_update_clone_config, &max_jobs);
+ update_clone_config_from_gitmodules(&max_jobs);
+ git_config(git_update_clone_config, &max_jobs);
argc = parse_options(argc, argv, prefix, module_update_clone_options,
git_submodule_helper_usage, 0);
@@ -1650,7 +1815,7 @@ static const char *remote_submodule_branch(const char *path)
const char *branch = NULL;
char *key;
- sub = submodule_from_path(&null_oid, path);
+ sub = submodule_from_path(the_repository, &null_oid, path);
if (!sub)
return NULL;
@@ -1735,13 +1900,14 @@ static int push_check(int argc, const char **argv, const char *prefix)
/* Check the refspec */
if (argc > 2) {
- int i, refspec_nr = argc - 2;
+ int i;
struct ref *local_refs = get_local_heads();
- struct refspec *refspec = parse_push_refspec(refspec_nr,
- argv + 2);
+ struct refspec refspec = REFSPEC_INIT_PUSH;
- for (i = 0; i < refspec_nr; i++) {
- struct refspec *rs = refspec + i;
+ refspec_appendn(&refspec, argv + 2, argc - 2);
+
+ for (i = 0; i < refspec.nr; i++) {
+ const struct refspec_item *rs = &refspec.items[i];
if (rs->pattern || rs->matching)
continue;
@@ -1768,7 +1934,7 @@ static int push_check(int argc, const char **argv, const char *prefix)
rs->src);
}
}
- free_refspec(refspec_nr, refspec);
+ refspec_clear(&refspec);
}
free(head);
@@ -1817,6 +1983,52 @@ static int is_active(int argc, const char **argv, const char *prefix)
return !is_submodule_active(the_repository, argv[1]);
}
+/*
+ * Exit non-zero if any of the submodule names given on the command line is
+ * invalid. If no names are given, filter stdin to print only valid names
+ * (which is primarily intended for testing).
+ */
+static int check_name(int argc, const char **argv, const char *prefix)
+{
+ if (argc > 1) {
+ while (*++argv) {
+ if (check_submodule_name(*argv) < 0)
+ return 1;
+ }
+ } else {
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (!check_submodule_name(buf.buf))
+ printf("%s\n", buf.buf);
+ }
+ strbuf_release(&buf);
+ }
+ return 0;
+}
+
+static int connect_gitdir_workingtree(int argc, const char **argv, const char *prefix)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char *name, *path;
+ char *sm_gitdir;
+
+ if (argc != 3)
+ BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
+
+ name = argv[1];
+ path = argv[2];
+
+ strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
+ sm_gitdir = absolute_pathdup(sb.buf);
+
+ connect_work_tree_and_git_dir(path, sm_gitdir, 0);
+
+ strbuf_release(&sb);
+ free(sm_gitdir);
+
+ return 0;
+}
+
#define SUPPORT_SUPER_PREFIX (1<<0)
struct cmd_struct {
@@ -1830,9 +2042,11 @@ static struct cmd_struct commands[] = {
{"name", module_name, 0},
{"clone", module_clone, 0},
{"update-clone", update_clone, 0},
+ {"connect-gitdir-workingtree", connect_gitdir_workingtree, 0},
{"relative-path", resolve_relative_path, 0},
{"resolve-relative-url", resolve_relative_url, 0},
{"resolve-relative-url-test", resolve_relative_url_test, 0},
+ {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
{"init", module_init, SUPPORT_SUPER_PREFIX},
{"status", module_status, SUPPORT_SUPER_PREFIX},
{"print-default-remote", print_default_remote, 0},
@@ -1842,6 +2056,7 @@ static struct cmd_struct commands[] = {
{"push-check", push_check, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
{"is-active", is_active, 0},
+ {"check-name", check_name, 0},
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)