Merge branch 'jc/utf8-fprintf'
authorJunio C Hamano <gitster@pobox.com>
Fri, 7 Jul 2017 01:14:44 +0000 (18:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 7 Jul 2017 01:14:44 +0000 (18:14 -0700)
Code cleanup.

* jc/utf8-fprintf:
submodule--helper: do not call utf8_fprintf() unnecessarily

1  2 
builtin/submodule--helper.c
index e1b06c41d8efe083e4a368d207720724e594ca65,704df874d168cf43aa465448af4b65cc193a3689..6abdad3294ce84652a55522aff55f6e7673d192f
@@@ -1,12 -1,9 +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"
@@@ -97,8 -94,6 +96,8 @@@ static int chop_last_dir(char **remoteu
   * NEEDSWORK: This works incorrectly on the domain and protocol part.
   * remote_url      url              outcome          expectation
   * http://a.com/b  ../c             http://a.com/c   as is
 + * http://a.com/b/ ../c             http://a.com/c   same as previous line, but
 + *                                                   ignore trailing slash in url
   * http://a.com/b  ../../c          http://c         error out
   * http://a.com/b  ../../../c       http:/c          error out
   * http://a.com/b  ../../../../c    http:c           error out
@@@ -117,8 -112,8 +116,8 @@@ static char *relative_url(const char *r
        struct strbuf sb = STRBUF_INIT;
        size_t len = strlen(remoteurl);
  
 -      if (is_dir_sep(remoteurl[len]))
 -              remoteurl[len] = '\0';
 +      if (is_dir_sep(remoteurl[len-1]))
 +              remoteurl[len-1] = '\0';
  
        if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
                is_relative = 0;
        }
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
 +      if (ends_with(url, "/"))
 +              strbuf_setlen(&sb, sb.len - 1);
        free(remoteurl);
  
        if (starts_with_dot_slash(sb.buf))
@@@ -235,7 -228,8 +234,7 @@@ static int module_list_compute(int argc
        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)
        return result;
  }
  
 +static void module_list_active(struct module_list *list)
 +{
 +      int i;
 +      struct module_list active_modules = MODULE_LIST_INIT;
 +
 +      gitmodules_config();
 +
 +      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;
                if (ce_stage(ce))
                        printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
                else
 -                      printf("%06o %s %d\t", ce->ce_mode, sha1_to_hex(ce->sha1), ce_stage(ce));
 +                      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;
  }
@@@ -341,12 -311,8 +340,12 @@@ static void init_submodule(const char *
        /* Only loads from .gitmodules, no overlay with .git/config */
        gitmodules_config();
  
 -      if (prefix) {
 -              strbuf_addf(&sb, "%s%s", prefix, path);
 +      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);
                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_reset(&sb);
 +              strbuf_addf(&sb, "submodule.%s.active", sub->name);
 +              git_config_set_gently(sb.buf, "true");
 +      }
 +
        /*
         * Copy url setting when it is not set yet.
         * To look up the url in .git/config, we must not fall back to
        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)) {
                        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);
@@@ -441,6 -397,9 +440,6 @@@ static int module_init(int argc, const 
        int i;
  
        struct option module_init_options[] = {
 -              OPT_STRING(0, "prefix", &prefix,
 -                         N_("path"),
 -                         N_("alternative anchor for relative paths")),
                OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
                OPT_END()
        };
        if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
                return 1;
  
 +      /*
 +       * 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);
 +
        for (i = 0; i < list.nr; i++)
                init_submodule(list.entries[i]->name, prefix, quiet);
  
@@@ -489,8 -441,7 +488,8 @@@ static int module_name(int argc, const 
  }
  
  static int clone_submodule(const char *path, const char *gitdir, const char *url,
 -                         const char *depth, const char *reference, int quiet)
 +                         const char *depth, struct string_list *reference,
 +                         int quiet, int progress)
  {
        struct child_process cp = CHILD_PROCESS_INIT;
  
        argv_array_push(&cp.args, "--no-checkout");
        if (quiet)
                argv_array_push(&cp.args, "--quiet");
 +      if (progress)
 +              argv_array_push(&cp.args, "--progress");
        if (depth && *depth)
                argv_array_pushl(&cp.args, "--depth", depth, NULL);
 -      if (reference && *reference)
 -              argv_array_pushl(&cp.args, "--reference", reference, NULL);
 +      if (reference->nr) {
 +              struct string_list_item *item;
 +              for_each_string_list_item(item, reference)
 +                      argv_array_pushl(&cp.args, "--reference",
 +                                       item->string, NULL);
 +      }
        if (gitdir && *gitdir)
                argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
  
        return run_command(&cp);
  }
  
 +struct submodule_alternate_setup {
 +      const char *submodule_name;
 +      enum SUBMODULE_ALTERNATE_ERROR_MODE {
 +              SUBMODULE_ALTERNATE_ERROR_DIE,
 +              SUBMODULE_ALTERNATE_ERROR_INFO,
 +              SUBMODULE_ALTERNATE_ERROR_IGNORE
 +      } error_mode;
 +      struct string_list *reference;
 +};
 +#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
 +      SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
 +
 +static int add_possible_reference_from_superproject(
 +              struct alternate_object_database *alt, void *sas_cb)
 +{
 +      struct submodule_alternate_setup *sas = sas_cb;
 +
 +      /*
 +       * If the alternate object store is another repository, try the
 +       * standard layout with .git/(modules/<name>)+/objects
 +       */
 +      if (ends_with(alt->path, "/objects")) {
 +              char *sm_alternate;
 +              struct strbuf sb = STRBUF_INIT;
 +              struct strbuf err = STRBUF_INIT;
 +              strbuf_add(&sb, alt->path, strlen(alt->path) - strlen("objects"));
 +
 +              /*
 +               * We need to end the new path with '/' to mark it as a dir,
 +               * otherwise a submodule name containing '/' will be broken
 +               * as the last part of a missing submodule reference would
 +               * be taken as a file name.
 +               */
 +              strbuf_addf(&sb, "modules/%s/", sas->submodule_name);
 +
 +              sm_alternate = compute_alternate_path(sb.buf, &err);
 +              if (sm_alternate) {
 +                      string_list_append(sas->reference, xstrdup(sb.buf));
 +                      free(sm_alternate);
 +              } else {
 +                      switch (sas->error_mode) {
 +                      case SUBMODULE_ALTERNATE_ERROR_DIE:
 +                              die(_("submodule '%s' cannot add alternate: %s"),
 +                                  sas->submodule_name, err.buf);
 +                      case SUBMODULE_ALTERNATE_ERROR_INFO:
 +                              fprintf(stderr, _("submodule '%s' cannot add alternate: %s"),
 +                                      sas->submodule_name, err.buf);
 +                      case SUBMODULE_ALTERNATE_ERROR_IGNORE:
 +                              ; /* nothing */
 +                      }
 +              }
 +              strbuf_release(&sb);
 +      }
 +
 +      return 0;
 +}
 +
 +static void prepare_possible_alternates(const char *sm_name,
 +              struct string_list *reference)
 +{
 +      char *sm_alternate = NULL, *error_strategy = NULL;
 +      struct submodule_alternate_setup sas = SUBMODULE_ALTERNATE_SETUP_INIT;
 +
 +      git_config_get_string("submodule.alternateLocation", &sm_alternate);
 +      if (!sm_alternate)
 +              return;
 +
 +      git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
 +
 +      if (!error_strategy)
 +              error_strategy = xstrdup("die");
 +
 +      sas.submodule_name = sm_name;
 +      sas.reference = reference;
 +      if (!strcmp(error_strategy, "die"))
 +              sas.error_mode = SUBMODULE_ALTERNATE_ERROR_DIE;
 +      else if (!strcmp(error_strategy, "info"))
 +              sas.error_mode = SUBMODULE_ALTERNATE_ERROR_INFO;
 +      else if (!strcmp(error_strategy, "ignore"))
 +              sas.error_mode = SUBMODULE_ALTERNATE_ERROR_IGNORE;
 +      else
 +              die(_("Value '%s' for submodule.alternateErrorStrategy is not recognized"), error_strategy);
 +
 +      if (!strcmp(sm_alternate, "superproject"))
 +              foreach_alt_odb(add_possible_reference_from_superproject, &sas);
 +      else if (!strcmp(sm_alternate, "no"))
 +              ; /* do nothing */
 +      else
 +              die(_("Value '%s' for submodule.alternateLocation is not recognized"), sm_alternate);
 +
 +      free(sm_alternate);
 +      free(error_strategy);
 +}
 +
  static int module_clone(int argc, const char **argv, const char *prefix)
  {
 -      const char *name = NULL, *url = NULL;
 -      const char *reference = NULL, *depth = NULL;
 +      const char *name = NULL, *url = NULL, *depth = NULL;
        int quiet = 0;
 -      FILE *submodule_dot_git;
 +      int progress = 0;
        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;
  
        struct option module_clone_options[] = {
                OPT_STRING(0, "prefix", &prefix,
                OPT_STRING(0, "url", &url,
                           N_("string"),
                           N_("url where to clone the submodule from")),
 -              OPT_STRING(0, "reference", &reference,
 -                         N_("string"),
 +              OPT_STRING_LIST(0, "reference", &reference,
 +                         N_("repo"),
                           N_("reference repository")),
                OPT_STRING(0, "depth", &depth,
                           N_("string"),
                           N_("depth for shallow clones")),
                OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
 +              OPT_BOOL(0, "progress", &progress,
 +                         N_("force cloning progress")),
                OPT_END()
        };
  
                                   module_clone_options);
  
        strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
 -      sm_gitdir = xstrdup(absolute_path(sb.buf));
 +      sm_gitdir = absolute_pathdup(sb.buf);
        strbuf_reset(&sb);
  
        if (!is_absolute_path(path)) {
        if (!file_exists(sm_gitdir)) {
                if (safe_create_leading_directories_const(sm_gitdir) < 0)
                        die(_("could not create directory '%s'"), sm_gitdir);
 -              if (clone_submodule(path, sm_gitdir, url, depth, reference, quiet))
 +
 +              prepare_possible_alternates(name, &reference);
 +
 +              if (clone_submodule(path, sm_gitdir, url, depth, &reference,
 +                                  quiet, progress))
                        die(_("clone of '%s' into submodule path '%s' failed"),
                            url, path);
        } else {
                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);
 +      if (sm_alternate)
 +              git_config_set_in_file(p, "submodule.alternateLocation",
 +                                         sm_alternate);
 +      git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
 +      if (error_strategy)
 +              git_config_set_in_file(p, "submodule.alternateErrorStrategy",
 +                                         error_strategy);
 +
 +      free(sm_alternate);
 +      free(error_strategy);
 +
        strbuf_release(&sb);
 -      strbuf_release(&rel_path);
        free(sm_gitdir);
        free(path);
        free(p);
@@@ -729,10 -576,9 +728,10 @@@ struct submodule_update_clone 
        struct submodule_update_strategy update;
  
        /* configuration parameters which are passed on to the children */
 +      int progress;
        int quiet;
        int recommend_shallow;
 -      const char *reference;
 +      struct string_list references;
        const char *depth;
        const char *recursive_prefix;
        const char *prefix;
        int failed_clones_nr, failed_clones_alloc;
  };
  #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
 -      SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, NULL, NULL, NULL, NULL, \
 +      SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \
 +      NULL, NULL, NULL, \
        STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
  
  
@@@ -784,6 -629,7 +783,6 @@@ static int prepare_to_clone_next_submod
        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)) {
                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, "%06o %s %d %d\t%s\n", ce->ce_mode,
 -                      sha1_to_hex(ce->sha1), ce_stage(ce),
 +                      oid_to_hex(&ce->oid), ce_stage(ce),
                        needs_cloning, ce->name);
        string_list_append(&suc->projectlines, sb.buf);
  
        child->err = -1;
        argv_array_push(&child->args, "submodule--helper");
        argv_array_push(&child->args, "clone");
 +      if (suc->progress)
 +              argv_array_push(&child->args, "--progress");
        if (suc->quiet)
                argv_array_push(&child->args, "--quiet");
        if (suc->prefix)
                argv_array_push(&child->args, "--depth=1");
        argv_array_pushl(&child->args, "--path", sub->path, NULL);
        argv_array_pushl(&child->args, "--name", sub->name, NULL);
 -      argv_array_pushl(&child->args, "--url", url, NULL);
 -      if (suc->reference)
 -              argv_array_push(&child->args, suc->reference);
 +      argv_array_pushl(&child->args, "--url", sub->url, NULL);
 +      if (suc->references.nr) {
 +              struct string_list_item *item;
 +              for_each_string_list_item(item, &suc->references)
 +                      argv_array_pushl(&child->args, "--reference", item->string, NULL);
 +      }
        if (suc->depth)
                argv_array_push(&child->args, suc->depth);
  
  cleanup:
 -      free(url);
        strbuf_reset(&displaypath_sb);
        strbuf_reset(&sb);
  
@@@ -980,7 -829,7 +979,7 @@@ static int update_clone(int argc, cons
                OPT_STRING(0, "update", &update,
                           N_("string"),
                           N_("rebase, merge, checkout or none")),
 -              OPT_STRING(0, "reference", &suc.reference, N_("repo"),
 +              OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"),
                           N_("reference repository")),
                OPT_STRING(0, "depth", &suc.depth, "<depth>",
                           N_("Create a shallow clone truncated to the "
                OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
                            N_("whether the initial clone should follow the shallow recommendation")),
                OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
 +              OPT_BOOL(0, "progress", &suc.progress,
 +                          N_("force cloning progress")),
                OPT_END()
        };
  
                return 1;
  
        for_each_string_list_item(item, &suc.projectlines)
-               utf8_fprintf(stdout, "%s", item->string);
+               fprintf(stdout, "%s", item->string);
  
        return 0;
  }
@@@ -1106,135 -953,33 +1105,135 @@@ static int resolve_remote_submodule_bra
        return 0;
  }
  
 +static int push_check(int argc, const char **argv, const char *prefix)
 +{
 +      struct remote *remote;
 +
 +      if (argc < 2)
 +              die("submodule--helper push-check requires at least 1 argument");
 +
 +      /*
 +       * 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
 +                       * NEEDSWORK: add logic to special case 'HEAD' once
 +                       * working with submodules in a detached head state
 +                       * ceases to be the norm.
 +                       */
 +                      if (count_refspec_match(rs->src, local_refs, NULL) != 1)
 +                              die("src refspec '%s' must name a ref",
 +                                  rs->src);
 +              }
 +              free_refspec(refspec_nr, refspec);
 +      }
 +
 +      return 0;
 +}
 +
 +static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 +{
 +      int i;
 +      struct pathspec pathspec;
 +      struct module_list list = MODULE_LIST_INIT;
 +      unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES;
 +
 +      struct option embed_gitdir_options[] = {
 +              OPT_STRING(0, "prefix", &prefix,
 +                         N_("path"),
 +                         N_("path into the working tree")),
 +              OPT_BIT(0, "--recursive", &flags, N_("recurse into submodules"),
 +                      ABSORB_GITDIR_RECURSE_SUBMODULES),
 +              OPT_END()
 +      };
 +
 +      const char *const git_submodule_helper_usage[] = {
 +              N_("git submodule--helper embed-git-dir [<path>...]"),
 +              NULL
 +      };
 +
 +      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;
 +
 +      for (i = 0; i < list.nr; i++)
 +              absorb_git_dir_into_superproject(prefix,
 +                              list.entries[i]->name, flags);
 +
 +      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");
 +
 +      gitmodules_config();
 +
 +      return !is_submodule_active(the_repository, argv[1]);
 +}
 +
 +#define SUPPORT_SUPER_PREFIX (1<<0)
 +
  struct cmd_struct {
        const char *cmd;
        int (*fn)(int, const char **, const char *);
 +      unsigned option;
  };
  
  static struct cmd_struct commands[] = {
 -      {"list", module_list},
 -      {"name", module_name},
 -      {"clone", module_clone},
 -      {"update-clone", update_clone},
 -      {"relative-path", resolve_relative_path},
 -      {"resolve-relative-url", resolve_relative_url},
 -      {"resolve-relative-url-test", resolve_relative_url_test},
 -      {"init", module_init},
 -      {"remote-branch", resolve_remote_submodule_branch}
 +      {"list", module_list, 0},
 +      {"name", module_name, 0},
 +      {"clone", module_clone, 0},
 +      {"update-clone", update_clone, 0},
 +      {"relative-path", resolve_relative_path, 0},
 +      {"resolve-relative-url", resolve_relative_url, 0},
 +      {"resolve-relative-url-test", resolve_relative_url_test, 0},
 +      {"init", module_init, 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"));
 -
 -      for (i = 0; i < ARRAY_SIZE(commands); i++)
 -              if (!strcmp(argv[1], commands[i].cmd))
 +      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)) {
 +                      if (get_super_prefix() &&
 +                          !(commands[i].option & SUPPORT_SUPER_PREFIX))
 +                              die(_("%s doesn't support --super-prefix"),
 +                                  commands[i].cmd);
                        return commands[i].fn(argc - 1, argv + 1, prefix);
 +              }
 +      }
  
        die(_("'%s' is not a valid submodule--helper "
              "subcommand"), argv[1]);