Merge branch 'jk/submodule-c-credential' into js/http-custom-headers
authorJunio C Hamano <gitster@pobox.com>
Tue, 10 May 2016 17:38:31 +0000 (10:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 10 May 2016 17:38:31 +0000 (10:38 -0700)
* jk/submodule-c-credential:
submodule: stop sanitizing config options
submodule: use prepare_submodule_repo_env consistently
submodule--helper: move config-sanitizing to submodule.c
submodule: export sanitized GIT_CONFIG_PARAMETERS
t5550: break submodule config test into multiple sub-tests
t5550: fix typo in $HTTPD_URL
git_config_push_parameter: handle empty GIT_CONFIG_PARAMETERS
git: submodule honor -c credential.* from command line
quote: implement sq_quotef()
submodule: fix segmentation fault in submodule--helper clone
submodule: fix submodule--helper clone usage
submodule: check argc count for git submodule--helper clone
submodule: don't pass empty string arguments to submodule--helper clone

1  2 
builtin/submodule--helper.c
config.c
git-submodule.sh
submodule.c
t/t1300-repo-config.sh
index 5295b727d4609fa33a08c31b2d051ce3f98244fb,89250f0b782c7328cc411c7d19b86e3978b01032..45c97e6ae12dbd37ed4a7910f934ad9c2a40e540
@@@ -22,12 -22,17 +22,12 @@@ static int module_list_compute(int argc
                               struct module_list *list)
  {
        int i, result = 0;
 -      char *max_prefix, *ps_matched = NULL;
 -      int max_prefix_len;
 +      char *ps_matched = NULL;
        parse_pathspec(pathspec, 0,
                       PATHSPEC_PREFER_FULL |
                       PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
                       prefix, argv);
  
 -      /* Find common prefix for all pathspec's */
 -      max_prefix = common_prefix(pathspec);
 -      max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
 -
        if (pathspec->nr)
                ps_matched = xcalloc(pathspec->nr, 1);
  
@@@ -37,9 -42,9 +37,9 @@@
        for (i = 0; i < active_nr; i++) {
                const struct cache_entry *ce = active_cache[i];
  
 -              if (!S_ISGITLINK(ce->ce_mode) ||
 -                  !match_pathspec(pathspec, ce->name, ce_namelen(ce),
 -                                  max_prefix_len, ps_matched, 1))
 +              if (!match_pathspec(pathspec, ce->name, ce_namelen(ce),
 +                                  0, ps_matched, 1) ||
 +                  !S_ISGITLINK(ce->ce_mode))
                        continue;
  
                ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
@@@ -52,6 -57,7 +52,6 @@@
                         */
                        i++;
        }
 -      free(max_prefix);
  
        if (ps_matched && report_path_error(ps_matched, pathspec, prefix))
                result = -1;
@@@ -118,6 -124,7 +118,7 @@@ static int module_name(int argc, const 
  
        return 0;
  }
  static int clone_submodule(const char *path, const char *gitdir, const char *url,
                           const char *depth, const char *reference, int quiet)
  {
        argv_array_push(&cp.args, path);
  
        cp.git_cmd = 1;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.no_stdin = 1;
  
        return run_command(&cp);
@@@ -180,14 -187,18 +181,18 @@@ static int module_clone(int argc, cons
  
        const char *const git_submodule_helper_usage[] = {
                N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
-                  "[--reference <repository>] [--name <name>] [--url <url>]"
-                  "[--depth <depth>] [--] [<path>...]"),
+                  "[--reference <repository>] [--name <name>] [--depth <depth>] "
+                  "--url <url> --path <path>"),
                NULL
        };
  
        argc = parse_options(argc, argv, prefix, module_clone_options,
                             git_submodule_helper_usage, 0);
  
+       if (argc || !url || !path)
+               usage_with_options(git_submodule_helper_usage,
+                                  module_clone_options);
        strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
        sm_gitdir = strbuf_detach(&sb, NULL);
  
diff --combined config.c
index 9ba40bc1b039b9b65425dc4fa1bd9c7f1fcb0868,8570eec51dcc8a23e4219762b04eef73411cb613..8f66519e616ad9ebb6ee2540028cd2f04ce46103
+++ b/config.c
@@@ -24,7 -24,6 +24,7 @@@ struct config_source 
                        size_t pos;
                } buf;
        } u;
 +      const char *origin_type;
        const char *name;
        const char *path;
        int die_on_error;
@@@ -162,7 -161,7 +162,7 @@@ void git_config_push_parameter(const ch
  {
        struct strbuf env = STRBUF_INIT;
        const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
-       if (old) {
+       if (old && *old) {
                strbuf_addstr(&env, old);
                strbuf_addch(&env, ' ');
        }
@@@ -472,9 -471,9 +472,9 @@@ static int git_parse_source(config_fn_
                        break;
        }
        if (cf->die_on_error)
 -              die(_("bad config file line %d in %s"), cf->linenr, cf->name);
 +              die(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
        else
 -              return error(_("bad config file line %d in %s"), cf->linenr, cf->name);
 +              return error(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
  }
  
  static int parse_unit_factor(const char *end, uintmax_t *val)
@@@ -589,9 -588,9 +589,9 @@@ static void die_bad_number(const char *
        if (!value)
                value = "";
  
 -      if (cf && cf->name)
 -              die(_("bad numeric config value '%s' for '%s' in %s: %s"),
 -                  value, name, cf->name, reason);
 +      if (cf && cf->origin_type && cf->name)
 +              die(_("bad numeric config value '%s' for '%s' in %s %s: %s"),
 +                  value, name, cf->origin_type, cf->name, reason);
        die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
  }
  
@@@ -1062,13 -1061,11 +1062,13 @@@ static int do_config_from(struct config
  }
  
  static int do_config_from_file(config_fn_t fn,
 -              const char *name, const char *path, FILE *f, void *data)
 +              const char *origin_type, const char *name, const char *path, FILE *f,
 +              void *data)
  {
        struct config_source top;
  
        top.u.file = f;
 +      top.origin_type = origin_type;
        top.name = name;
        top.path = path;
        top.die_on_error = 1;
  
  static int git_config_from_stdin(config_fn_t fn, void *data)
  {
 -      return do_config_from_file(fn, "<stdin>", NULL, stdin, data);
 +      return do_config_from_file(fn, "standard input", "", NULL, stdin, data);
  }
  
  int git_config_from_file(config_fn_t fn, const char *filename, void *data)
        f = fopen(filename, "r");
        if (f) {
                flockfile(f);
 -              ret = do_config_from_file(fn, filename, filename, f, data);
 +              ret = do_config_from_file(fn, "file", filename, filename, f, data);
                funlockfile(f);
                fclose(f);
        }
        return ret;
  }
  
 -int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
 -                      size_t len, void *data)
 +int git_config_from_mem(config_fn_t fn, const char *origin_type,
 +                      const char *name, const char *buf, size_t len, void *data)
  {
        struct config_source top;
  
        top.u.buf.buf = buf;
        top.u.buf.len = len;
        top.u.buf.pos = 0;
 +      top.origin_type = origin_type;
        top.name = name;
        top.path = NULL;
        top.die_on_error = 0;
@@@ -1136,7 -1132,7 +1136,7 @@@ static int git_config_from_blob_sha1(co
                return error("reference '%s' does not point to a blob", name);
        }
  
 -      ret = git_config_from_buf(fn, name, buf, size, data);
 +      ret = git_config_from_mem(fn, "blob", name, buf, size, data);
        free(buf);
  
        return ret;
@@@ -1853,26 -1849,15 +1853,26 @@@ contline
        return offset;
  }
  
 -int git_config_set_in_file(const char *config_filename,
 -                      const char *key, const char *value)
 +int git_config_set_in_file_gently(const char *config_filename,
 +                                const char *key, const char *value)
  {
 -      return git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);
 +      return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);
  }
  
 -int git_config_set(const char *key, const char *value)
 +void git_config_set_in_file(const char *config_filename,
 +                          const char *key, const char *value)
  {
 -      return git_config_set_multivar(key, value, NULL, 0);
 +      git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);
 +}
 +
 +int git_config_set_gently(const char *key, const char *value)
 +{
 +      return git_config_set_multivar_gently(key, value, NULL, 0);
 +}
 +
 +void git_config_set(const char *key, const char *value)
 +{
 +      git_config_set_multivar(key, value, NULL, 0);
  }
  
  /*
@@@ -1917,7 -1902,7 +1917,7 @@@ static int git_config_parse_key_1(cons
         * Validate the key and while at it, lower case it for matching.
         */
        if (store_key)
 -              *store_key = xmalloc(strlen(key) + 1);
 +              *store_key = xmallocz(strlen(key));
  
        dot = 0;
        for (i = 0; key[i]; i++) {
                if (store_key)
                        (*store_key)[i] = c;
        }
 -      if (store_key)
 -              (*store_key)[i] = 0;
  
        return 0;
  
@@@ -1987,10 -1974,9 +1987,10 @@@ int git_config_key_is_valid(const char 
   * - the config file is removed and the lock file rename()d to it.
   *
   */
 -int git_config_set_multivar_in_file(const char *config_filename,
 -                              const char *key, const char *value,
 -                              const char *value_regex, int multi_replace)
 +int git_config_set_multivar_in_file_gently(const char *config_filename,
 +                                         const char *key, const char *value,
 +                                         const char *value_regex,
 +                                         int multi_replace)
  {
        int fd = -1, in_fd = -1;
        int ret;
@@@ -2217,27 -2203,11 +2217,27 @@@ write_err_out
  
  }
  
 -int git_config_set_multivar(const char *key, const char *value,
 -                      const char *value_regex, int multi_replace)
 +void git_config_set_multivar_in_file(const char *config_filename,
 +                                   const char *key, const char *value,
 +                                   const char *value_regex, int multi_replace)
 +{
 +      if (git_config_set_multivar_in_file_gently(config_filename, key, value,
 +                                                 value_regex, multi_replace) < 0)
 +              die(_("Could not set '%s' to '%s'"), key, value);
 +}
 +
 +int git_config_set_multivar_gently(const char *key, const char *value,
 +                                 const char *value_regex, int multi_replace)
  {
 -      return git_config_set_multivar_in_file(NULL, key, value, value_regex,
 -                                             multi_replace);
 +      return git_config_set_multivar_in_file_gently(NULL, key, value, value_regex,
 +                                                    multi_replace);
 +}
 +
 +void git_config_set_multivar(const char *key, const char *value,
 +                           const char *value_regex, int multi_replace)
 +{
 +      git_config_set_multivar_in_file(NULL, key, value, value_regex,
 +                                      multi_replace);
  }
  
  static int section_name_match (const char *buf, const char *name)
@@@ -2439,13 -2409,3 +2439,13 @@@ int parse_config_key(const char *var
  
        return 0;
  }
 +
 +const char *current_config_origin_type(void)
 +{
 +      return cf && cf->origin_type ? cf->origin_type : "command line";
 +}
 +
 +const char *current_config_name(void)
 +{
 +      return cf && cf->name ? cf->name : "";
 +}
diff --combined git-submodule.sh
index 43c68deee9db2b243d39eba704878eb7551ada2d,b1c056c71567522e7b1bdb94bacf10f70743e024..b9469e7db6ed3bb4844727897325f1dd5380303f
@@@ -192,6 -192,17 +192,17 @@@ isnumber(
        n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
  }
  
+ # Sanitize the local git environment for use within a submodule. We
+ # can't simply use clear_local_git_env since we want to preserve some
+ # of the settings from GIT_CONFIG_PARAMETERS.
+ sanitize_submodule_env()
+ {
+       save_config=$GIT_CONFIG_PARAMETERS
+       clear_local_git_env
+       GIT_CONFIG_PARAMETERS=$save_config
+       export GIT_CONFIG_PARAMETERS
+ }
  #
  # Add a new submodule to the working tree, .gitmodules and the index
  #
@@@ -347,9 -358,9 +358,9 @@@ Use -f if you really want to add it." >
                                echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
                        fi
                fi
-               git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" "$reference" "$depth" || exit
+               git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${depth:+"$depth"} || exit
                (
-                       clear_local_git_env
+                       sanitize_submodule_env
                        cd "$sm_path" &&
                        # ash fails to wordsplit ${branch:+-b "$branch"...}
                        case "$branch" in
@@@ -418,7 -429,7 +429,7 @@@ cmd_foreach(
                        name=$(git submodule--helper name "$sm_path")
                        (
                                prefix="$prefix$sm_path/"
-                               clear_local_git_env
+                               sanitize_submodule_env
                                cd "$sm_path" &&
                                sm_path=$(relative_path "$sm_path") &&
                                # we make $path available to scripts ...
@@@ -591,24 -602,6 +602,24 @@@ cmd_deinit(
        done
  }
  
-       clear_local_git_env
 +is_tip_reachable () (
-       clear_local_git_env
++      sanitize_submodule_env &&
 +      cd "$1" &&
 +      rev=$(git rev-list -n 1 "$2" --not --all 2>/dev/null) &&
 +      test -z "$rev"
 +)
 +
 +fetch_in_submodule () (
++      sanitize_submodule_env &&
 +      cd "$1" &&
 +      case "$2" in
 +      '')
 +              git fetch ;;
 +      *)
 +              git fetch $(get_default_remote) "$2" ;;
 +      esac
 +)
 +
  #
  # Update each submodule path to correct revision, using clone and checkout as needed
  #
@@@ -727,11 -720,11 +738,11 @@@ Maybe you want to use 'update --init'?"
  
                if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
                then
-                       git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$prefix" --path "$sm_path" --name "$name" --url "$url" "$reference" "$depth" || exit
+                       git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$prefix" --path "$sm_path" --name "$name" --url "$url" ${reference:+"$reference"} ${depth:+"$depth"} || exit
                        cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
-                       subsha1=$(clear_local_git_env; cd "$sm_path" &&
+                       subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
                                git rev-parse --verify HEAD) ||
                        die "$(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
                fi
                        if test -z "$nofetch"
                        then
                                # Fetch remote before determining tracking $sha1
-                               (clear_local_git_env; cd "$sm_path" && git-fetch) ||
+                               (sanitize_submodule_env; cd "$sm_path" && git-fetch) ||
                                die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
                        fi
-                       remote_name=$(clear_local_git_env; cd "$sm_path" && get_default_remote)
-                       sha1=$(clear_local_git_env; cd "$sm_path" &&
+                       remote_name=$(sanitize_submodule_env; cd "$sm_path" && get_default_remote)
+                       sha1=$(sanitize_submodule_env; cd "$sm_path" &&
                                git rev-parse --verify "${remote_name}/${branch}") ||
                        die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")"
                fi
                        then
                                # Run fetch only if $sha1 isn't present or it
                                # is not reachable from a ref.
 -                              (sanitize_submodule_env; cd "$sm_path" &&
 -                                      ( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
 -                                       test -z "$rev") || git-fetch)) ||
 +                              is_tip_reachable "$sm_path" "$sha1" ||
 +                              fetch_in_submodule "$sm_path" ||
                                die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
 +
 +                              # Now we tried the usual fetch, but $sha1 may
 +                              # not be reachable from any of the refs
 +                              is_tip_reachable "$sm_path" "$sha1" ||
 +                              fetch_in_submodule "$sm_path" "$sha1" ||
 +                              die "$(eval_gettext "Fetched in submodule path '\$displaypath', but it did not contain $sha1. Direct fetching of that commit failed.")"
                        fi
  
                        # Is this something we just cloned?
                                die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
                        esac
  
-                       if (clear_local_git_env; cd "$sm_path" && $command "$sha1")
+                       if (sanitize_submodule_env; cd "$sm_path" && $command "$sha1")
                        then
                                say "$say_msg"
                        elif test -n "$must_die_on_failure"
                then
                        (
                                prefix="$prefix$sm_path/"
-                               clear_local_git_env
+                               sanitize_submodule_env
                                cd "$sm_path" &&
                                eval cmd_update
                        )
  
  set_name_rev () {
        revname=$( (
-               clear_local_git_env
+               sanitize_submodule_env
                cd "$1" && {
                        git describe "$2" 2>/dev/null ||
                        git describe --tags "$2" 2>/dev/null ||
@@@ -1148,7 -1136,7 +1159,7 @@@ cmd_status(
                else
                        if test -z "$cached"
                        then
-                               sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD)
+                               sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD)
                        fi
                        set_name_rev "$sm_path" "$sha1"
                        say "+$sha1 $displaypath$revname"
                then
                        (
                                prefix="$displaypath/"
-                               clear_local_git_env
+                               sanitize_submodule_env
                                cd "$sm_path" &&
                                eval cmd_status
                        ) ||
@@@ -1232,7 -1220,7 +1243,7 @@@ cmd_sync(
                        if test -e "$sm_path"/.git
                        then
                        (
-                               clear_local_git_env
+                               sanitize_submodule_env
                                cd "$sm_path"
                                remote=$(get_default_remote)
                                git config remote."$remote".url "$sub_origin_url"
diff --combined submodule.c
index 62c4356c50d4a41381336559f4e9af27e520ad0d,d5988811142a29a56a20535b0532e36998d9020f..4b02da6987e12406d0825ee86c0313cb20da2420
@@@ -13,6 -13,7 +13,7 @@@
  #include "argv-array.h"
  #include "blob.h"
  #include "thread-utils.h"
+ #include "quote.h"
  
  static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
  static struct string_list changed_submodule_paths;
@@@ -69,7 -70,7 +70,7 @@@ int update_path_in_gitmodules(const cha
        strbuf_addstr(&entry, "submodule.");
        strbuf_addstr(&entry, submodule->name);
        strbuf_addstr(&entry, ".path");
 -      if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) {
 +      if (git_config_set_in_file_gently(".gitmodules", entry.buf, newpath) < 0) {
                /* Maybe the user already did that, don't error out here */
                warning(_("Could not update .gitmodules entry %s"), entry.buf);
                strbuf_release(&entry);
@@@ -123,7 -124,7 +124,7 @@@ static int add_submodule_odb(const cha
        struct strbuf objects_directory = STRBUF_INIT;
        struct alternate_object_database *alt_odb;
        int ret = 0;
 -      int alloc;
 +      size_t alloc;
  
        strbuf_git_path_submodule(&objects_directory, path, "objects/");
        if (!is_directory(objects_directory.buf)) {
                                        objects_directory.len))
                        goto done;
  
 -      alloc = objects_directory.len + 42; /* for "12/345..." sha1 */
 -      alt_odb = xmalloc(sizeof(*alt_odb) + alloc);
 +      alloc = st_add(objects_directory.len, 42); /* for "12/345..." sha1 */
 +      alt_odb = xmalloc(st_add(sizeof(*alt_odb), alloc));
        alt_odb->next = alt_odb_list;
        xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf);
        alt_odb->name = alt_odb->base + objects_directory.len;
@@@ -366,7 -367,7 +367,7 @@@ static int submodule_needs_pushing(cons
  
                argv[1] = sha1_to_hex(sha1);
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.out = -1;
@@@ -453,7 -454,7 +454,7 @@@ static int push_submodule(const char *p
                const char *argv[] = {"push", NULL};
  
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.dir = path;
@@@ -499,7 -500,7 +500,7 @@@ static int is_submodule_commit_present(
  
                argv[3] = sha1_to_hex(sha1);
                cp.argv = argv;
-               cp.env = local_repo_env;
+               prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.dir = path;
@@@ -682,7 -683,7 +683,7 @@@ static int get_next_submodule(struct ch
                if (is_directory(git_dir)) {
                        child_process_init(cp);
                        cp->dir = strbuf_detach(&submodule_path, NULL);
-                       cp->env = local_repo_env;
+                       prepare_submodule_repo_env(&cp->env_array);
                        cp->git_cmd = 1;
                        if (!spf->quiet)
                                strbuf_addf(err, "Fetching submodule %s%s\n",
        return 0;
  }
  
 -static int fetch_start_failure(struct child_process *cp,
 -                             struct strbuf *err,
 +static int fetch_start_failure(struct strbuf *err,
                               void *cb, void *task_cb)
  {
        struct submodule_parallel_fetch *spf = cb;
        return 0;
  }
  
 -static int fetch_finish(int retvalue, struct child_process *cp,
 -                      struct strbuf *err, void *cb, void *task_cb)
 +static int fetch_finish(int retvalue, struct strbuf *err,
 +                      void *cb, void *task_cb)
  {
        struct submodule_parallel_fetch *spf = cb;
  
@@@ -794,7 -796,7 +795,7 @@@ unsigned is_submodule_modified(const ch
                argv[2] = "-uno";
  
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
@@@ -855,7 -857,7 +856,7 @@@ int submodule_uses_gitfile(const char *
  
        /* Now test that all nested submodules use a gitfile too */
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.no_stderr = 1;
@@@ -888,7 -890,7 +889,7 @@@ int ok_to_remove_submodule(const char *
                return 0;
  
        cp.argv = argv;
-       cp.env = local_repo_env;
+       prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
@@@ -1086,11 -1088,23 +1087,21 @@@ void connect_work_tree_and_git_dir(cons
        /* Update core.worktree setting */
        strbuf_reset(&file_name);
        strbuf_addf(&file_name, "%s/config", git_dir);
 -      if (git_config_set_in_file(file_name.buf, "core.worktree",
 -                                 relative_path(real_work_tree, git_dir,
 -                                               &rel_path)))
 -              die(_("Could not set core.worktree in %s"),
 -                  file_name.buf);
 +      git_config_set_in_file(file_name.buf, "core.worktree",
 +                             relative_path(real_work_tree, git_dir,
 +                                           &rel_path));
  
        strbuf_release(&file_name);
        strbuf_release(&rel_path);
        free((void *)real_work_tree);
  }
+ void prepare_submodule_repo_env(struct argv_array *out)
+ {
+       const char * const *var;
+       for (var = local_repo_env; *var; var++) {
+               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
+                       argv_array_push(out, *var);
+       }
+ }
diff --combined t/t1300-repo-config.sh
index 6767da87cba9589222b033c80b605b832f708baf,c96774030475ff2006477106946946107ead39ed..1867a0fd419769195399864aa2ca7436c12005cd
@@@ -699,13 -699,11 +699,13 @@@ test_expect_success 'invalid unit' 
        echo 1auto >expect &&
        git config aninvalid.unit >actual &&
        test_cmp expect actual &&
 -      cat >expect <<-\EOF &&
 -      fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit
 -      EOF
        test_must_fail git config --int --get aninvalid.unit 2>actual &&
 -      test_i18ncmp expect actual
 +      test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
 +'
 +
 +test_expect_success 'invalid stdin config' '
 +      echo "[broken" | test_must_fail git config --list --file - >output 2>&1 &&
 +      test_i18ngrep "bad config line 1 in standard input" output
  '
  
  cat > expect << EOF
@@@ -959,15 -957,13 +959,15 @@@ Qsection.sub=section.val
  Qsection.sub=section.val5Q
  EOF
  test_expect_success '--null --list' '
 -      git config --null --list | nul_to_q >result &&
 +      git config --null --list >result.raw &&
 +      nul_to_q <result.raw >result &&
        echo >>result &&
        test_cmp expect result
  '
  
  test_expect_success '--null --get-regexp' '
 -      git config --null --get-regexp "val[0-9]" | nul_to_q >result &&
 +      git config --null --get-regexp "val[0-9]" >result.raw &&
 +      nul_to_q <result.raw >result &&
        echo >>result &&
        test_cmp expect result
  '
@@@ -1087,6 -1083,20 +1087,20 @@@ test_expect_success 'git -c complains a
        test_must_fail git -c "" rev-parse
  '
  
+ test_expect_success 'multiple git -c appends config' '
+       test_config alias.x "!git -c x.two=2 config --get-regexp ^x\.*" &&
+       cat >expect <<-\EOF &&
+       x.one 1
+       x.two 2
+       EOF
+       git -c x.one=1 x >actual &&
+       test_cmp expect actual
+ '
+ test_expect_success 'git -c is not confused by empty environment' '
+       GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
+ '
  test_expect_success 'git config --edit works' '
        git config -f tmp test.value no &&
        echo test.value=yes >expect &&
@@@ -1205,154 -1215,4 +1219,154 @@@ test_expect_success POSIXPERM,PERL 'pre
          "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
  '
  
 +! test_have_prereq MINGW ||
 +HOME="$(pwd)" # convert to Windows path
 +
 +test_expect_success 'set up --show-origin tests' '
 +      INCLUDE_DIR="$HOME/include" &&
 +      mkdir -p "$INCLUDE_DIR" &&
 +      cat >"$INCLUDE_DIR"/absolute.include <<-\EOF &&
 +              [user]
 +                      absolute = include
 +      EOF
 +      cat >"$INCLUDE_DIR"/relative.include <<-\EOF &&
 +              [user]
 +                      relative = include
 +      EOF
 +      cat >"$HOME"/.gitconfig <<-EOF &&
 +              [user]
 +                      global = true
 +                      override = global
 +              [include]
 +                      path = "$INCLUDE_DIR/absolute.include"
 +      EOF
 +      cat >.git/config <<-\EOF
 +              [user]
 +                      local = true
 +                      override = local
 +              [include]
 +                      path = ../include/relative.include
 +      EOF
 +'
 +
 +test_expect_success '--show-origin with --list' '
 +      cat >expect <<-EOF &&
 +              file:$HOME/.gitconfig   user.global=true
 +              file:$HOME/.gitconfig   user.override=global
 +              file:$HOME/.gitconfig   include.path=$INCLUDE_DIR/absolute.include
 +              file:$INCLUDE_DIR/absolute.include      user.absolute=include
 +              file:.git/config        user.local=true
 +              file:.git/config        user.override=local
 +              file:.git/config        include.path=../include/relative.include
 +              file:.git/../include/relative.include   user.relative=include
 +              command line:   user.cmdline=true
 +      EOF
 +      git -c user.cmdline=true config --list --show-origin >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success '--show-origin with --list --null' '
 +      cat >expect <<-EOF &&
 +              file:$HOME/.gitconfigQuser.global
 +              trueQfile:$HOME/.gitconfigQuser.override
 +              globalQfile:$HOME/.gitconfigQinclude.path
 +              $INCLUDE_DIR/absolute.includeQfile:$INCLUDE_DIR/absolute.includeQuser.absolute
 +              includeQfile:.git/configQuser.local
 +              trueQfile:.git/configQuser.override
 +              localQfile:.git/configQinclude.path
 +              ../include/relative.includeQfile:.git/../include/relative.includeQuser.relative
 +              includeQcommand line:Quser.cmdline
 +              trueQ
 +      EOF
 +      git -c user.cmdline=true config --null --list --show-origin >output.raw &&
 +      nul_to_q <output.raw >output &&
 +      # The here-doc above adds a newline that the --null output would not
 +      # include. Add it here to make the two comparable.
 +      echo >>output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success '--show-origin with single file' '
 +      cat >expect <<-\EOF &&
 +              file:.git/config        user.local=true
 +              file:.git/config        user.override=local
 +              file:.git/config        include.path=../include/relative.include
 +      EOF
 +      git config --local --list --show-origin >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success '--show-origin with --get-regexp' '
 +      cat >expect <<-EOF &&
 +              file:$HOME/.gitconfig   user.global true
 +              file:.git/config        user.local true
 +      EOF
 +      git config --show-origin --get-regexp "user\.[g|l].*" >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success '--show-origin getting a single key' '
 +      cat >expect <<-\EOF &&
 +              file:.git/config        local
 +      EOF
 +      git config --show-origin user.override >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success 'set up custom config file' '
 +      CUSTOM_CONFIG_FILE="file\" (dq) and spaces.conf" &&
 +      cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
 +              [user]
 +                      custom = true
 +      EOF
 +'
 +
 +test_expect_success !MINGW '--show-origin escape special file name characters' '
 +      cat >expect <<-\EOF &&
 +              file:"file\" (dq) and spaces.conf"      user.custom=true
 +      EOF
 +      git config --file "$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success '--show-origin stdin' '
 +      cat >expect <<-\EOF &&
 +              standard input: user.custom=true
 +      EOF
 +      git config --file - --show-origin --list <"$CUSTOM_CONFIG_FILE" >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success '--show-origin stdin with file include' '
 +      cat >"$INCLUDE_DIR"/stdin.include <<-EOF &&
 +              [user]
 +                      stdin = include
 +      EOF
 +      cat >expect <<-EOF &&
 +              file:$INCLUDE_DIR/stdin.include include
 +      EOF
 +      echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" \
 +              | git config --show-origin --includes --file - user.stdin >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success !MINGW '--show-origin blob' '
 +      cat >expect <<-\EOF &&
 +              blob:a9d9f9e555b5c6f07cbe09d3f06fe3df11e09c08   user.custom=true
 +      EOF
 +      blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
 +      git config --blob=$blob --show-origin --list >output &&
 +      test_cmp expect output
 +'
 +
 +test_expect_success !MINGW '--show-origin blob ref' '
 +      cat >expect <<-\EOF &&
 +              blob:"master:file\" (dq) and spaces.conf"       user.custom=true
 +      EOF
 +      git add "$CUSTOM_CONFIG_FILE" &&
 +      git commit -m "new config file" &&
 +      git config --blob=master:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
 +      test_cmp expect output
 +'
 +
  test_done