Merge branch 'js/run-process-parallel-api-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 23 Aug 2017 21:33:49 +0000 (14:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 23 Aug 2017 21:33:49 +0000 (14:33 -0700)
API fix.

* js/run-process-parallel-api-fix:
run_processes_parallel: change confusing task_cb convention

1  2 
builtin/submodule--helper.c
run-command.c
index 6abdad3294ce84652a55522aff55f6e7673d192f,b0ca2a548f47ca9c264cb29dea7a1414b4ebb7ce..3a3c9ca72b678f1a446013cc17cc36caa3e47355
@@@ -1,11 -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"
@@@ -96,8 -95,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
@@@ -116,8 -113,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))
@@@ -234,7 -229,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;
  }
@@@ -340,12 -312,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);
@@@ -440,6 -398,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);
  
@@@ -488,8 -442,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);
@@@ -728,10 -577,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}
  
  
@@@ -783,6 -630,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);
  
@@@ -930,7 -781,7 +930,7 @@@ static int update_clone_task_finished(i
        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);
  
@@@ -979,7 -830,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;
  }
@@@ -1105,135 -954,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]);
diff --combined run-command.c
index 9e36151bf97d36945ca3464e719687718ee6d1c7,038d0e837cdde1bd1f529ec8c3e57cbf187a646a..b5e6eb37c0eb3d6ed0cbd4fc35e879bf9fbd3db6
@@@ -21,7 -21,6 +21,7 @@@ void child_process_clear(struct child_p
  
  struct child_to_clean {
        pid_t pid;
 +      struct child_process *process;
        struct child_to_clean *next;
  };
  static struct child_to_clean *children_to_clean;
@@@ -29,41 -28,10 +29,41 @@@ static int installed_child_cleanup_hand
  
  static void cleanup_children(int sig, int in_signal)
  {
 +      struct child_to_clean *children_to_wait_for = NULL;
 +
        while (children_to_clean) {
                struct child_to_clean *p = children_to_clean;
                children_to_clean = p->next;
 +
 +              if (p->process && !in_signal) {
 +                      struct child_process *process = p->process;
 +                      if (process->clean_on_exit_handler) {
 +                              trace_printf(
 +                                      "trace: run_command: running exit handler for pid %"
 +                                      PRIuMAX, (uintmax_t)p->pid
 +                              );
 +                              process->clean_on_exit_handler(process);
 +                      }
 +              }
 +
                kill(p->pid, sig);
 +
 +              if (p->process && p->process->wait_after_clean) {
 +                      p->next = children_to_wait_for;
 +                      children_to_wait_for = p;
 +              } else {
 +                      if (!in_signal)
 +                              free(p);
 +              }
 +      }
 +
 +      while (children_to_wait_for) {
 +              struct child_to_clean *p = children_to_wait_for;
 +              children_to_wait_for = p->next;
 +
 +              while (waitpid(p->pid, NULL, 0) < 0 && errno == EINTR)
 +                      ; /* spin waiting for process exit or error */
 +
                if (!in_signal)
                        free(p);
        }
@@@ -81,11 -49,10 +81,11 @@@ static void cleanup_children_on_exit(vo
        cleanup_children(SIGTERM, 0);
  }
  
 -static void mark_child_for_cleanup(pid_t pid)
 +static void mark_child_for_cleanup(pid_t pid, struct child_process *process)
  {
        struct child_to_clean *p = xmalloc(sizeof(*p));
        p->pid = pid;
 +      p->process = process;
        p->next = children_to_clean;
        children_to_clean = p;
  
@@@ -117,65 -84,18 +117,65 @@@ static inline void close_pair(int fd[2]
        close(fd[1]);
  }
  
 -#ifndef GIT_WINDOWS_NATIVE
 -static inline void dup_devnull(int to)
 +int is_executable(const char *name)
  {
 -      int fd = open("/dev/null", O_RDWR);
 -      if (fd < 0)
 -              die_errno(_("open /dev/null failed"));
 -      if (dup2(fd, to) < 0)
 -              die_errno(_("dup2(%d,%d) failed"), fd, to);
 -      close(fd);
 +      struct stat st;
 +
 +      if (stat(name, &st) || /* stat, not lstat */
 +          !S_ISREG(st.st_mode))
 +              return 0;
 +
 +#if defined(GIT_WINDOWS_NATIVE)
 +      /*
 +       * On Windows there is no executable bit. The file extension
 +       * indicates whether it can be run as an executable, and Git
 +       * has special-handling to detect scripts and launch them
 +       * through the indicated script interpreter. We test for the
 +       * file extension first because virus scanners may make
 +       * it quite expensive to open many files.
 +       */
 +      if (ends_with(name, ".exe"))
 +              return S_IXUSR;
 +
 +{
 +      /*
 +       * Now that we know it does not have an executable extension,
 +       * peek into the file instead.
 +       */
 +      char buf[3] = { 0 };
 +      int n;
 +      int fd = open(name, O_RDONLY);
 +      st.st_mode &= ~S_IXUSR;
 +      if (fd >= 0) {
 +              n = read(fd, buf, 2);
 +              if (n == 2)
 +                      /* look for a she-bang */
 +                      if (!strcmp(buf, "#!"))
 +                              st.st_mode |= S_IXUSR;
 +              close(fd);
 +      }
  }
  #endif
 +      return st.st_mode & S_IXUSR;
 +}
  
 +/*
 + * Search $PATH for a command.  This emulates the path search that
 + * execvp would perform, without actually executing the command so it
 + * can be used before fork() to prepare to run a command using
 + * execve() or after execvp() to diagnose why it failed.
 + *
 + * The caller should ensure that file contains no directory
 + * separators.
 + *
 + * Returns the path to the command, as found in $PATH or NULL if the
 + * command could not be found.  The caller inherits ownership of the memory
 + * used to store the resultant path.
 + *
 + * This should not be used on Windows, where the $PATH search rules
 + * are more complicated (e.g., a search for "foo" should find
 + * "foo.exe").
 + */
  static char *locate_in_PATH(const char *file)
  {
        const char *p = getenv("PATH");
                }
                strbuf_addstr(&buf, file);
  
 -              if (!access(buf.buf, F_OK))
 +              if (is_executable(buf.buf))
                        return strbuf_detach(&buf, NULL);
  
                if (!*end)
@@@ -268,248 -188,31 +268,248 @@@ static const char **prepare_shell_cmd(s
  }
  
  #ifndef GIT_WINDOWS_NATIVE
 -static int execv_shell_cmd(const char **argv)
 +static int child_notifier = -1;
 +
 +enum child_errcode {
 +      CHILD_ERR_CHDIR,
 +      CHILD_ERR_DUP2,
 +      CHILD_ERR_CLOSE,
 +      CHILD_ERR_SIGPROCMASK,
 +      CHILD_ERR_ENOENT,
 +      CHILD_ERR_SILENT,
 +      CHILD_ERR_ERRNO
 +};
 +
 +struct child_err {
 +      enum child_errcode err;
 +      int syserr; /* errno */
 +};
 +
 +static void child_die(enum child_errcode err)
  {
 -      struct argv_array nargv = ARGV_ARRAY_INIT;
 -      prepare_shell_cmd(&nargv, argv);
 -      trace_argv_printf(nargv.argv, "trace: exec:");
 -      sane_execvp(nargv.argv[0], (char **)nargv.argv);
 -      argv_array_clear(&nargv);
 -      return -1;
 +      struct child_err buf;
 +
 +      buf.err = err;
 +      buf.syserr = errno;
 +
 +      /* write(2) on buf smaller than PIPE_BUF (min 512) is atomic: */
 +      xwrite(child_notifier, &buf, sizeof(buf));
 +      _exit(1);
  }
 -#endif
  
 -#ifndef GIT_WINDOWS_NATIVE
 -static int child_notifier = -1;
 +static void child_dup2(int fd, int to)
 +{
 +      if (dup2(fd, to) < 0)
 +              child_die(CHILD_ERR_DUP2);
 +}
 +
 +static void child_close(int fd)
 +{
 +      if (close(fd))
 +              child_die(CHILD_ERR_CLOSE);
 +}
  
 -static void notify_parent(void)
 +static void child_close_pair(int fd[2])
  {
 +      child_close(fd[0]);
 +      child_close(fd[1]);
 +}
 +
 +/*
 + * parent will make it look like the child spewed a fatal error and died
 + * this is needed to prevent changes to t0061.
 + */
 +static void fake_fatal(const char *err, va_list params)
 +{
 +      vreportf("fatal: ", err, params);
 +}
 +
 +static void child_error_fn(const char *err, va_list params)
 +{
 +      const char msg[] = "error() should not be called in child\n";
 +      xwrite(2, msg, sizeof(msg) - 1);
 +}
 +
 +static void child_warn_fn(const char *err, va_list params)
 +{
 +      const char msg[] = "warn() should not be called in child\n";
 +      xwrite(2, msg, sizeof(msg) - 1);
 +}
 +
 +static void NORETURN child_die_fn(const char *err, va_list params)
 +{
 +      const char msg[] = "die() should not be called in child\n";
 +      xwrite(2, msg, sizeof(msg) - 1);
 +      _exit(2);
 +}
 +
 +/* this runs in the parent process */
 +static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
 +{
 +      static void (*old_errfn)(const char *err, va_list params);
 +
 +      old_errfn = get_error_routine();
 +      set_error_routine(fake_fatal);
 +      errno = cerr->syserr;
 +
 +      switch (cerr->err) {
 +      case CHILD_ERR_CHDIR:
 +              error_errno("exec '%s': cd to '%s' failed",
 +                          cmd->argv[0], cmd->dir);
 +              break;
 +      case CHILD_ERR_DUP2:
 +              error_errno("dup2() in child failed");
 +              break;
 +      case CHILD_ERR_CLOSE:
 +              error_errno("close() in child failed");
 +              break;
 +      case CHILD_ERR_SIGPROCMASK:
 +              error_errno("sigprocmask failed restoring signals");
 +              break;
 +      case CHILD_ERR_ENOENT:
 +              error_errno("cannot run %s", cmd->argv[0]);
 +              break;
 +      case CHILD_ERR_SILENT:
 +              break;
 +      case CHILD_ERR_ERRNO:
 +              error_errno("cannot exec '%s'", cmd->argv[0]);
 +              break;
 +      }
 +      set_error_routine(old_errfn);
 +}
 +
 +static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
 +{
 +      if (!cmd->argv[0])
 +              die("BUG: command is empty");
 +
 +      /*
 +       * Add SHELL_PATH so in the event exec fails with ENOEXEC we can
 +       * attempt to interpret the command with 'sh'.
 +       */
 +      argv_array_push(out, SHELL_PATH);
 +
 +      if (cmd->git_cmd) {
 +              argv_array_push(out, "git");
 +              argv_array_pushv(out, cmd->argv);
 +      } else if (cmd->use_shell) {
 +              prepare_shell_cmd(out, cmd->argv);
 +      } else {
 +              argv_array_pushv(out, cmd->argv);
 +      }
 +
        /*
 -       * execvp failed.  If possible, we'd like to let start_command
 -       * know, so failures like ENOENT can be handled right away; but
 -       * otherwise, finish_command will still report the error.
 +       * If there are no '/' characters in the command then perform a path
 +       * lookup and use the resolved path as the command to exec.  If there
 +       * are no '/' characters or if the command wasn't found in the path,
 +       * have exec attempt to invoke the command directly.
         */
 -      xwrite(child_notifier, "", 1);
 +      if (!strchr(out->argv[1], '/')) {
 +              char *program = locate_in_PATH(out->argv[1]);
 +              if (program) {
 +                      free((char *)out->argv[1]);
 +                      out->argv[1] = program;
 +              }
 +      }
 +}
 +
 +static char **prep_childenv(const char *const *deltaenv)
 +{
 +      extern char **environ;
 +      char **childenv;
 +      struct string_list env = STRING_LIST_INIT_DUP;
 +      struct strbuf key = STRBUF_INIT;
 +      const char *const *p;
 +      int i;
 +
 +      /* Construct a sorted string list consisting of the current environ */
 +      for (p = (const char *const *) environ; p && *p; p++) {
 +              const char *equals = strchr(*p, '=');
 +
 +              if (equals) {
 +                      strbuf_reset(&key);
 +                      strbuf_add(&key, *p, equals - *p);
 +                      string_list_append(&env, key.buf)->util = (void *) *p;
 +              } else {
 +                      string_list_append(&env, *p)->util = (void *) *p;
 +              }
 +      }
 +      string_list_sort(&env);
 +
 +      /* Merge in 'deltaenv' with the current environ */
 +      for (p = deltaenv; p && *p; p++) {
 +              const char *equals = strchr(*p, '=');
 +
 +              if (equals) {
 +                      /* ('key=value'), insert or replace entry */
 +                      strbuf_reset(&key);
 +                      strbuf_add(&key, *p, equals - *p);
 +                      string_list_insert(&env, key.buf)->util = (void *) *p;
 +              } else {
 +                      /* otherwise ('key') remove existing entry */
 +                      string_list_remove(&env, *p, 0);
 +              }
 +      }
 +
 +      /* Create an array of 'char *' to be used as the childenv */
 +      childenv = xmalloc((env.nr + 1) * sizeof(char *));
 +      for (i = 0; i < env.nr; i++)
 +              childenv[i] = env.items[i].util;
 +      childenv[env.nr] = NULL;
 +
 +      string_list_clear(&env, 0);
 +      strbuf_release(&key);
 +      return childenv;
 +}
 +
 +struct atfork_state {
 +#ifndef NO_PTHREADS
 +      int cs;
 +#endif
 +      sigset_t old;
 +};
 +
 +#ifndef NO_PTHREADS
 +static void bug_die(int err, const char *msg)
 +{
 +      if (err) {
 +              errno = err;
 +              die_errno("BUG: %s", msg);
 +      }
  }
  #endif
  
 +static void atfork_prepare(struct atfork_state *as)
 +{
 +      sigset_t all;
 +
 +      if (sigfillset(&all))
 +              die_errno("sigfillset");
 +#ifdef NO_PTHREADS
 +      if (sigprocmask(SIG_SETMASK, &all, &as->old))
 +              die_errno("sigprocmask");
 +#else
 +      bug_die(pthread_sigmask(SIG_SETMASK, &all, &as->old),
 +              "blocking all signals");
 +      bug_die(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs),
 +              "disabling cancellation");
 +#endif
 +}
 +
 +static void atfork_parent(struct atfork_state *as)
 +{
 +#ifdef NO_PTHREADS
 +      if (sigprocmask(SIG_SETMASK, &as->old, NULL))
 +              die_errno("sigprocmask");
 +#else
 +      bug_die(pthread_setcancelstate(as->cs, NULL),
 +              "re-enabling cancellation");
 +      bug_die(pthread_sigmask(SIG_SETMASK, &as->old, NULL),
 +              "restoring signal mask");
 +#endif
 +}
 +#endif /* GIT_WINDOWS_NATIVE */
 +
  static inline void set_cloexec(int fd)
  {
        int flags = fcntl(fd, F_GETFD);
@@@ -545,6 -248,13 +545,6 @@@ static int wait_or_whine(pid_t pid, con
                code += 128;
        } else if (WIFEXITED(status)) {
                code = WEXITSTATUS(status);
 -              /*
 -               * Convert special exit code when execvp failed.
 -               */
 -              if (code == 127) {
 -                      code = -1;
 -                      failed_errno = ENOENT;
 -              }
        } else {
                error("waitpid is confused (%s)", argv0);
        }
@@@ -629,149 -339,109 +629,149 @@@ fail_pipe
  #ifndef GIT_WINDOWS_NATIVE
  {
        int notify_pipe[2];
 +      int null_fd = -1;
 +      char **childenv;
 +      struct argv_array argv = ARGV_ARRAY_INIT;
 +      struct child_err cerr;
 +      struct atfork_state as;
 +
        if (pipe(notify_pipe))
                notify_pipe[0] = notify_pipe[1] = -1;
  
 +      if (cmd->no_stdin || cmd->no_stdout || cmd->no_stderr) {
 +              null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
 +              if (null_fd < 0)
 +                      die_errno(_("open /dev/null failed"));
 +              set_cloexec(null_fd);
 +      }
 +
 +      prepare_cmd(&argv, cmd);
 +      childenv = prep_childenv(cmd->env);
 +      atfork_prepare(&as);
 +
 +      /*
 +       * NOTE: In order to prevent deadlocking when using threads special
 +       * care should be taken with the function calls made in between the
 +       * fork() and exec() calls.  No calls should be made to functions which
 +       * require acquiring a lock (e.g. malloc) as the lock could have been
 +       * held by another thread at the time of forking, causing the lock to
 +       * never be released in the child process.  This means only
 +       * Async-Signal-Safe functions are permitted in the child.
 +       */
        cmd->pid = fork();
        failed_errno = errno;
        if (!cmd->pid) {
 +              int sig;
                /*
 -               * Redirect the channel to write syscall error messages to
 -               * before redirecting the process's stderr so that all die()
 -               * in subsequent call paths use the parent's stderr.
 +               * Ensure the default die/error/warn routines do not get
 +               * called, they can take stdio locks and malloc.
                 */
 -              if (cmd->no_stderr || need_err) {
 -                      int child_err = dup(2);
 -                      set_cloexec(child_err);
 -                      set_error_handle(fdopen(child_err, "w"));
 -              }
 +              set_die_routine(child_die_fn);
 +              set_error_routine(child_error_fn);
 +              set_warn_routine(child_warn_fn);
  
                close(notify_pipe[0]);
                set_cloexec(notify_pipe[1]);
                child_notifier = notify_pipe[1];
 -              atexit(notify_parent);
  
                if (cmd->no_stdin)
 -                      dup_devnull(0);
 +                      child_dup2(null_fd, 0);
                else if (need_in) {
 -                      dup2(fdin[0], 0);
 -                      close_pair(fdin);
 +                      child_dup2(fdin[0], 0);
 +                      child_close_pair(fdin);
                } else if (cmd->in) {
 -                      dup2(cmd->in, 0);
 -                      close(cmd->in);
 +                      child_dup2(cmd->in, 0);
 +                      child_close(cmd->in);
                }
  
                if (cmd->no_stderr)
 -                      dup_devnull(2);
 +                      child_dup2(null_fd, 2);
                else if (need_err) {
 -                      dup2(fderr[1], 2);
 -                      close_pair(fderr);
 +                      child_dup2(fderr[1], 2);
 +                      child_close_pair(fderr);
                } else if (cmd->err > 1) {
 -                      dup2(cmd->err, 2);
 -                      close(cmd->err);
 +                      child_dup2(cmd->err, 2);
 +                      child_close(cmd->err);
                }
  
                if (cmd->no_stdout)
 -                      dup_devnull(1);
 +                      child_dup2(null_fd, 1);
                else if (cmd->stdout_to_stderr)
 -                      dup2(2, 1);
 +                      child_dup2(2, 1);
                else if (need_out) {
 -                      dup2(fdout[1], 1);
 -                      close_pair(fdout);
 +                      child_dup2(fdout[1], 1);
 +                      child_close_pair(fdout);
                } else if (cmd->out > 1) {
 -                      dup2(cmd->out, 1);
 -                      close(cmd->out);
 +                      child_dup2(cmd->out, 1);
 +                      child_close(cmd->out);
                }
  
                if (cmd->dir && chdir(cmd->dir))
 -                      die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
 -                          cmd->dir);
 -              if (cmd->env) {
 -                      for (; *cmd->env; cmd->env++) {
 -                              if (strchr(*cmd->env, '='))
 -                                      putenv((char *)*cmd->env);
 -                              else
 -                                      unsetenv(*cmd->env);
 -                      }
 +                      child_die(CHILD_ERR_CHDIR);
 +
 +              /*
 +               * restore default signal handlers here, in case
 +               * we catch a signal right before execve below
 +               */
 +              for (sig = 1; sig < NSIG; sig++) {
 +                      /* ignored signals get reset to SIG_DFL on execve */
 +                      if (signal(sig, SIG_DFL) == SIG_IGN)
 +                              signal(sig, SIG_IGN);
                }
 -              if (cmd->git_cmd)
 -                      execv_git_cmd(cmd->argv);
 -              else if (cmd->use_shell)
 -                      execv_shell_cmd(cmd->argv);
 -              else
 -                      sane_execvp(cmd->argv[0], (char *const*) cmd->argv);
 +
 +              if (sigprocmask(SIG_SETMASK, &as.old, NULL) != 0)
 +                      child_die(CHILD_ERR_SIGPROCMASK);
 +
 +              /*
 +               * Attempt to exec using the command and arguments starting at
 +               * argv.argv[1].  argv.argv[0] contains SHELL_PATH which will
 +               * be used in the event exec failed with ENOEXEC at which point
 +               * we will try to interpret the command using 'sh'.
 +               */
 +              execve(argv.argv[1], (char *const *) argv.argv + 1,
 +                     (char *const *) childenv);
 +              if (errno == ENOEXEC)
 +                      execve(argv.argv[0], (char *const *) argv.argv,
 +                             (char *const *) childenv);
 +
                if (errno == ENOENT) {
 -                      if (!cmd->silent_exec_failure)
 -                              error("cannot run %s: %s", cmd->argv[0],
 -                                      strerror(ENOENT));
 -                      exit(127);
 +                      if (cmd->silent_exec_failure)
 +                              child_die(CHILD_ERR_SILENT);
 +                      child_die(CHILD_ERR_ENOENT);
                } else {
 -                      die_errno("cannot exec '%s'", cmd->argv[0]);
 +                      child_die(CHILD_ERR_ERRNO);
                }
        }
 +      atfork_parent(&as);
        if (cmd->pid < 0)
                error_errno("cannot fork() for %s", cmd->argv[0]);
        else if (cmd->clean_on_exit)
 -              mark_child_for_cleanup(cmd->pid);
 +              mark_child_for_cleanup(cmd->pid, cmd);
  
        /*
 -       * Wait for child's execvp. If the execvp succeeds (or if fork()
 +       * Wait for child's exec. If the exec succeeds (or if fork()
         * failed), EOF is seen immediately by the parent. Otherwise, the
 -       * child process sends a single byte.
 +       * child process sends a child_err struct.
         * Note that use of this infrastructure is completely advisory,
         * therefore, we keep error checks minimal.
         */
        close(notify_pipe[1]);
 -      if (read(notify_pipe[0], &notify_pipe[1], 1) == 1) {
 +      if (xread(notify_pipe[0], &cerr, sizeof(cerr)) == sizeof(cerr)) {
                /*
 -               * At this point we know that fork() succeeded, but execvp()
 +               * At this point we know that fork() succeeded, but exec()
                 * failed. Errors have been reported to our stderr.
                 */
                wait_or_whine(cmd->pid, cmd->argv[0], 0);
 +              child_err_spew(cmd, &cerr);
                failed_errno = errno;
                cmd->pid = -1;
        }
        close(notify_pipe[0]);
 +
 +      if (null_fd >= 0)
 +              close(null_fd);
 +      argv_array_clear(&argv);
 +      free(childenv);
  }
  #else
  {
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
                error_errno("cannot spawn %s", cmd->argv[0]);
        if (cmd->clean_on_exit && cmd->pid >= 0)
 -              mark_child_for_cleanup(cmd->pid);
 +              mark_child_for_cleanup(cmd->pid, cmd);
  
        argv_array_clear(&nargv);
        cmd->argv = sargv;
@@@ -964,7 -634,7 +964,7 @@@ int in_async(void
        return !pthread_equal(main_thread, pthread_self());
  }
  
 -void NORETURN async_exit(int code)
 +static void NORETURN async_exit(int code)
  {
        pthread_exit((void *)(intptr_t)code);
  }
@@@ -1014,26 -684,13 +1014,26 @@@ int in_async(void
        return process_is_async;
  }
  
 -void NORETURN async_exit(int code)
 +static void NORETURN async_exit(int code)
  {
        exit(code);
  }
  
  #endif
  
 +void check_pipe(int err)
 +{
 +      if (err == EPIPE) {
 +              if (in_async())
 +                      async_exit(141);
 +
 +              signal(SIGPIPE, SIG_DFL);
 +              raise(SIGPIPE);
 +              /* Should never happen, but just in case... */
 +              exit(141);
 +      }
 +}
 +
  int start_async(struct async *async)
  {
        int need_in, need_out;
                exit(!!async->proc(proc_in, proc_out, async->data));
        }
  
 -      mark_child_for_cleanup(async->pid);
 +      mark_child_for_cleanup(async->pid, NULL);
  
        if (need_in)
                close(fdin[0]);
@@@ -1168,14 -825,8 +1168,14 @@@ const char *find_hook(const char *name
  
        strbuf_reset(&path);
        strbuf_git_path(&path, "hooks/%s", name);
 -      if (access(path.buf, X_OK) < 0)
 +      if (access(path.buf, X_OK) < 0) {
 +#ifdef STRIP_EXTENSION
 +              strbuf_addstr(&path, STRIP_EXTENSION);
 +              if (access(path.buf, X_OK) >= 0)
 +                      return path.buf;
 +#endif
                return NULL;
 +      }
        return path.buf;
  }
  
@@@ -1533,7 -1184,7 +1533,7 @@@ static int pp_start_one(struct parallel
        if (start_command(&pp->children[i].process)) {
                code = pp->start_failure(&pp->children[i].err,
                                         pp->data,
-                                        &pp->children[i].data);
+                                        pp->children[i].data);
                strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
                strbuf_reset(&pp->children[i].err);
                if (code)
@@@ -1601,7 -1252,7 +1601,7 @@@ static int pp_collect_finished(struct p
  
                code = pp->task_finished(code,
                                         &pp->children[i].err, pp->data,
-                                        &pp->children[i].data);
+                                        pp->children[i].data);
  
                if (code)
                        result = code;