diff options
Diffstat (limited to 'builtin/submodule--helper.c')
-rw-r--r-- | builtin/submodule--helper.c | 346 |
1 files changed, 306 insertions, 40 deletions
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 1902b6c319..8e1db55e13 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -55,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")); @@ -64,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; } @@ -330,7 +331,7 @@ static int module_list_compute(int argc, const char **argv, for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; - if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), + if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce), 0, ps_matched, 1) || !S_ISGITLINK(ce->ce_mode)) continue; @@ -440,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; @@ -648,7 +792,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, path, NULL); git_config(git_diff_basic_config, NULL); - init_revisions(&rev, prefix); + repo_init_revisions(the_repository, &rev, prefix); rev.abbrev = 0; diff_files_args.argc = setup_revisions(diff_files_args.argc, diff_files_args.argv, @@ -880,7 +1024,6 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data { struct sync_cb *info = cb_data; sync_submodule(list_item->name, info->prefix, info->flags); - } static int module_sync(int argc, const char **argv, const char *prefix) @@ -1301,6 +1444,70 @@ static int module_clone(int argc, const char **argv, const char *prefix) return 0; } +static void determine_submodule_update_strategy(struct repository *r, + int just_cloned, + const char *path, + const char *update, + struct submodule_update_strategy *out) +{ + const struct submodule *sub = submodule_from_path(r, &null_oid, path); + char *key; + const char *val; + + key = xstrfmt("submodule.%s.update", sub->name); + + if (update) { + if (parse_submodule_update_strategy(update, out) < 0) + die(_("Invalid update mode '%s' for submodule path '%s'"), + update, path); + } else if (!repo_config_get_string_const(r, key, &val)) { + if (parse_submodule_update_strategy(val, out) < 0) + die(_("Invalid update mode '%s' configured for submodule path '%s'"), + val, path); + } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { + out->type = sub->update_strategy.type; + out->command = sub->update_strategy.command; + } else + out->type = SM_UPDATE_CHECKOUT; + + if (just_cloned && + (out->type == SM_UPDATE_MERGE || + out->type == SM_UPDATE_REBASE || + out->type == SM_UPDATE_NONE)) + out->type = SM_UPDATE_CHECKOUT; + + free(key); +} + +static int module_update_module_mode(int argc, const char **argv, const char *prefix) +{ + const char *path, *update = NULL; + int just_cloned; + struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT }; + + if (argc < 3 || argc > 4) + die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]"); + + just_cloned = git_config_int("just_cloned", argv[1]); + path = argv[2]; + + if (argc == 4) + update = argv[3]; + + determine_submodule_update_strategy(the_repository, + just_cloned, path, update, + &update_strategy); + fputs(submodule_strategy_to_string(&update_strategy), stdout); + + return 0; +} + +struct update_clone_data { + const struct submodule *sub; + struct object_id oid; + unsigned just_cloned; +}; + struct submodule_update_clone { /* index into 'list', the list of submodules to look into for cloning */ int current; @@ -1320,8 +1527,9 @@ struct submodule_update_clone { const char *recursive_prefix; const char *prefix; - /* Machine-readable status lines to be consumed by git-submodule.sh */ - struct string_list projectlines; + /* to be consumed by git-submodule.sh */ + struct update_clone_data *update_clone; + int update_clone_nr; int update_clone_alloc; /* If we want to stop as fast as possible and return an error */ unsigned quickstop : 1; @@ -1329,11 +1537,13 @@ struct submodule_update_clone { /* failed clones to be retried again */ const struct cache_entry **failed_clones; int failed_clones_nr, failed_clones_alloc; + + int max_jobs; }; #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \ SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \ NULL, NULL, NULL, \ - STRING_LIST_INIT_DUP, 0, NULL, 0, 0} + NULL, 0, 0, 0, NULL, 0, 0, 0} static void next_submodule_warn_missing(struct submodule_update_clone *suc, @@ -1427,11 +1637,12 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, strbuf_addf(&sb, "%s/.git", ce->name); needs_cloning = !file_exists(sb.buf); - strbuf_reset(&sb); - strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode, - oid_to_hex(&ce->oid), ce_stage(ce), - needs_cloning, ce->name); - string_list_append(&suc->projectlines, sb.buf); + ALLOC_GROW(suc->update_clone, suc->update_clone_nr + 1, + suc->update_clone_alloc); + oidcpy(&suc->update_clone[suc->update_clone_nr].oid, &ce->oid); + suc->update_clone[suc->update_clone_nr].just_cloned = needs_cloning; + suc->update_clone[suc->update_clone_nr].sub = sub; + suc->update_clone_nr++; if (!needs_cloning) goto cleanup; @@ -1563,8 +1774,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")) @@ -1572,11 +1783,44 @@ static int gitmodules_update_clone_config(const char *var, const char *value, return 0; } +static void update_submodule(struct update_clone_data *ucd) +{ + fprintf(stdout, "dummy %s %d\t%s\n", + oid_to_hex(&ucd->oid), + ucd->just_cloned, + ucd->sub->path); +} + +static int update_submodules(struct submodule_update_clone *suc) +{ + int i; + + run_processes_parallel(suc->max_jobs, + update_clone_get_next_task, + update_clone_start_failure, + update_clone_task_finished, + suc); + + /* + * We saved the output and put it out all at once now. + * That means: + * - the listener does not have to interleave their (checkout) + * work with our fetching. The writes involved in a + * checkout involve more straightforward sequential I/O. + * - the listener can avoid doing any work if fetching failed. + */ + if (suc->quickstop) + return 1; + + for (i = 0; i < suc->update_clone_nr; i++) + update_submodule(&suc->update_clone[i]); + + return 0; +} + static int update_clone(int argc, const char **argv, const char *prefix) { const char *update = NULL; - int max_jobs = 1; - struct string_list_item *item; struct pathspec pathspec; struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT; @@ -1598,7 +1842,7 @@ static int update_clone(int argc, const char **argv, const char *prefix) OPT_STRING(0, "depth", &suc.depth, "<depth>", N_("Create a shallow clone truncated to the " "specified number of revisions")), - OPT_INTEGER('j', "jobs", &max_jobs, + OPT_INTEGER('j', "jobs", &suc.max_jobs, N_("parallel jobs")), OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow, N_("whether the initial clone should follow the shallow recommendation")), @@ -1614,8 +1858,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(&suc.max_jobs); + git_config(git_update_clone_config, &suc.max_jobs); argc = parse_options(argc, argv, prefix, module_update_clone_options, git_submodule_helper_usage, 0); @@ -1630,27 +1874,7 @@ static int update_clone(int argc, const char **argv, const char *prefix) if (pathspec.nr) suc.warn_if_uninitialized = 1; - run_processes_parallel(max_jobs, - update_clone_get_next_task, - update_clone_start_failure, - update_clone_task_finished, - &suc); - - /* - * We saved the output and put it out all at once now. - * That means: - * - the listener does not have to interleave their (checkout) - * work with our fetching. The writes involved in a - * checkout involve more straightforward sequential I/O. - * - the listener can avoid doing any work if fetching failed. - */ - if (suc.quickstop) - return 1; - - for_each_string_list_item(item, &suc.projectlines) - fprintf(stdout, "%s", item->string); - - return 0; + return update_submodules(&suc); } static int resolve_relative_path(int argc, const char **argv, const char *prefix) @@ -1796,6 +2020,45 @@ static int push_check(int argc, const char **argv, const char *prefix) return 0; } +static int ensure_core_worktree(int argc, const char **argv, const char *prefix) +{ + const struct submodule *sub; + const char *path; + char *cw; + struct repository subrepo; + + if (argc != 2) + BUG("submodule--helper connect-gitdir-workingtree <name> <path>"); + + path = argv[1]; + + sub = submodule_from_path(the_repository, &null_oid, path); + if (!sub) + BUG("We could get the submodule handle before?"); + + if (repo_submodule_init(&subrepo, the_repository, path)) + die(_("could not get a repository handle for submodule '%s'"), path); + + if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) { + char *cfg_file, *abs_path; + const char *rel_path; + struct strbuf sb = STRBUF_INIT; + + cfg_file = repo_git_path(&subrepo, "config"); + + abs_path = absolute_pathdup(path); + rel_path = relative_path(abs_path, subrepo.gitdir, &sb); + + git_config_set_in_file(cfg_file, "core.worktree", rel_path); + + free(cfg_file); + free(abs_path); + strbuf_release(&sb); + } + + return 0; +} + static int absorb_git_dirs(int argc, const char **argv, const char *prefix) { int i; @@ -1873,10 +2136,13 @@ static struct cmd_struct commands[] = { {"list", module_list, 0}, {"name", module_name, 0}, {"clone", module_clone, 0}, + {"update-module-mode", module_update_module_mode, 0}, {"update-clone", update_clone, 0}, + {"ensure-core-worktree", ensure_core_worktree, 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}, |