Merge branch 'ps/config-error'
authorJunio C Hamano <gitster@pobox.com>
Fri, 26 Feb 2016 21:37:19 +0000 (13:37 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 26 Feb 2016 21:37:19 +0000 (13:37 -0800)
Many codepaths forget to check return value from git_config_set();
the function is made to die() to make sure we do not proceed when
setting a configuration variable failed.

* ps/config-error:
config: rename git_config_set_or_die to git_config_set
config: rename git_config_set to git_config_set_gently
compat: die when unable to set core.precomposeunicode
sequencer: die on config error when saving replay opts
init-db: die on config errors when initializing empty repo
clone: die on config error in cmd_clone
remote: die on config error when manipulating remotes
remote: die on config error when setting/adding branches
remote: die on config error when setting URL
submodule--helper: die on config error when cloning module
submodule: die on config error when linking modules
branch: die on config error when editing branch description
branch: die on config error when unsetting upstream
branch: report errors in tracking branch setup
config: introduce set_or_die wrappers

1  2 
builtin/clone.c
builtin/config.c
builtin/remote.c
cache.h
config.c
submodule.c
t/t5505-remote.sh
diff --combined builtin/clone.c
index b8c39aa701d6fd53f5bbb121ef9b9f0e77ca9d6d,8a90cad5b534adbb4d1370f8f2365de21a934248..9ac6c014427908324c4a2d7ca79dbc858f5b622e
@@@ -47,7 -47,6 +47,7 @@@ static const char *real_git_dir
  static char *option_upload_pack = "git-upload-pack";
  static int option_verbosity;
  static int option_progress = -1;
 +static enum transport_family family;
  static struct string_list option_config;
  static struct string_list option_reference;
  static int option_dissociate;
@@@ -93,10 -92,6 +93,10 @@@ static struct option builtin_clone_opti
                   N_("separate git dir from working tree")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
                        N_("set config inside the new repository")),
 +      OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
 +                      TRANSPORT_FAMILY_IPV4),
 +      OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
 +                      TRANSPORT_FAMILY_IPV6),
        OPT_END()
  };
  
@@@ -344,7 -339,7 +344,7 @@@ static void copy_alternates(struct strb
        FILE *in = fopen(src->buf, "r");
        struct strbuf line = STRBUF_INIT;
  
 -      while (strbuf_getline(&line, in, '\n') != EOF) {
 +      while (strbuf_getline(&line, in) != EOF) {
                char *abs_path;
                if (!line.len || line.buf[0] == '#')
                        continue;
@@@ -641,11 -636,9 +641,11 @@@ static void update_remote_refs(const st
                struct strbuf head_ref = STRBUF_INIT;
                strbuf_addstr(&head_ref, branch_top);
                strbuf_addstr(&head_ref, "HEAD");
 -              create_symref(head_ref.buf,
 -                            remote_head_points_at->peer_ref->name,
 -                            msg);
 +              if (create_symref(head_ref.buf,
 +                                remote_head_points_at->peer_ref->name,
 +                                msg) < 0)
 +                      die("unable to update %s", head_ref.buf);
 +              strbuf_release(&head_ref);
        }
  }
  
@@@ -655,8 -648,7 +655,8 @@@ static void update_head(const struct re
        const char *head;
        if (our && skip_prefix(our->name, "refs/heads/", &head)) {
                /* Local default branch link */
 -              create_symref("HEAD", our->name, NULL);
 +              if (create_symref("HEAD", our->name, NULL) < 0)
 +                      die("unable to update HEAD");
                if (!option_bare) {
                        update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0,
                                   UPDATE_REFS_DIE_ON_ERR);
@@@ -740,7 -732,7 +740,7 @@@ static int checkout(void
  
  static int write_one_config(const char *key, const char *value, void *data)
  {
-       return git_config_set_multivar(key, value ? value : "true", "^$", 0);
+       return git_config_set_multivar_gently(key, value ? value : "true", "^$", 0);
  }
  
  static void write_config(struct string_list *config)
@@@ -975,7 -967,6 +975,7 @@@ int cmd_clone(int argc, const char **ar
        remote = remote_get(option_origin);
        transport = transport_get(remote, remote->url[0]);
        transport_set_verbosity(transport, option_verbosity, option_progress);
 +      transport->family = family;
  
        path = get_repo_path(remote->url[0], &is_bundle);
        is_local = option_local != 0 && path && !is_bundle;
diff --combined builtin/config.c
index 0f1422006f230b9b6a2e0975a8450465d039a753,c26d6e7fdd5d2896850ac909cb9ce04960520b0d..8602b216d876e5f68c725bd004350d8bdfe819a5
@@@ -3,7 -3,6 +3,7 @@@
  #include "color.h"
  #include "parse-options.h"
  #include "urlmatch.h"
 +#include "quote.h"
  
  static const char *const builtin_config_usage[] = {
        N_("git config [<options>]"),
@@@ -28,7 -27,6 +28,7 @@@ static int actions, types
  static const char *get_color_slot, *get_colorbool_slot;
  static int end_null;
  static int respect_includes = -1;
 +static int show_origin;
  
  #define ACTION_GET (1<<0)
  #define ACTION_GET_ALL (1<<1)
@@@ -83,7 -81,6 +83,7 @@@ static struct option builtin_config_opt
        OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
        OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
 +      OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
        OPT_END(),
  };
  
@@@ -94,28 -91,8 +94,28 @@@ static void check_argc(int argc, int mi
        usage_with_options(builtin_config_usage, builtin_config_options);
  }
  
 +static void show_config_origin(struct strbuf *buf)
 +{
 +      const char term = end_null ? '\0' : '\t';
 +
 +      strbuf_addstr(buf, current_config_origin_type());
 +      strbuf_addch(buf, ':');
 +      if (end_null)
 +              strbuf_addstr(buf, current_config_name());
 +      else
 +              quote_c_style(current_config_name(), buf, NULL, 0);
 +      strbuf_addch(buf, term);
 +}
 +
  static int show_all_config(const char *key_, const char *value_, void *cb)
  {
 +      if (show_origin) {
 +              struct strbuf buf = STRBUF_INIT;
 +              show_config_origin(&buf);
 +              /* Use fwrite as "buf" can contain \0's if "end_null" is set. */
 +              fwrite(buf.buf, 1, buf.len, stdout);
 +              strbuf_release(&buf);
 +      }
        if (!omit_values && value_)
                printf("%s%c%s%c", key_, delim, value_, term);
        else
@@@ -131,8 -108,6 +131,8 @@@ struct strbuf_list 
  
  static int format_config(struct strbuf *buf, const char *key_, const char *value_)
  {
 +      if (show_origin)
 +              show_config_origin(buf);
        if (show_keys)
                strbuf_addstr(buf, key_);
        if (!omit_values) {
@@@ -563,14 -538,6 +563,14 @@@ int cmd_config(int argc, const char **a
                error("--name-only is only applicable to --list or --get-regexp");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
 +
 +      if (show_origin && !(actions &
 +              (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
 +              error("--show-origin is only applicable to --get, --get-all, "
 +                        "--get-regexp, and --list.");
 +              usage_with_options(builtin_config_usage, builtin_config_options);
 +      }
 +
        if (actions == ACTION_LIST) {
                check_argc(argc, 0, 0);
                if (git_config_with_options(show_all_config, NULL,
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
-               ret = git_config_set_in_file(given_config_source.file, argv[0], value);
+               ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
                if (ret == CONFIG_NOTHING_SET)
                        error("cannot overwrite multiple values with a single value\n"
                        "       Use a regexp, --add or --replace-all to change %s.", argv[0]);
                check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
-               return git_config_set_multivar_in_file(given_config_source.file,
-                                                      argv[0], value, argv[2], 0);
+               return git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                             argv[0], value, argv[2], 0);
        }
        else if (actions == ACTION_ADD) {
                check_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
-               return git_config_set_multivar_in_file(given_config_source.file,
-                                                      argv[0], value,
-                                                      CONFIG_REGEX_NONE, 0);
+               return git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                             argv[0], value,
+                                                             CONFIG_REGEX_NONE, 0);
        }
        else if (actions == ACTION_REPLACE_ALL) {
                check_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
-               return git_config_set_multivar_in_file(given_config_source.file,
-                                                      argv[0], value, argv[2], 1);
+               return git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                             argv[0], value, argv[2], 1);
        }
        else if (actions == ACTION_GET) {
                check_argc(argc, 1, 2);
                check_write();
                check_argc(argc, 1, 2);
                if (argc == 2)
-                       return git_config_set_multivar_in_file(given_config_source.file,
-                                                              argv[0], NULL, argv[1], 0);
+                       return git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                                     argv[0], NULL, argv[1], 0);
                else
-                       return git_config_set_in_file(given_config_source.file,
-                                                     argv[0], NULL);
+                       return git_config_set_in_file_gently(given_config_source.file,
+                                                            argv[0], NULL);
        }
        else if (actions == ACTION_UNSET_ALL) {
                check_write();
                check_argc(argc, 1, 2);
-               return git_config_set_multivar_in_file(given_config_source.file,
-                                                      argv[0], NULL, argv[1], 1);
+               return git_config_set_multivar_in_file_gently(given_config_source.file,
+                                                             argv[0], NULL, argv[1], 1);
        }
        else if (actions == ACTION_RENAME_SECTION) {
                int ret;
diff --combined builtin/remote.c
index bd57f1b29badc050c74d8ae433cd6444e9dc9114,43136951b55be959d9fd6a2cc960b19f1a1484ac..fda5c2e53d28ae84f4876ca77ac9fefebee9ada2
@@@ -108,8 -108,8 +108,8 @@@ enum 
  #define MIRROR_PUSH 2
  #define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH)
  
- static int add_branch(const char *key, const char *branchname,
-               const char *remotename, int mirror, struct strbuf *tmp)
+ static void add_branch(const char *key, const char *branchname,
+                      const char *remotename, int mirror, struct strbuf *tmp)
  {
        strbuf_reset(tmp);
        strbuf_addch(tmp, '+');
        else
                strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
                                branchname, remotename, branchname);
-       return git_config_set_multivar(key, tmp->buf, "^$", 0);
+       git_config_set_multivar(key, tmp->buf, "^$", 0);
  }
  
  static const char mirror_advice[] =
@@@ -186,7 -186,10 +186,7 @@@ static int add(int argc, const char **a
        url = argv[1];
  
        remote = remote_get(name);
 -      if (remote && (remote->url_nr > 1 ||
 -                      (strcmp(name, remote->url[0]) &&
 -                              strcmp(url, remote->url[0])) ||
 -                      remote->fetch_refspec_nr))
 +      if (remote_is_configured(remote))
                die(_("remote %s already exists."), name);
  
        strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
                die(_("'%s' is not a valid remote name"), name);
  
        strbuf_addf(&buf, "remote.%s.url", name);
-       if (git_config_set(buf.buf, url))
-               return 1;
+       git_config_set(buf.buf, url);
  
        if (!mirror || mirror & MIRROR_FETCH) {
                strbuf_reset(&buf);
                if (track.nr == 0)
                        string_list_append(&track, "*");
                for (i = 0; i < track.nr; i++) {
-                       if (add_branch(buf.buf, track.items[i].string,
-                                      name, mirror, &buf2))
-                               return 1;
+                       add_branch(buf.buf, track.items[i].string,
+                                  name, mirror, &buf2);
                }
        }
  
        if (mirror & MIRROR_PUSH) {
                strbuf_reset(&buf);
                strbuf_addf(&buf, "remote.%s.mirror", name);
-               if (git_config_set(buf.buf, "true"))
-                       return 1;
+               git_config_set(buf.buf, "true");
        }
  
        if (fetch_tags != TAGS_DEFAULT) {
                strbuf_reset(&buf);
                strbuf_addf(&buf, "remote.%s.tagopt", name);
-               if (git_config_set(buf.buf,
-                       fetch_tags == TAGS_SET ? "--tags" : "--no-tags"))
-                       return 1;
+               git_config_set(buf.buf,
+                              fetch_tags == TAGS_SET ? "--tags" : "--no-tags");
        }
  
        if (fetch && fetch_remote(name))
  struct branch_info {
        char *remote_name;
        struct string_list merge;
 -      int rebase;
 +      enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase;
  };
  
  static struct string_list branch_list;
@@@ -308,9 -307,7 +304,9 @@@ static int config_read_branches(const c
                        if (v >= 0)
                                info->rebase = v;
                        else if (!strcmp(value, "preserve"))
 -                              info->rebase = 1;
 +                              info->rebase = NORMAL_REBASE;
 +                      else if (!strcmp(value, "interactive"))
 +                              info->rebase = INTERACTIVE_REBASE;
                }
        }
        return 0;
@@@ -589,25 -586,20 +585,20 @@@ static int migrate_file(struct remote *
  
        strbuf_addf(&buf, "remote.%s.url", remote->name);
        for (i = 0; i < remote->url_nr; i++)
-               if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
-                       return error(_("Could not append '%s' to '%s'"),
-                                       remote->url[i], buf.buf);
+               git_config_set_multivar(buf.buf, remote->url[i], "^$", 0);
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.push", remote->name);
        for (i = 0; i < remote->push_refspec_nr; i++)
-               if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
-                       return error(_("Could not append '%s' to '%s'"),
-                                       remote->push_refspec[i], buf.buf);
+               git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0);
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.fetch", remote->name);
        for (i = 0; i < remote->fetch_refspec_nr; i++)
-               if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
-                       return error(_("Could not append '%s' to '%s'"),
-                                       remote->fetch_refspec[i], buf.buf);
+               git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0);
        if (remote->origin == REMOTE_REMOTES)
                unlink_or_warn(git_path("remotes/%s", remote->name));
        else if (remote->origin == REMOTE_BRANCHES)
                unlink_or_warn(git_path("branches/%s", remote->name));
        return 0;
  }
  
@@@ -631,14 -623,14 +622,14 @@@ static int mv(int argc, const char **ar
        rename.remote_branches = &remote_branches;
  
        oldremote = remote_get(rename.old);
 -      if (!oldremote)
 +      if (!remote_is_configured(oldremote))
                die(_("No such remote: %s"), rename.old);
  
        if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
  
        newremote = remote_get(rename.new);
 -      if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
 +      if (remote_is_configured(newremote))
                die(_("remote %s already exists."), rename.new);
  
        strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
  
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.fetch", rename.new);
-       if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
-               return error(_("Could not remove config section '%s'"), buf.buf);
+       git_config_set_multivar(buf.buf, NULL, NULL, 1);
        strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
        for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
                char *ptr;
                                  "\tPlease update the configuration manually if necessary."),
                                buf2.buf);
  
-               if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
-                       return error(_("Could not append '%s'"), buf.buf);
+               git_config_set_multivar(buf.buf, buf2.buf, "^$", 0);
        }
  
        read_branches();
                if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
                        strbuf_reset(&buf);
                        strbuf_addf(&buf, "branch.%s.remote", item->string);
-                       if (git_config_set(buf.buf, rename.new)) {
-                               return error(_("Could not set '%s'"), buf.buf);
-                       }
+                       git_config_set(buf.buf, rename.new);
                }
        }
  
@@@ -770,7 -758,7 +757,7 @@@ static int rm(int argc, const char **ar
                usage_with_options(builtin_remote_rm_usage, options);
  
        remote = remote_get(argv[1]);
 -      if (!remote)
 +      if (!remote_is_configured(remote))
                die(_("No such remote: %s"), argv[1]);
  
        known_remotes.to_delete = remote;
                                strbuf_reset(&buf);
                                strbuf_addf(&buf, "branch.%s.%s",
                                                item->string, *k);
-                               if (git_config_set(buf.buf, NULL)) {
-                                       strbuf_release(&buf);
-                                       return -1;
-                               }
+                               git_config_set(buf.buf, NULL);
                        }
                }
        }
@@@ -979,9 -964,7 +963,9 @@@ static int show_local_info_item(struct 
  
        printf("    %-*s ", show_info->width, item->string);
        if (branch_info->rebase) {
 -              printf_ln(_("rebases onto remote %s"), merge->items[0].string);
 +              printf_ln(_(branch_info->rebase == INTERACTIVE_REBASE ?
 +                      "rebases interactively onto remote %s" :
 +                      "rebases onto remote %s"), merge->items[0].string);
                return 0;
        } else if (show_info->any_rebase) {
                printf_ln(_(" merges with remote %s"), merge->items[0].string);
@@@ -1410,24 -1393,20 +1394,20 @@@ static int update(int argc, const char 
  
  static int remove_all_fetch_refspecs(const char *remote, const char *key)
  {
-       return git_config_set_multivar(key, NULL, NULL, 1);
+       return git_config_set_multivar_gently(key, NULL, NULL, 1);
  }
  
- static int add_branches(struct remote *remote, const char **branches,
-                       const char *key)
+ static void add_branches(struct remote *remote, const char **branches,
+                        const char *key)
  {
        const char *remotename = remote->name;
        int mirror = remote->mirror;
        struct strbuf refspec = STRBUF_INIT;
  
        for (; *branches; branches++)
-               if (add_branch(key, *branches, remotename, mirror, &refspec)) {
-                       strbuf_release(&refspec);
-                       return 1;
-               }
+               add_branch(key, *branches, remotename, mirror, &refspec);
  
        strbuf_release(&refspec);
-       return 0;
  }
  
  static int set_remote_branches(const char *remotename, const char **branches,
  
        strbuf_addf(&key, "remote.%s.fetch", remotename);
  
 -      if (!remote_is_configured(remotename))
 -              die(_("No such remote '%s'"), remotename);
        remote = remote_get(remotename);
 +      if (!remote_is_configured(remote))
 +              die(_("No such remote '%s'"), remotename);
  
        if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
                strbuf_release(&key);
                return 1;
        }
-       if (add_branches(remote, branches, key.buf)) {
-               strbuf_release(&key);
-               return 1;
-       }
+       add_branches(remote, branches, key.buf);
  
        strbuf_release(&key);
        return 0;
@@@ -1495,9 -1471,9 +1472,9 @@@ static int get_url(int argc, const cha
  
        remotename = argv[0];
  
 -      if (!remote_is_configured(remotename))
 -              die(_("No such remote '%s'"), remotename);
        remote = remote_get(remotename);
 +      if (!remote_is_configured(remote))
 +              die(_("No such remote '%s'"), remotename);
  
        url_nr = 0;
        if (push_mode) {
@@@ -1563,9 -1539,9 +1540,9 @@@ static int set_url(int argc, const cha
        if (delete_mode)
                oldurl = newurl;
  
 -      if (!remote_is_configured(remotename))
 -              die(_("No such remote '%s'"), remotename);
        remote = remote_get(remotename);
 +      if (!remote_is_configured(remote))
 +              die(_("No such remote '%s'"), remotename);
  
        if (push_mode) {
                strbuf_addf(&name_buf, "remote.%s.pushurl", remotename);
        if ((!oldurl && !delete_mode) || add_mode) {
                if (add_mode)
                        git_config_set_multivar(name_buf.buf, newurl,
-                               "^$", 0);
+                                                      "^$", 0);
                else
                        git_config_set(name_buf.buf, newurl);
                strbuf_release(&name_buf);
                return 0;
        }
  
diff --combined cache.h
index 61c6e0abe39f0c0b0ff5991426853462f2f7eb24,dd39270f93f15274f14477eeef405d63d82df30e..d7ff46ec4a016c6ab7d233b9d4a196ecde623528
+++ b/cache.h
@@@ -9,7 -9,6 +9,7 @@@
  #include "convert.h"
  #include "trace.h"
  #include "string-list.h"
 +#include "pack-revindex.h"
  
  #include SHA1_HEADER
  #ifndef platform_SHA_CTX
@@@ -229,9 -228,7 +229,9 @@@ struct cache_entry 
  #error "CE_EXTENDED_FLAGS out of range"
  #endif
  
 +/* Forward structure decls */
  struct pathspec;
 +struct child_process;
  
  /*
   * Copy the sha1 and stat state of a cache entry from one to
@@@ -262,7 -259,6 +262,7 @@@ static inline unsigned create_ce_flags(
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
  #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
 +#define ce_intent_to_add(ce) ((ce)->ce_flags & CE_INTENT_TO_ADD)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
  static inline unsigned int create_ce_mode(unsigned int mode)
@@@ -1321,7 -1317,6 +1321,7 @@@ extern struct packed_git 
                 freshened:1,
                 do_not_close:1;
        unsigned char sha1[20];
 +      struct revindex_entry *revindex;
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
  } *packed_git;
@@@ -1489,7 -1484,7 +1489,7 @@@ extern int update_server_info(int)
  /* git_config_parse_key() returns these negated: */
  #define CONFIG_INVALID_KEY 1
  #define CONFIG_NO_SECTION_OR_NAME 2
- /* git_config_set(), git_config_set_multivar() return the above or these: */
+ /* git_config_set_gently(), git_config_set_multivar_gently() return the above or these: */
  #define CONFIG_NO_LOCK -1
  #define CONFIG_INVALID_FILE 3
  #define CONFIG_NO_WRITE 4
@@@ -1508,8 -1503,8 +1508,8 @@@ struct git_config_source 
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
 -extern int git_config_from_buf(config_fn_t fn, const char *name,
 -                             const char *buf, size_t len, void *data);
 +extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
 +                                      const char *name, const char *buf, size_t len, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern void git_config(config_fn_t fn, void *);
@@@ -1527,12 -1522,16 +1527,16 @@@ extern int git_config_bool(const char *
  extern int git_config_maybe_bool(const char *, const char *);
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_pathname(const char **, const char *, const char *);
- extern int git_config_set_in_file(const char *, const char *, const char *);
- extern int git_config_set(const char *, const char *);
+ extern int git_config_set_in_file_gently(const char *, const char *, const char *);
+ extern void git_config_set_in_file(const char *, const char *, const char *);
+ extern int git_config_set_gently(const char *, const char *);
+ extern void git_config_set(const char *, const char *);
  extern int git_config_parse_key(const char *, char **, int *);
  extern int git_config_key_is_valid(const char *key);
- extern int git_config_set_multivar(const char *, const char *, const char *, int);
- extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
+ extern int git_config_set_multivar_gently(const char *, const char *, const char *, int);
+ extern void git_config_set_multivar(const char *, const char *, const char *, int);
+ extern int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);
+ extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern int git_config_rename_section_in_file(const char *, const char *, const char *);
  extern const char *git_etc_gitconfig(void);
@@@ -1548,8 -1547,6 +1552,8 @@@ extern const char *get_log_output_encod
  extern const char *get_commit_output_encoding(void);
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 +extern const char *current_config_origin_type(void);
 +extern const char *current_config_name(void);
  
  struct config_include_data {
        int depth;
@@@ -1628,14 -1625,6 +1632,14 @@@ extern int git_config_get_bool(const ch
  extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
  extern int git_config_get_maybe_bool(const char *key, int *dest);
  extern int git_config_get_pathname(const char *key, const char **dest);
 +extern int git_config_get_untracked_cache(void);
 +
 +/*
 + * This is a hack for test programs like test-dump-untracked-cache to
 + * ensure that they do not modify the untracked cache when reading it.
 + * Do not use it otherwise!
 + */
 +extern int ignore_untracked_cache_config;
  
  struct key_value_info {
        const char *filename;
@@@ -1689,7 -1678,6 +1693,7 @@@ extern int pager_use_color
  extern int term_columns(void);
  extern int decimal_width(uintmax_t);
  extern int check_pager_config(const char *cmd);
 +extern void prepare_pager_args(struct child_process *, const char *pager);
  
  extern const char *editor_program;
  extern const char *askpass_program;
diff --combined config.c
index d732e0b3d830a3c27b05fd8a7ad3e5ebc77a25e0,325c3eaf9d1c9af8b0857ed1997fea1b6f237da6..9ba40bc1b039b9b65425dc4fa1bd9c7f1fcb0868
+++ 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;
@@@ -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;
@@@ -1598,30 -1594,6 +1598,30 @@@ int git_config_get_pathname(const char 
        return ret;
  }
  
 +int git_config_get_untracked_cache(void)
 +{
 +      int val = -1;
 +      const char *v;
 +
 +      /* Hack for test programs like test-dump-untracked-cache */
 +      if (ignore_untracked_cache_config)
 +              return -1;
 +
 +      if (!git_config_get_maybe_bool("core.untrackedcache", &val))
 +              return val;
 +
 +      if (!git_config_get_value("core.untrackedcache", &v)) {
 +              if (!strcasecmp(v, "keep"))
 +                      return -1;
 +
 +              error("unknown core.untrackedCache value '%s'; "
 +                    "using 'keep' default value", v);
 +              return -1;
 +      }
 +
 +      return -1; /* default value */
 +}
 +
  NORETURN
  void git_die_config_linenr(const char *key, const char *filename, int linenr)
  {
@@@ -1853,15 -1825,26 +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);
  }
  
  /*
@@@ -1906,7 -1889,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;
  
@@@ -1976,9 -1961,10 +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;
@@@ -2205,11 -2191,27 +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_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)
  {
-       return git_config_set_multivar_in_file(NULL, key, value, value_regex,
-                                              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)
@@@ -2411,13 -2413,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 submodule.c
index ac61c656115b65c45aad971024678e9353bc5ed3,b58d4ee945883fb3b69caf3c606e174bd677acf7..24fb81ac62e2161e79cbd5ab1f2a241663c2a3a1
@@@ -12,7 -12,6 +12,7 @@@
  #include "sha1-array.h"
  #include "argv-array.h"
  #include "blob.h"
 +#include "thread-utils.h"
  
  static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
  static struct string_list changed_submodule_paths;
@@@ -69,7 -68,7 +69,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 -122,7 +123,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;
@@@ -611,28 -610,37 +611,28 @@@ static void calculate_changed_submodule
        initialized_fetch_ref_tips = 0;
  }
  
 -int fetch_populated_submodules(const struct argv_array *options,
 -                             const char *prefix, int command_line_option,
 -                             int quiet)
 +struct submodule_parallel_fetch {
 +      int count;
 +      struct argv_array args;
 +      const char *work_tree;
 +      const char *prefix;
 +      int command_line_option;
 +      int quiet;
 +      int result;
 +};
 +#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0}
 +
 +static int get_next_submodule(struct child_process *cp,
 +                            struct strbuf *err, void *data, void **task_cb)
  {
 -      int i, result = 0;
 -      struct child_process cp = CHILD_PROCESS_INIT;
 -      struct argv_array argv = ARGV_ARRAY_INIT;
 -      const char *work_tree = get_git_work_tree();
 -      if (!work_tree)
 -              goto out;
 -
 -      if (read_cache() < 0)
 -              die("index file corrupt");
 -
 -      argv_array_push(&argv, "fetch");
 -      for (i = 0; i < options->argc; i++)
 -              argv_array_push(&argv, options->argv[i]);
 -      argv_array_push(&argv, "--recurse-submodules-default");
 -      /* default value, "--submodule-prefix" and its value are added later */
 -
 -      cp.env = local_repo_env;
 -      cp.git_cmd = 1;
 -      cp.no_stdin = 1;
 -
 -      calculate_changed_submodule_paths();
 +      int ret = 0;
 +      struct submodule_parallel_fetch *spf = data;
  
 -      for (i = 0; i < active_nr; i++) {
 +      for (; spf->count < active_nr; spf->count++) {
                struct strbuf submodule_path = STRBUF_INIT;
                struct strbuf submodule_git_dir = STRBUF_INIT;
                struct strbuf submodule_prefix = STRBUF_INIT;
 -              const struct cache_entry *ce = active_cache[i];
 +              const struct cache_entry *ce = active_cache[spf->count];
                const char *git_dir, *default_argv;
                const struct submodule *submodule;
  
                        submodule = submodule_from_name(null_sha1, ce->name);
  
                default_argv = "yes";
 -              if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
 +              if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
                        if (submodule &&
                            submodule->fetch_recurse !=
                                                RECURSE_SUBMODULES_NONE) {
                                        default_argv = "on-demand";
                                }
                        }
 -              } else if (command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
 +              } else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
                        if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
                                continue;
                        default_argv = "on-demand";
                }
  
 -              strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
 +              strbuf_addf(&submodule_path, "%s/%s", spf->work_tree, ce->name);
                strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
 -              strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name);
 +              strbuf_addf(&submodule_prefix, "%s%s/", spf->prefix, ce->name);
                git_dir = read_gitfile(submodule_git_dir.buf);
                if (!git_dir)
                        git_dir = submodule_git_dir.buf;
                if (is_directory(git_dir)) {
 -                      if (!quiet)
 -                              printf("Fetching submodule %s%s\n", prefix, ce->name);
 -                      cp.dir = submodule_path.buf;
 -                      argv_array_push(&argv, default_argv);
 -                      argv_array_push(&argv, "--submodule-prefix");
 -                      argv_array_push(&argv, submodule_prefix.buf);
 -                      cp.argv = argv.argv;
 -                      if (run_command(&cp))
 -                              result = 1;
 -                      argv_array_pop(&argv);
 -                      argv_array_pop(&argv);
 -                      argv_array_pop(&argv);
 +                      child_process_init(cp);
 +                      cp->dir = strbuf_detach(&submodule_path, NULL);
 +                      cp->env = local_repo_env;
 +                      cp->git_cmd = 1;
 +                      if (!spf->quiet)
 +                              strbuf_addf(err, "Fetching submodule %s%s\n",
 +                                          spf->prefix, ce->name);
 +                      argv_array_init(&cp->args);
 +                      argv_array_pushv(&cp->args, spf->args.argv);
 +                      argv_array_push(&cp->args, default_argv);
 +                      argv_array_push(&cp->args, "--submodule-prefix");
 +                      argv_array_push(&cp->args, submodule_prefix.buf);
 +                      ret = 1;
                }
                strbuf_release(&submodule_path);
                strbuf_release(&submodule_git_dir);
                strbuf_release(&submodule_prefix);
 +              if (ret) {
 +                      spf->count++;
 +                      return 1;
 +              }
        }
 -      argv_array_clear(&argv);
 +      return 0;
 +}
 +
 +static int fetch_start_failure(struct child_process *cp,
 +                             struct strbuf *err,
 +                             void *cb, void *task_cb)
 +{
 +      struct submodule_parallel_fetch *spf = cb;
 +
 +      spf->result = 1;
 +
 +      return 0;
 +}
 +
 +static int fetch_finish(int retvalue, struct child_process *cp,
 +                      struct strbuf *err, void *cb, void *task_cb)
 +{
 +      struct submodule_parallel_fetch *spf = cb;
 +
 +      if (retvalue)
 +              spf->result = 1;
 +
 +      return 0;
 +}
 +
 +int fetch_populated_submodules(const struct argv_array *options,
 +                             const char *prefix, int command_line_option,
 +                             int quiet, int max_parallel_jobs)
 +{
 +      int i;
 +      struct submodule_parallel_fetch spf = SPF_INIT;
 +
 +      spf.work_tree = get_git_work_tree();
 +      spf.command_line_option = command_line_option;
 +      spf.quiet = quiet;
 +      spf.prefix = prefix;
 +
 +      if (!spf.work_tree)
 +              goto out;
 +
 +      if (read_cache() < 0)
 +              die("index file corrupt");
 +
 +      argv_array_push(&spf.args, "fetch");
 +      for (i = 0; i < options->argc; i++)
 +              argv_array_push(&spf.args, options->argv[i]);
 +      argv_array_push(&spf.args, "--recurse-submodules-default");
 +      /* default value, "--submodule-prefix" and its value are added later */
 +
 +      calculate_changed_submodule_paths();
 +      run_processes_parallel(max_parallel_jobs,
 +                             get_next_submodule,
 +                             fetch_start_failure,
 +                             fetch_finish,
 +                             &spf);
 +
 +      argv_array_clear(&spf.args);
  out:
        string_list_clear(&changed_submodule_paths, 1);
 -      return result;
 +      return spf.result;
  }
  
  unsigned is_submodule_modified(const char *path, int ignore_untracked)
@@@ -1087,11 -1034,9 +1087,9 @@@ 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);
diff --combined t/t5505-remote.sh
index 949725e9bcc5b4d6358ed28ca5675b92f9b7c7fa,013e03dee2651f6891782325671f0764bb02f41f..dd2e6ce34e7dff0e59e917fd4e3aefa01181778c
@@@ -51,11 -51,6 +51,11 @@@ test_expect_success setup 
        git clone one test
  '
  
 +test_expect_success 'add remote whose URL agrees with url.<...>.insteadOf' '
 +      test_config url.git@host.com:team/repo.git.insteadOf myremote &&
 +      git remote add myremote git@host.com:team/repo.git
 +'
 +
  test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
        (
                cd test &&
@@@ -90,7 -85,7 +90,7 @@@ test_expect_success C_LOCALE_OUTPUT 'ch
  test_expect_success 'remote forces tracking branches' '
        (
                cd test &&
 -              case `git config remote.second.fetch` in
 +              case $(git config remote.second.fetch) in
                +*) true ;;
                 *) false ;;
                esac
@@@ -144,39 -139,6 +144,39 @@@ test_expect_success 'remove remote prot
        )
  '
  
 +test_expect_success 'remove errors out early when deleting non-existent branch' '
 +      (
 +              cd test &&
 +              echo "fatal: No such remote: foo" >expect &&
 +              test_must_fail git remote rm foo 2>actual &&
 +              test_i18ncmp expect actual
 +      )
 +'
 +
 +test_expect_success 'rename errors out early when deleting non-existent branch' '
 +      (
 +              cd test &&
 +              echo "fatal: No such remote: foo" >expect &&
 +              test_must_fail git remote rename foo bar 2>actual &&
 +              test_i18ncmp expect actual
 +      )
 +'
 +
 +test_expect_success 'add existing foreign_vcs remote' '
 +      test_config remote.foo.vcs bar &&
 +      echo "fatal: remote foo already exists." >expect &&
 +      test_must_fail git remote add foo bar 2>actual &&
 +      test_i18ncmp expect actual
 +'
 +
 +test_expect_success 'add existing foreign_vcs remote' '
 +      test_config remote.foo.vcs bar &&
 +      test_config remote.bar.vcs bar &&
 +      echo "fatal: remote bar already exists." >expect &&
 +      test_must_fail git remote rename foo bar 2>actual &&
 +      test_i18ncmp expect actual
 +'
 +
  cat >test/expect <<EOF
  * remote origin
    Fetch URL: $(pwd)/one
@@@ -970,6 -932,15 +970,15 @@@ test_expect_success 'get-url on new rem
        echo foo | get_url_test --push --all someremote
  '
  
+ test_expect_success 'remote set-url with locked config' '
+       test_when_finished "rm -f .git/config.lock" &&
+       git config --get-all remote.someremote.url >expect &&
+       >.git/config.lock &&
+       test_must_fail git remote set-url someremote baz &&
+       git config --get-all remote.someremote.url >actual &&
+       cmp expect actual
+ '
  test_expect_success 'remote set-url bar' '
        git remote set-url someremote bar &&
        echo bar >expect &&