diff options
Diffstat (limited to 'builtin/submodule--helper.c')
-rw-r--r-- | builtin/submodule--helper.c | 539 |
1 files changed, 446 insertions, 93 deletions
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 899dc334e3..2086f0eb08 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1,10 +1,11 @@ #include "builtin.h" +#include "repository.h" #include "cache.h" +#include "config.h" #include "parse-options.h" #include "quote.h" #include "pathspec.h" #include "dir.h" -#include "utf8.h" #include "submodule.h" #include "submodule-config.h" #include "string-list.h" @@ -12,13 +13,22 @@ #include "remote.h" #include "refs.h" #include "connect.h" +#include "revision.h" +#include "diffcore.h" +#include "diff.h" + +#define OPT_QUIET (1 << 0) +#define OPT_CACHED (1 << 1) +#define OPT_RECURSIVE (1 << 2) + +typedef void (*each_submodule_fn)(const struct cache_entry *list_item, + void *cb_data); static char *get_default_remote(void) { char *dest = NULL, *ret; - unsigned char sha1[20]; struct strbuf sb = STRBUF_INIT; - const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL); + const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL); if (!refname) die(_("No such ref: %s"), "HEAD"); @@ -219,6 +229,64 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr return 0; } +/* the result should be freed by the caller. */ +static char *get_submodule_displaypath(const char *path, const char *prefix) +{ + const char *super_prefix = get_super_prefix(); + + if (prefix && super_prefix) { + BUG("cannot have prefix '%s' and superprefix '%s'", + prefix, super_prefix); + } else if (prefix) { + struct strbuf sb = STRBUF_INIT; + char *displaypath = xstrdup(relative_path(path, prefix, &sb)); + strbuf_release(&sb); + return displaypath; + } else if (super_prefix) { + return xstrfmt("%s%s", super_prefix, path); + } else { + return xstrdup(path); + } +} + +static char *compute_rev_name(const char *sub_path, const char* object_id) +{ + struct strbuf sb = STRBUF_INIT; + const char ***d; + + static const char *describe_bare[] = { NULL }; + + static const char *describe_tags[] = { "--tags", NULL }; + + static const char *describe_contains[] = { "--contains", NULL }; + + static const char *describe_all_always[] = { "--all", "--always", NULL }; + + static const char **describe_argv[] = { describe_bare, describe_tags, + describe_contains, + describe_all_always, NULL }; + + for (d = describe_argv; *d; d++) { + struct child_process cp = CHILD_PROCESS_INIT; + prepare_submodule_repo_env(&cp.env_array); + cp.dir = sub_path; + cp.git_cmd = 1; + cp.no_stderr = 1; + + argv_array_push(&cp.args, "describe"); + argv_array_pushv(&cp.args, *d); + argv_array_push(&cp.args, object_id); + + if (!capture_command(&cp, &sb, 0)) { + strbuf_strip_suffix(&sb, "\n"); + return strbuf_detach(&sb, NULL); + } + } + + strbuf_release(&sb); + return NULL; +} + struct module_list { const struct cache_entry **entries; int alloc, nr; @@ -233,8 +301,7 @@ static int module_list_compute(int argc, const char **argv, int i, result = 0; char *ps_matched = NULL; parse_pathspec(pathspec, 0, - PATHSPEC_PREFER_FULL | - PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, + PATHSPEC_PREFER_FULL, prefix, argv); if (pathspec->nr) @@ -270,6 +337,27 @@ static int module_list_compute(int argc, const char **argv, return result; } +static void module_list_active(struct module_list *list) +{ + int i; + struct module_list active_modules = MODULE_LIST_INIT; + + for (i = 0; i < list->nr; i++) { + const struct cache_entry *ce = list->entries[i]; + + if (!is_submodule_active(the_repository, ce->name)) + continue; + + ALLOC_GROW(active_modules.entries, + active_modules.nr + 1, + active_modules.alloc); + active_modules.entries[active_modules.nr++] = ce; + } + + free(list->entries); + *list = active_modules; +} + static int module_list(int argc, const char **argv, const char *prefix) { int i; @@ -303,50 +391,66 @@ static int module_list(int argc, const char **argv, const char *prefix) printf("%06o %s %d\t", ce->ce_mode, oid_to_hex(&ce->oid), ce_stage(ce)); - utf8_fprintf(stdout, "%s\n", ce->name); + fprintf(stdout, "%s\n", ce->name); } return 0; } -static void init_submodule(const char *path, const char *prefix, int quiet) +static void for_each_listed_submodule(const struct module_list *list, + each_submodule_fn fn, void *cb_data) +{ + int i; + for (i = 0; i < list->nr; i++) + fn(list->entries[i], cb_data); +} + +struct init_cb { + const char *prefix; + unsigned int flags; +}; + +#define INIT_CB_INIT { NULL, 0 } + +static void init_submodule(const char *path, const char *prefix, + unsigned int flags) { const struct submodule *sub; struct strbuf sb = STRBUF_INIT; char *upd = NULL, *url = NULL, *displaypath; - /* Only loads from .gitmodules, no overlay with .git/config */ - gitmodules_config(); - - if (prefix && get_super_prefix()) - die("BUG: cannot have prefix and superprefix"); - else if (prefix) - displaypath = xstrdup(relative_path(path, prefix, &sb)); - else if (get_super_prefix()) { - strbuf_addf(&sb, "%s%s", get_super_prefix(), path); - displaypath = strbuf_detach(&sb, NULL); - } else - displaypath = xstrdup(path); + displaypath = get_submodule_displaypath(path, prefix); - sub = submodule_from_path(null_sha1, path); + sub = submodule_from_path(&null_oid, path); if (!sub) die(_("No url found for submodule path '%s' in .gitmodules"), displaypath); /* + * NEEDSWORK: In a multi-working-tree world, this needs to be + * set in the per-worktree config. + * + * Set active flag for the submodule being initialized + */ + if (!is_submodule_active(the_repository, path)) { + strbuf_addf(&sb, "submodule.%s.active", sub->name); + git_config_set_gently(sb.buf, "true"); + strbuf_reset(&sb); + } + + /* * Copy url setting when it is not set yet. * To look up the url in .git/config, we must not fall back to * .gitmodules, so look it up directly. */ - strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.url", sub->name); if (git_config_get_string(sb.buf, &url)) { - url = xstrdup(sub->url); - - if (!url) + if (!sub->url) die(_("No url found for submodule path '%s' in .gitmodules"), displaypath); + url = xstrdup(sub->url); + /* Possibly a url relative to parent */ if (starts_with_dot_dot_slash(url) || starts_with_dot_slash(url)) { @@ -356,12 +460,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet) strbuf_addf(&remotesb, "remote.%s.url", remote); free(remote); - if (git_config_get_string(remotesb.buf, &remoteurl)) - /* - * The repository is its own - * authoritative upstream - */ + if (git_config_get_string(remotesb.buf, &remoteurl)) { + warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf); remoteurl = xgetcwd(); + } relurl = relative_url(remoteurl, url, NULL); strbuf_release(&remotesb); free(remoteurl); @@ -372,14 +474,14 @@ static void init_submodule(const char *path, const char *prefix, int quiet) if (git_config_set_gently(sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); - if (!quiet) + if (!(flags & OPT_QUIET)) fprintf(stderr, _("Submodule '%s' (%s) registered for path '%s'\n"), sub->name, url, displaypath); } + strbuf_reset(&sb); /* Copy "update" setting when it is not set yet */ - strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.update", sub->name); if (git_config_get_string(sb.buf, &upd) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { @@ -399,12 +501,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet) free(upd); } +static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) +{ + struct init_cb *info = cb_data; + init_submodule(list_item->name, info->prefix, info->flags); +} + static int module_init(int argc, const char **argv, const char *prefix) { + struct init_cb info = INIT_CB_INIT; struct pathspec pathspec; struct module_list list = MODULE_LIST_INIT; int quiet = 0; - int i; struct option module_init_options[] = { OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")), @@ -422,8 +530,172 @@ static int module_init(int argc, const char **argv, const char *prefix) if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) return 1; - for (i = 0; i < list.nr; i++) - init_submodule(list.entries[i]->name, prefix, quiet); + /* + * If there are no path args and submodule.active is set then, + * by default, only initialize 'active' modules. + */ + if (!argc && git_config_get_value_multi("submodule.active")) + module_list_active(&list); + + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + + for_each_listed_submodule(&list, init_submodule_cb, &info); + + return 0; +} + +struct status_cb { + const char *prefix; + unsigned int flags; +}; + +#define STATUS_CB_INIT { NULL, 0 } + +static void print_status(unsigned int flags, char state, const char *path, + const struct object_id *oid, const char *displaypath) +{ + if (flags & OPT_QUIET) + return; + + printf("%c%s %s", state, oid_to_hex(oid), displaypath); + + if (state == ' ' || state == '+') + printf(" (%s)", compute_rev_name(path, oid_to_hex(oid))); + + printf("\n"); +} + +static int handle_submodule_head_ref(const char *refname, + const struct object_id *oid, int flags, + void *cb_data) +{ + struct object_id *output = cb_data; + if (oid) + oidcpy(output, oid); + + return 0; +} + +static void status_submodule(const char *path, const struct object_id *ce_oid, + unsigned int ce_flags, const char *prefix, + unsigned int flags) +{ + char *displaypath; + struct argv_array diff_files_args = ARGV_ARRAY_INIT; + struct rev_info rev; + int diff_files_result; + + if (!submodule_from_path(&null_oid, path)) + die(_("no submodule mapping found in .gitmodules for path '%s'"), + path); + + displaypath = get_submodule_displaypath(path, prefix); + + if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) { + print_status(flags, 'U', path, &null_oid, displaypath); + goto cleanup; + } + + if (!is_submodule_active(the_repository, path)) { + print_status(flags, '-', path, ce_oid, displaypath); + goto cleanup; + } + + argv_array_pushl(&diff_files_args, "diff-files", + "--ignore-submodules=dirty", "--quiet", "--", + path, NULL); + + git_config(git_diff_basic_config, NULL); + init_revisions(&rev, prefix); + rev.abbrev = 0; + diff_files_args.argc = setup_revisions(diff_files_args.argc, + diff_files_args.argv, + &rev, NULL); + diff_files_result = run_diff_files(&rev, 0); + + if (!diff_result_code(&rev.diffopt, diff_files_result)) { + print_status(flags, ' ', path, ce_oid, + displaypath); + } else if (!(flags & OPT_CACHED)) { + struct object_id oid; + + if (refs_head_ref(get_submodule_ref_store(path), + handle_submodule_head_ref, &oid)) + die(_("could not resolve HEAD ref inside the" + "submodule '%s'"), path); + + print_status(flags, '+', path, &oid, displaypath); + } else { + print_status(flags, '+', path, ce_oid, 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_push(&cpr.args, "--super-prefix"); + argv_array_pushf(&cpr.args, "%s/", displaypath); + argv_array_pushl(&cpr.args, "submodule--helper", "status", + "--recursive", NULL); + + if (flags & OPT_CACHED) + argv_array_push(&cpr.args, "--cached"); + + if (flags & OPT_QUIET) + argv_array_push(&cpr.args, "--quiet"); + + if (run_command(&cpr)) + die(_("failed to recurse into submodule '%s'"), path); + } + +cleanup: + argv_array_clear(&diff_files_args); + free(displaypath); +} + +static void status_submodule_cb(const struct cache_entry *list_item, + void *cb_data) +{ + struct status_cb *info = cb_data; + status_submodule(list_item->name, &list_item->oid, list_item->ce_flags, + info->prefix, info->flags); +} + +static int module_status(int argc, const char **argv, const char *prefix) +{ + struct status_cb info = STATUS_CB_INIT; + struct pathspec pathspec; + struct module_list list = MODULE_LIST_INIT; + int quiet = 0; + + struct option module_status_options[] = { + OPT__QUIET(&quiet, N_("Suppress submodule status output")), + OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED), + OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE), + OPT_END() + }; + + const char *const git_submodule_helper_usage[] = { + N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"), + NULL + }; + + argc = parse_options(argc, argv, prefix, module_status_options, + git_submodule_helper_usage, 0); + + if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) + return 1; + + info.prefix = prefix; + if (quiet) + info.flags |= OPT_QUIET; + + for_each_listed_submodule(&list, status_submodule_cb, &info); return 0; } @@ -435,8 +707,7 @@ static int module_name(int argc, const char **argv, const char *prefix) if (argc != 2) usage(_("git submodule--helper name <path>")); - gitmodules_config(); - sub = submodule_from_path(null_sha1, argv[1]); + sub = submodule_from_path(&null_oid, argv[1]); if (!sub) die(_("no submodule mapping found in .gitmodules for path '%s'"), @@ -579,9 +850,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) const char *name = NULL, *url = NULL, *depth = NULL; int quiet = 0; int progress = 0; - FILE *submodule_dot_git; char *p, *path = NULL, *sm_gitdir; - struct strbuf rel_path = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; struct string_list reference = STRING_LIST_INIT_NODUP; char *sm_alternate = NULL, *error_strategy = NULL; @@ -653,27 +922,12 @@ static int module_clone(int argc, const char **argv, const char *prefix) strbuf_reset(&sb); } - /* Write a .git file in the submodule to redirect to the superproject. */ - strbuf_addf(&sb, "%s/.git", path); - if (safe_create_leading_directories_const(sb.buf) < 0) - die(_("could not create leading directories of '%s'"), sb.buf); - submodule_dot_git = fopen(sb.buf, "w"); - if (!submodule_dot_git) - die_errno(_("cannot open file '%s'"), sb.buf); - - fprintf_or_die(submodule_dot_git, "gitdir: %s\n", - relative_path(sm_gitdir, path, &rel_path)); - if (fclose(submodule_dot_git)) - die(_("could not close file %s"), sb.buf); - strbuf_reset(&sb); - strbuf_reset(&rel_path); + /* Connect module worktree and git dir */ + connect_work_tree_and_git_dir(path, sm_gitdir); - /* Redirect the worktree of the submodule in the superproject's config */ p = git_pathdup_submodule(path, "config"); if (!p) die(_("could not get submodule directory for '%s'"), path); - git_config_set_in_file(p, "core.worktree", - relative_path(path, sm_gitdir, &rel_path)); /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */ git_config_get_string("submodule.alternateLocation", &sm_alternate); @@ -689,7 +943,6 @@ static int module_clone(int argc, const char **argv, const char *prefix) free(error_strategy); strbuf_release(&sb); - strbuf_release(&rel_path); free(sm_gitdir); free(path); free(p); @@ -758,10 +1011,13 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, struct strbuf *out) { const struct submodule *sub = NULL; + const char *url = NULL; + const char *update_string; + enum submodule_update_type update_type; + char *key; struct strbuf displaypath_sb = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; const char *displaypath = NULL; - char *url = NULL; int needs_cloning = 0; if (ce_stage(ce)) { @@ -774,7 +1030,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, goto cleanup; } - sub = submodule_from_path(null_sha1, ce->name); + sub = submodule_from_path(&null_oid, ce->name); if (suc->recursive_prefix) displaypath = relative_path(suc->recursive_prefix, @@ -787,28 +1043,34 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, goto cleanup; } + key = xstrfmt("submodule.%s.update", sub->name); + if (!repo_config_get_string_const(the_repository, key, &update_string)) { + update_type = parse_submodule_update_type(update_string); + } else { + update_type = sub->update_strategy.type; + } + free(key); + if (suc->update.type == SM_UPDATE_NONE || (suc->update.type == SM_UPDATE_UNSPECIFIED - && sub->update_strategy.type == SM_UPDATE_NONE)) { + && update_type == SM_UPDATE_NONE)) { strbuf_addf(out, _("Skipping submodule '%s'"), displaypath); strbuf_addch(out, '\n'); goto cleanup; } - /* - * Looking up the url in .git/config. - * We must not fall back to .gitmodules as we only want - * to process configured submodules. - */ - strbuf_reset(&sb); - strbuf_addf(&sb, "submodule.%s.url", sub->name); - git_config_get_string(sb.buf, &url); - if (!url) { + /* Check if the submodule has been initialized. */ + if (!is_submodule_active(the_repository, ce->name)) { next_submodule_warn_missing(suc, out, displaypath); goto cleanup; } strbuf_reset(&sb); + strbuf_addf(&sb, "submodule.%s.url", sub->name); + if (repo_config_get_string_const(the_repository, sb.buf, &url)) + url = sub->url; + + strbuf_reset(&sb); strbuf_addf(&sb, "%s/.git", ce->name); needs_cloning = !file_exists(sb.buf); @@ -847,7 +1109,6 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, argv_array_push(&child->args, suc->depth); cleanup: - free(url); strbuf_reset(&displaypath_sb); strbuf_reset(&sb); @@ -917,7 +1178,7 @@ static int update_clone_task_finished(int result, const struct cache_entry *ce; struct submodule_update_clone *suc = suc_cb; - int *idxP = *(int**)idx_task_cb; + int *idxP = idx_task_cb; int idx = *idxP; free(idxP); @@ -947,10 +1208,19 @@ static int update_clone_task_finished(int result, return 0; } +static int gitmodules_update_clone_config(const char *var, const char *value, + void *cb) +{ + int *max_jobs = cb; + if (!strcmp(var, "submodule.fetchjobs")) + *max_jobs = parse_submodule_fetchjobs(var, value); + return 0; +} + static int update_clone(int argc, const char **argv, const char *prefix) { const char *update = NULL; - int max_jobs = -1; + int max_jobs = 1; struct string_list_item *item; struct pathspec pathspec; struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT; @@ -987,6 +1257,9 @@ 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); + argc = parse_options(argc, argv, prefix, module_update_clone_options, git_submodule_helper_usage, 0); @@ -1000,13 +1273,6 @@ static int update_clone(int argc, const char **argv, const char *prefix) if (pathspec.nr) suc.warn_if_uninitialized = 1; - /* Overlay the parsed .gitmodules file with .git/config */ - gitmodules_config(); - git_config(submodule_config, NULL); - - if (max_jobs < 0) - max_jobs = parallel_submodules(); - run_processes_parallel(max_jobs, update_clone_get_next_task, update_clone_start_failure, @@ -1025,7 +1291,7 @@ static int update_clone(int argc, const char **argv, const char *prefix) return 1; for_each_string_list_item(item, &suc.projectlines) - utf8_fprintf(stdout, "%s", item->string); + fprintf(stdout, "%s", item->string); return 0; } @@ -1044,19 +1310,23 @@ static int resolve_relative_path(int argc, const char **argv, const char *prefix static const char *remote_submodule_branch(const char *path) { const struct submodule *sub; - gitmodules_config(); - git_config(submodule_config, NULL); + const char *branch = NULL; + char *key; - sub = submodule_from_path(null_sha1, path); + sub = submodule_from_path(&null_oid, path); if (!sub) return NULL; - if (!sub->branch) + key = xstrfmt("submodule.%s.branch", sub->name); + if (repo_config_get_string_const(the_repository, key, &branch)) + branch = sub->branch; + free(key); + + if (!branch) return "master"; - if (!strcmp(sub->branch, ".")) { - unsigned char sha1[20]; - const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL); + if (!strcmp(branch, ".")) { + const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL); if (!refname) die(_("No such ref: %s"), "HEAD"); @@ -1072,7 +1342,7 @@ static const char *remote_submodule_branch(const char *path) return refname; } - return sub->branch; + return branch; } static int resolve_remote_submodule_branch(int argc, const char **argv, @@ -1092,6 +1362,82 @@ static int resolve_remote_submodule_branch(int argc, const char **argv, return 0; } +static int push_check(int argc, const char **argv, const char *prefix) +{ + struct remote *remote; + const char *superproject_head; + char *head; + int detached_head = 0; + struct object_id head_oid; + + if (argc < 3) + die("submodule--helper push-check requires at least 2 arguments"); + + /* + * superproject's resolved head ref. + * if HEAD then the superproject is in a detached head state, otherwise + * it will be the resolved head ref. + */ + superproject_head = argv[1]; + argv++; + argc--; + /* Get the submodule's head ref and determine if it is detached */ + head = resolve_refdup("HEAD", 0, &head_oid, NULL); + if (!head) + die(_("Failed to resolve HEAD as a valid ref.")); + if (!strcmp(head, "HEAD")) + detached_head = 1; + + /* + * The remote must be configured. + * This is to avoid pushing to the exact same URL as the parent. + */ + remote = pushremote_get(argv[1]); + if (!remote || remote->origin == REMOTE_UNCONFIGURED) + die("remote '%s' not configured", argv[1]); + + /* Check the refspec */ + if (argc > 2) { + int i, refspec_nr = argc - 2; + struct ref *local_refs = get_local_heads(); + struct refspec *refspec = parse_push_refspec(refspec_nr, + argv + 2); + + for (i = 0; i < refspec_nr; i++) { + struct refspec *rs = refspec + i; + + if (rs->pattern || rs->matching) + continue; + + /* LHS must match a single ref */ + switch (count_refspec_match(rs->src, local_refs, NULL)) { + case 1: + break; + case 0: + /* + * If LHS matches 'HEAD' then we need to ensure + * that it matches the same named branch + * checked out in the superproject. + */ + if (!strcmp(rs->src, "HEAD")) { + if (!detached_head && + !strcmp(head, superproject_head)) + break; + die("HEAD does not match the named branch in the superproject"); + } + /* fallthrough */ + default: + die("src refspec '%s' must name a ref", + rs->src); + } + } + free_refspec(refspec_nr, refspec); + } + free(head); + + return 0; +} + static int absorb_git_dirs(int argc, const char **argv, const char *prefix) { int i; @@ -1116,9 +1462,6 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, embed_gitdir_options, git_submodule_helper_usage, 0); - gitmodules_config(); - git_config(submodule_config, NULL); - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) return 1; @@ -1129,6 +1472,14 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix) return 0; } +static int is_active(int argc, const char **argv, const char *prefix) +{ + if (argc != 2) + die("submodule--helper is-active takes exactly 1 argument"); + + return !is_submodule_active(the_repository, argv[1]); +} + #define SUPPORT_SUPER_PREFIX (1<<0) struct cmd_struct { @@ -1146,16 +1497,18 @@ static struct cmd_struct commands[] = { {"resolve-relative-url", resolve_relative_url, 0}, {"resolve-relative-url-test", resolve_relative_url_test, 0}, {"init", module_init, SUPPORT_SUPER_PREFIX}, + {"status", module_status, SUPPORT_SUPER_PREFIX}, {"remote-branch", resolve_remote_submodule_branch, 0}, + {"push-check", push_check, 0}, {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, + {"is-active", is_active, 0}, }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) { int i; - if (argc < 2) - die(_("submodule--helper subcommand must be " - "called with a subcommand")); + if (argc < 2 || !strcmp(argv[1], "-h")) + usage("git submodule--helper <command>"); for (i = 0; i < ARRAY_SIZE(commands); i++) { if (!strcmp(argv[1], commands[i].cmd)) { |