diff options
Diffstat (limited to 'builtin/submodule--helper.c')
-rw-r--r-- | builtin/submodule--helper.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a5c4a8a694..46dee6bf5e 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -718,6 +718,156 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +struct cb_foreach { + int argc; + const char **argv; + const char *prefix; + unsigned int flags; +}; +#define CB_FOREACH_INIT { 0, NULL, NULL, 0 } + +static void runcommand_in_submodule(const char *path, const struct object_id *ce_oid, + int argc, const char **argv, const char *prefix, + unsigned int flags) +{ + const struct submodule *sub; + struct child_process cp = CHILD_PROCESS_INIT; + char *displaypath; + + displaypath = get_submodule_displaypath(path, prefix); + + sub = submodule_from_path(&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 maintianing a faithful + * translation from shell script. + */ + if (argc == 1) { + char *toplevel = xgetcwd(); + + 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. + */ + argv_array_pushf(&cp.args, "path=%s; %s", + path, argv[0]); + free(toplevel); + } else { + argv_array_pushv(&cp.args, argv); + } + + if (!(flags & OPT_QUIET)) + printf(_("Entering '%s'\n"), displaypath); + + if (argv[0] && run_command(&cp)) + die(_("run_command returned non-zero status for %s\n."), + displaypath); + + if (flags & OPT_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 (flags & OPT_QUIET) + argv_array_push(&cpr.args, "--quiet"); + + argv_array_pushv(&cpr.args, 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 void runcommand_in_submodule_cb(const struct cache_entry *list_item, + void *cb_data) +{ + struct cb_foreach *info = cb_data; + runcommand_in_submodule(list_item->name, &list_item->oid, info->argc, + info->argv, info->prefix, info->flags); +} + +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; + int quiet = 0; + int recursive = 0; + + struct option module_foreach_options[] = { + OPT__QUIET(&quiet, N_("Suppress output of entering each submodule command")), + OPT_BOOL(0, "recursive", &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) + BUG("module_list_compute should not choke on empty pathspec"); + + info.argc = argc; + info.argv = argv; + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + if (recursive) + info.flags |= OPT_RECURSIVE; + + for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info); + + return 0; +} + static int clone_submodule(const char *path, const char *gitdir, const char *url, const char *depth, struct string_list *reference, int quiet, int progress) @@ -1496,6 +1646,7 @@ static struct cmd_struct commands[] = { {"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}, {"remote-branch", resolve_remote_submodule_branch, 0}, |