]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ab/config-multi-and-nonbool'
authorJunio C Hamano <gitster@pobox.com>
Thu, 6 Apr 2023 20:38:28 +0000 (13:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 6 Apr 2023 20:38:29 +0000 (13:38 -0700)
Assorted config API updates.

* ab/config-multi-and-nonbool:
  for-each-repo: with bad config, don't conflate <path> and <cmd>
  config API: add "string" version of *_value_multi(), fix segfaults
  config API users: test for *_get_value_multi() segfaults
  for-each-repo: error on bad --config
  config API: have *_multi() return an "int" and take a "dest"
  versioncmp.c: refactor config reading next commit
  config API: add and use a "git_config_get()" family of functions
  config tests: add "NULL" tests for *_get_value_multi()
  config tests: cover blind spots in git_die_config() tests

12 files changed:
1  2 
builtin/gc.c
builtin/log.c
builtin/submodule--helper.c
builtin/worktree.c
config.c
config.h
pack-bitmap.c
submodule.c
t/t0068-for-each-repo.sh
t/t5304-prune.sh
t/t5310-pack-bitmaps.sh
t/t7004-tag.sh

diff --combined builtin/gc.c
index c58fe8c936c63da46134c5ab764228baaa0b88ef,9497bdf23e4bfad786ed3eb1781a44e00df46a0f..efc1b9a0fda0fa5ceed7366a6f0732a8c69eff24
@@@ -11,7 -11,6 +11,7 @@@
   */
  
  #include "builtin.h"
 +#include "hex.h"
  #include "repository.h"
  #include "config.h"
  #include "tempfile.h"
@@@ -977,9 -976,9 +977,9 @@@ struct write_loose_object_data 
  
  static int loose_object_auto_limit = 100;
  
 -static int loose_object_count(const struct object_id *oid,
 -                             const char *path,
 -                             void *data)
 +static int loose_object_count(const struct object_id *oid UNUSED,
 +                            const char *path UNUSED,
 +                            void *data)
  {
        int *count = (int*)data;
        if (++(*count) >= loose_object_auto_limit)
@@@ -1004,15 -1003,15 +1004,15 @@@ static int loose_object_auto_condition(
                                             NULL, NULL, &count);
  }
  
 -static int bail_on_loose(const struct object_id *oid,
 -                       const char *path,
 -                       void *data)
 +static int bail_on_loose(const struct object_id *oid UNUSED,
 +                       const char *path UNUSED,
 +                       void *data UNUSED)
  {
        return 1;
  }
  
  static int write_loose_object_to_stdin(const struct object_id *oid,
 -                                     const char *path,
 +                                     const char *path UNUSED,
                                       void *data)
  {
        struct write_loose_object_data *d = (struct write_loose_object_data *)data;
@@@ -1494,7 -1493,6 +1494,6 @@@ static int maintenance_register(int arg
        };
        int found = 0;
        const char *key = "maintenance.repo";
-       char *config_value;
        char *maintpath = get_maintpath();
        struct string_list_item *item;
        const struct string_list *list;
        git_config_set("maintenance.auto", "false");
  
        /* Set maintenance strategy, if unset */
-       if (!git_config_get_string("maintenance.strategy", &config_value))
-               free(config_value);
-       else
+       if (git_config_get("maintenance.strategy"))
                git_config_set("maintenance.strategy", "incremental");
  
-       list = git_config_get_value_multi(key);
-       if (list) {
+       if (!git_config_get_string_multi(key, &list)) {
                for_each_string_list_item(item, list) {
                        if (!strcmp(maintpath, item->string)) {
                                found = 1;
@@@ -1581,11 -1576,10 +1577,10 @@@ static int maintenance_unregister(int a
        if (config_file) {
                git_configset_init(&cs);
                git_configset_add_file(&cs, config_file);
-               list = git_configset_get_value_multi(&cs, key);
-       } else {
-               list = git_config_get_value_multi(key);
        }
-       if (list) {
+       if (!(config_file
+             ? git_configset_get_string_multi(&cs, key, &list)
+             : git_config_get_string_multi(key, &list))) {
                for_each_string_list_item(item, list) {
                        if (!strcmp(maintpath, item->string)) {
                                found = 1;
diff --combined builtin/log.c
index 4693385e8ed307ad104c5238284f4930286c8c1e,cd17f3117125227e0f2c695b018c97d4c95ecd76..0c6556b2d783637d1d42d7edbe2a0593344a965b
@@@ -4,10 -4,9 +4,10 @@@
   * (C) Copyright 2006 Linus Torvalds
   *             2006 Junio Hamano
   */
 -#define USE_THE_INDEX_COMPATIBILITY_MACROS
 -#include "cache.h"
 +#include "git-compat-util.h"
 +#include "alloc.h"
  #include "config.h"
 +#include "hex.h"
  #include "refs.h"
  #include "object-store.h"
  #include "color.h"
@@@ -54,11 -53,9 +54,11 @@@ static int decoration_style
  static int decoration_given;
  static int use_mailmap_config = 1;
  static unsigned int force_in_body_from;
 +static int stdout_mboxrd;
  static const char *fmt_patch_subject_prefix = "PATCH";
  static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
  static const char *fmt_pretty;
 +static int format_no_prefix;
  
  static const char * const builtin_log_usage[] = {
        N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
@@@ -185,10 -182,10 +185,10 @@@ static void set_default_decoration_filt
        int i;
        char *value = NULL;
        struct string_list *include = decoration_filter->include_ref_pattern;
-       const struct string_list *config_exclude =
-                       git_config_get_value_multi("log.excludeDecoration");
+       const struct string_list *config_exclude;
  
-       if (config_exclude) {
+       if (!git_config_get_string_multi("log.excludeDecoration",
+                                        &config_exclude)) {
                struct string_list_item *item;
                for_each_string_list_item(item, config_exclude)
                        string_list_append(decoration_filter->exclude_ref_config_pattern,
@@@ -439,7 -436,7 +439,7 @@@ static void log_show_early(struct rev_i
        setitimer(ITIMER_REAL, &early_output_timer, NULL);
  }
  
 -static void early_output(int signal)
 +static void early_output(int signal UNUSED)
  {
        show_early_output = log_show_early;
  }
@@@ -604,6 -601,8 +604,6 @@@ static int git_log_config(const char *v
                return 0;
        }
  
 -      if (git_gpg_config(var, value, cb) < 0)
 -              return -1;
        return git_diff_ui_config(var, value, cb);
  }
  
@@@ -1008,8 -1007,6 +1008,8 @@@ static int git_format_config(const cha
        if (!strcmp(var, "format.attach")) {
                if (value && *value)
                        default_attach = xstrdup(value);
 +              else if (value && !*value)
 +                      FREE_AND_NULL(default_attach);
                else
                        default_attach = xstrdup(git_version_string);
                return 0;
                cover_from_description_mode = parse_cover_from_description(value);
                return 0;
        }
 +      if (!strcmp(var, "format.mboxrd")) {
 +              stdout_mboxrd = git_config_bool(var, value);
 +              return 0;
 +      }
 +      if (!strcmp(var, "format.noprefix")) {
 +              format_no_prefix = 1;
 +              return 0;
 +      }
 +
 +      /*
 +       * ignore some porcelain config which would otherwise be parsed by
 +       * git_diff_ui_config(), via git_log_config(); we can't just avoid
 +       * diff_ui_config completely, because we do care about some ui options
 +       * like color.
 +       */
 +      if (!strcmp(var, "diff.noprefix"))
 +              return 0;
  
        return git_log_config(var, value, cb);
  }
@@@ -1891,7 -1871,6 +1891,7 @@@ int cmd_format_patch(int argc, const ch
        struct strbuf rdiff1 = STRBUF_INIT;
        struct strbuf rdiff2 = STRBUF_INIT;
        struct strbuf rdiff_title = STRBUF_INIT;
 +      struct strbuf sprefix = STRBUF_INIT;
        int creation_factor = -1;
  
        const struct option builtin_format_patch_options[] = {
        s_r_opt.def = "HEAD";
        s_r_opt.revarg_opt = REVARG_COMMITTISH;
  
 +      if (format_no_prefix)
 +              diff_set_noprefix(&rev.diffopt);
 +
        if (default_attach) {
                rev.mime_boundary = default_attach;
                rev.no_inline = 1;
                cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
  
        if (reroll_count) {
 -              struct strbuf sprefix = STRBUF_INIT;
 -
                strbuf_addf(&sprefix, "%s v%s",
                            rev.subject_prefix, reroll_count);
                rev.reroll_count = reroll_count;
 -              rev.subject_prefix = strbuf_detach(&sprefix, NULL);
 +              rev.subject_prefix = sprefix.buf;
        }
  
        for (i = 0; i < extra_hdr.nr; i++) {
  
        /* Always generate a patch */
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 +      rev.always_show_header = 1;
  
        rev.zero_commit = zero_commit;
        rev.patch_name_max = fmt_patch_name_max;
                                  rev.diffopt.close_file, "--output",
                                  !!output_directory, "--output-directory");
  
 +      if (use_stdout && stdout_mboxrd)
 +              rev.commit_format = CMIT_FMT_MBOXRD;
 +
        if (use_stdout) {
                setup_pager();
        } else if (!rev.diffopt.close_file) {
@@@ -2403,7 -2377,6 +2403,7 @@@ done
        strbuf_release(&rdiff1);
        strbuf_release(&rdiff2);
        strbuf_release(&rdiff_title);
 +      strbuf_release(&sprefix);
        free(to_free);
        if (rev.ref_message_ids)
                string_list_clear(rev.ref_message_ids, 0);
index 65a053261a952b8b012a70f976569ee101ebfc9f,2f678adbcc60210bd4bd8f479443d5a0503b11aa..6cd807471e721fc4ab5e7ff3af90df1cf55338b6
@@@ -1,7 -1,5 +1,7 @@@
 -#define USE_THE_INDEX_COMPATIBILITY_MACROS
 +#define USE_THE_INDEX_VARIABLE
  #include "builtin.h"
 +#include "alloc.h"
 +#include "hex.h"
  #include "repository.h"
  #include "cache.h"
  #include "config.h"
@@@ -115,9 -113,10 +115,9 @@@ static char *resolve_relative_url(cons
  }
  
  /* the result should be freed by the caller. */
 -static char *get_submodule_displaypath(const char *path, const char *prefix)
 +static char *get_submodule_displaypath(const char *path, const char *prefix,
 +                                     const char *super_prefix)
  {
 -      const char *super_prefix = get_super_prefix();
 -
        if (prefix && super_prefix) {
                BUG("cannot have prefix '%s' and superprefix '%s'",
                    prefix, super_prefix);
@@@ -197,11 -196,11 +197,11 @@@ static int module_list_compute(const ch
        if (pathspec->nr)
                ps_matched = xcalloc(pathspec->nr, 1);
  
 -      if (read_cache() < 0)
 +      if (repo_read_index(the_repository) < 0)
                die(_("index file corrupt"));
  
 -      for (i = 0; i < active_nr; i++) {
 -              const struct cache_entry *ce = active_cache[i];
 +      for (i = 0; i < the_index.cache_nr; i++) {
 +              const struct cache_entry *ce = the_index.cache[i];
  
                if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
                                    0, ps_matched, 1) ||
  
                ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
                list->entries[list->nr++] = ce;
 -              while (i + 1 < active_nr &&
 -                     !strcmp(ce->name, active_cache[i + 1]->name))
 +              while (i + 1 < the_index.cache_nr &&
 +                     !strcmp(ce->name, the_index.cache[i + 1]->name))
                        /*
                         * Skip entries with the same name in different stages
                         * to make sure an entry is returned only once.
@@@ -280,7 -279,6 +280,7 @@@ struct foreach_cb 
        int argc;
        const char **argv;
        const char *prefix;
 +      const char *super_prefix;
        int quiet;
        int recursive;
  };
@@@ -296,8 -294,7 +296,8 @@@ static void runcommand_in_submodule_cb(
        struct child_process cp = CHILD_PROCESS_INIT;
        char *displaypath;
  
 -      displaypath = get_submodule_displaypath(path, info->prefix);
 +      displaypath = get_submodule_displaypath(path, info->prefix,
 +                                              info->super_prefix);
  
        sub = submodule_from_path(the_repository, null_oid(), path);
  
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
  
 -              strvec_pushl(&cpr.args, "--super-prefix", NULL);
 -              strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive",
                             NULL);
 +              strvec_pushl(&cpr.args, "--super-prefix", NULL);
 +              strvec_pushf(&cpr.args, "%s/", displaypath);
  
                if (info->quiet)
                        strvec_push(&cpr.args, "--quiet");
@@@ -394,7 -391,6 +394,7 @@@ static int module_foreach(int argc, con
        struct pathspec pathspec = { 0 };
        struct module_list list = MODULE_LIST_INIT;
        struct option module_foreach_options[] = {
 +              OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&info.quiet, N_("suppress output of entering each submodule command")),
                OPT_BOOL(0, "recursive", &info.recursive,
                         N_("recurse into nested submodules")),
@@@ -439,13 -435,11 +439,13 @@@ static int starts_with_dot_dot_slash(co
  
  struct init_cb {
        const char *prefix;
 +      const char *super_prefix;
        unsigned int flags;
  };
  #define INIT_CB_INIT { 0 }
  
  static void init_submodule(const char *path, const char *prefix,
 +                         const char *super_prefix,
                           unsigned int flags)
  {
        const struct submodule *sub;
        const char *upd;
        char *url = NULL, *displaypath;
  
 -      displaypath = get_submodule_displaypath(path, prefix);
 +      displaypath = get_submodule_displaypath(path, prefix, super_prefix);
  
        sub = submodule_from_path(the_repository, null_oid(), path);
  
@@@ -529,8 -523,7 +529,8 @@@ static void init_submodule_cb(const str
  {
        struct init_cb *info = cb_data;
  
 -      init_submodule(list_item->name, info->prefix, info->flags);
 +      init_submodule(list_item->name, info->prefix, info->super_prefix,
 +                     info->flags);
  }
  
  static int module_init(int argc, const char **argv, const char *prefix)
         * 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"))
+       if (!argc && !git_config_get("submodule.active"))
                module_list_active(&list);
  
        info.prefix = prefix;
@@@ -577,7 -570,6 +577,7 @@@ cleanup
  
  struct status_cb {
        const char *prefix;
 +      const char *super_prefix;
        unsigned int flags;
  };
  #define STATUS_CB_INIT { 0 }
@@@ -616,7 -608,7 +616,7 @@@ static int handle_submodule_head_ref(co
  
  static void status_submodule(const char *path, const struct object_id *ce_oid,
                             unsigned int ce_flags, const char *prefix,
 -                           unsigned int flags)
 +                           const char *super_prefix, unsigned int flags)
  {
        char *displaypath;
        struct strvec diff_files_args = STRVEC_INIT;
                die(_("no submodule mapping found in .gitmodules for path '%s'"),
                      path);
  
 -      displaypath = get_submodule_displaypath(path, prefix);
 +      displaypath = get_submodule_displaypath(path, prefix, super_prefix);
  
        if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
                print_status(flags, 'U', path, null_oid(), displaypath);
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
  
 -              strvec_push(&cpr.args, "--super-prefix");
 -              strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "status",
                             "--recursive", NULL);
 +              strvec_push(&cpr.args, "--super-prefix");
 +              strvec_pushf(&cpr.args, "%s/", displaypath);
  
                if (flags & OPT_CACHED)
                        strvec_push(&cpr.args, "--cached");
@@@ -717,7 -709,7 +717,7 @@@ static void status_submodule_cb(const s
        struct status_cb *info = cb_data;
  
        status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
 -                       info->prefix, info->flags);
 +                       info->prefix, info->super_prefix, info->flags);
  }
  
  static int module_status(int argc, const char **argv, const char *prefix)
        struct module_list list = MODULE_LIST_INIT;
        int quiet = 0;
        struct option module_status_options[] = {
 +              OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&quiet, N_("suppress submodule status output")),
                OPT_BIT(0, "cached", &info.flags, N_("use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
                OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
@@@ -796,7 -787,6 +796,7 @@@ struct summary_cb 
        int argc;
        const char **argv;
        const char *prefix;
 +      const char *super_prefix;
        unsigned int cached: 1;
        unsigned int for_status: 1;
        unsigned int files: 1;
@@@ -958,8 -948,7 +958,8 @@@ static void generate_submodule_summary(
                dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
        }
  
 -      displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
 +      displaypath = get_submodule_displaypath(p->sm_path, info->prefix,
 +                                              info->super_prefix);
  
        if (!missing_src && !missing_dst) {
                struct child_process cp_rev_list = CHILD_PROCESS_INIT;
@@@ -1054,7 -1043,7 +1054,7 @@@ static void prepare_submodule_summary(s
  }
  
  static void submodule_summary_callback(struct diff_queue_struct *q,
 -                                     struct diff_options *options,
 +                                     struct diff_options *options UNUSED,
                                       void *data)
  {
        int i;
@@@ -1121,13 -1110,13 +1121,13 @@@ static int compute_summary_module_list(
        if (!info->cached) {
                if (diff_cmd == DIFF_INDEX)
                        setup_work_tree();
 -              if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 -                      perror("read_cache_preload");
 +              if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) {
 +                      perror("repo_read_index_preload");
                        ret = -1;
                        goto cleanup;
                }
 -      } else if (read_cache() < 0) {
 -              perror("read_cache");
 +      } else if (repo_read_index(the_repository) < 0) {
 +              perror("repo_read_cache");
                ret = -1;
                goto cleanup;
        }
@@@ -1214,13 -1203,12 +1214,13 @@@ static int module_summary(int argc, con
  
  struct sync_cb {
        const char *prefix;
 +      const char *super_prefix;
        unsigned int flags;
  };
  #define SYNC_CB_INIT { 0 }
  
  static void sync_submodule(const char *path, const char *prefix,
 -                         unsigned int flags)
 +                         const char *super_prefix, unsigned int flags)
  {
        const struct submodule *sub;
        char *remote_key = NULL;
                super_config_url = xstrdup("");
        }
  
 -      displaypath = get_submodule_displaypath(path, prefix);
 +      displaypath = get_submodule_displaypath(path, prefix, super_prefix);
  
        if (!(flags & OPT_QUIET))
                printf(_("Synchronizing submodule url for '%s'\n"),
                cpr.dir = path;
                prepare_submodule_repo_env(&cpr.env);
  
 -              strvec_push(&cpr.args, "--super-prefix");
 -              strvec_pushf(&cpr.args, "%s/", displaypath);
                strvec_pushl(&cpr.args, "submodule--helper", "sync",
                             "--recursive", NULL);
 +              strvec_push(&cpr.args, "--super-prefix");
 +              strvec_pushf(&cpr.args, "%s/", displaypath);
 +
  
                if (flags & OPT_QUIET)
                        strvec_push(&cpr.args, "--quiet");
@@@ -1315,8 -1302,7 +1315,8 @@@ static void sync_submodule_cb(const str
  {
        struct sync_cb *info = cb_data;
  
 -      sync_submodule(list_item->name, info->prefix, info->flags);
 +      sync_submodule(list_item->name, info->prefix, info->super_prefix,
 +                     info->flags);
  }
  
  static int module_sync(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int recursive = 0;
        struct option module_sync_options[] = {
 +              OPT__SUPER_PREFIX(&info.super_prefix),
                OPT__QUIET(&quiet, N_("suppress output of synchronizing submodule url")),
                OPT_BOOL(0, "recursive", &recursive,
                        N_("recurse into nested submodules")),
@@@ -1380,7 -1365,7 +1380,7 @@@ static void deinit_submodule(const cha
        if (!sub || !sub->name)
                goto cleanup;
  
 -      displaypath = get_submodule_displaypath(path, prefix);
 +      displaypath = get_submodule_displaypath(path, prefix, NULL);
  
        /* remove the submodule work tree (unless the user already did it) */
        if (is_directory(path)) {
                                          ".git file by using absorbgitdirs."),
                                        displaypath);
  
 -                      absorb_git_dir_into_superproject(path);
 +                      absorb_git_dir_into_superproject(path, NULL);
  
                }
  
@@@ -1898,7 -1883,6 +1898,7 @@@ static void submodule_update_clone_rele
  
  struct update_data {
        const char *prefix;
 +      const char *super_prefix;
        char *displaypath;
        enum submodule_update_type update_default;
        struct object_id suboid;
@@@ -1974,8 -1958,7 +1974,8 @@@ static int prepare_to_clone_next_submod
        enum submodule_update_type update_type;
        char *key;
        const struct update_data *ud = suc->update_data;
 -      char *displaypath = get_submodule_displaypath(ce->name, ud->prefix);
 +      char *displaypath = get_submodule_displaypath(ce->name, ud->prefix,
 +                                                    ud->super_prefix);
        struct strbuf sb = STRBUF_INIT;
        int needs_cloning = 0;
        int need_free_url = 0;
@@@ -2134,9 -2117,9 +2134,9 @@@ static int update_clone_get_next_task(s
        return 0;
  }
  
 -static int update_clone_start_failure(struct strbuf *err,
 +static int update_clone_start_failure(struct strbuf *err UNUSED,
                                      void *suc_cb,
 -                                    void *idx_task_cb)
 +                                    void *idx_task_cb UNUSED)
  {
        struct submodule_update_clone *suc = suc_cb;
  
@@@ -2455,11 -2438,11 +2455,11 @@@ static void update_data_to_args(const s
  {
        enum submodule_update_type update_type = update_data->update_default;
  
 +      strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
        if (update_data->displaypath) {
                strvec_push(args, "--super-prefix");
                strvec_pushf(args, "%s/", update_data->displaypath);
        }
 -      strvec_pushl(args, "submodule--helper", "update", "--recursive", NULL);
        strvec_pushf(args, "--jobs=%d", update_data->max_jobs);
        if (update_data->quiet)
                strvec_push(args, "--quiet");
@@@ -2625,8 -2608,7 +2625,8 @@@ static int update_submodules(struct upd
                        goto fail;
  
                update_data->displaypath = get_submodule_displaypath(
 -                      update_data->sm_path, update_data->prefix);
 +                      update_data->sm_path, update_data->prefix,
 +                      update_data->super_prefix);
                code = update_submodule(update_data);
                FREE_AND_NULL(update_data->displaypath);
  fail:
@@@ -2652,7 -2634,6 +2652,7 @@@ static int module_update(int argc, cons
                LIST_OBJECTS_FILTER_INIT;
        int ret;
        struct option module_update_options[] = {
 +              OPT__SUPER_PREFIX(&opt.super_prefix),
                OPT__FORCE(&opt.force, N_("force checkout updates"), 0),
                OPT_BOOL(0, "init", &opt.init,
                         N_("initialize uninitialized submodules before update")),
                 * 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"))
+               if (!argc && !git_config_get("submodule.active"))
                        module_list_active(&list);
  
                info.prefix = opt.prefix;
 +              info.super_prefix = opt.super_prefix;
                if (opt.quiet)
                        info.flags |= OPT_QUIET;
  
@@@ -2766,7 -2746,7 +2766,7 @@@ cleanup
        return ret;
  }
  
 -static int push_check(int argc, const char **argv, const char *prefix)
 +static int push_check(int argc, const char **argv, const char *prefix UNUSED)
  {
        struct remote *remote;
        const char *superproject_head;
@@@ -2848,9 -2828,7 +2848,9 @@@ static int absorb_git_dirs(int argc, co
        int i;
        struct pathspec pathspec = { 0 };
        struct module_list list = MODULE_LIST_INIT;
 +      const char *super_prefix = NULL;
        struct option embed_gitdir_options[] = {
 +              OPT__SUPER_PREFIX(&super_prefix),
                OPT_END()
        };
        const char *const git_submodule_helper_usage[] = {
                goto cleanup;
  
        for (i = 0; i < list.nr; i++)
 -              absorb_git_dir_into_superproject(list.entries[i]->name);
 +              absorb_git_dir_into_superproject(list.entries[i]->name,
 +                                               super_prefix);
  
        ret = 0;
  cleanup:
@@@ -2899,7 -2876,7 +2899,7 @@@ static int module_set_url(int argc, con
        config_name = xstrfmt("submodule.%s.url", path);
  
        config_set_in_gitmodules_file_gently(config_name, newurl);
 -      sync_submodule(path, prefix, quiet ? OPT_QUIET : 0);
 +      sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0);
  
        free(config_name);
  
@@@ -3142,7 -3119,6 +3142,6 @@@ static int config_submodule_in_gitmodul
  static void configure_added_submodule(struct add_data *add_data)
  {
        char *key;
-       const char *val;
        struct child_process add_submod = CHILD_PROCESS_INIT;
        struct child_process add_gitmodules = CHILD_PROCESS_INIT;
  
         * is_submodule_active(), since that function needs to find
         * out the value of "submodule.active" again anyway.
         */
-       if (!git_config_get_string_tmp("submodule.active", &val)) {
+       if (!git_config_get("submodule.active")) {
                /*
                 * If the submodule being added isn't already covered by the
                 * current configured pathspec, set the submodule's active flag
@@@ -3210,7 -3186,7 +3209,7 @@@ static void die_on_index_match(const ch
        const char *args[] = { path, NULL };
        parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
  
 -      if (read_cache_preload(NULL) < 0)
 +      if (repo_read_index_preload(the_repository, NULL, 0) < 0)
                die(_("index file corrupt"));
  
        if (ps.nr) {
                ensure_full_index(&the_index);
  
                /*
 -               * Since there is only one pathspec, we just need
 -               * need to check ps_matched[0] to know if a cache
 -               * entry matched.
 +               * Since there is only one pathspec, we just need to
 +               * check ps_matched[0] to know if a cache entry matched.
                 */
 -              for (i = 0; i < active_nr; i++) {
 -                      ce_path_match(&the_index, active_cache[i], &ps,
 +              for (i = 0; i < the_index.cache_nr; i++) {
 +                      ce_path_match(&the_index, the_index.cache[i], &ps,
                                      ps_matched);
  
                        if (ps_matched[0]) {
                                if (!force)
                                        die(_("'%s' already exists in the index"),
                                            path);
 -                              if (!S_ISGITLINK(active_cache[i]->ce_mode))
 +                              if (!S_ISGITLINK(the_index.cache[i]->ce_mode))
                                        die(_("'%s' already exists in the index "
                                              "and is not a submodule"), path);
                                break;
@@@ -3375,6 -3352,8 +3374,6 @@@ cleanup
  
  int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
  {
 -      const char *cmd = argv[0];
 -      const char *subcmd;
        parse_opt_subcommand_fn *fn = NULL;
        const char *const usage[] = {
                N_("git submodule--helper <command>"),
                OPT_END()
        };
        argc = parse_options(argc, argv, prefix, options, usage, 0);
 -      subcmd = argv[0];
 -
 -      if (strcmp(subcmd, "clone") && strcmp(subcmd, "update") &&
 -          strcmp(subcmd, "foreach") && strcmp(subcmd, "status") &&
 -          strcmp(subcmd, "sync") && strcmp(subcmd, "absorbgitdirs") &&
 -          get_super_prefix())
 -              /*
 -               * xstrfmt() rather than "%s %s" to keep the translated
 -               * string identical to git.c's.
 -               */
 -              die(_("%s doesn't support --super-prefix"),
 -                  xstrfmt("'%s %s'", cmd, subcmd));
  
        return fn(argc, argv, prefix);
  }
diff --combined builtin/worktree.c
index 80d05e246d8c4615e6858a700a08d842575ff165,6d086cc2828e3973c443af40ff82263b191a2433..476325ef98fa5c0e05723c7de145e8756a311777
@@@ -3,7 -3,6 +3,7 @@@
  #include "config.h"
  #include "builtin.h"
  #include "dir.h"
 +#include "hex.h"
  #include "parse-options.h"
  #include "strvec.h"
  #include "branch.h"
@@@ -174,7 -173,7 +174,7 @@@ static void prune_worktrees(void
  {
        struct strbuf reason = STRBUF_INIT;
        struct strbuf main_path = STRBUF_INIT;
 -      struct string_list kept = STRING_LIST_INIT_NODUP;
 +      struct string_list kept = STRING_LIST_INIT_DUP;
        DIR *dir = opendir(git_path("worktrees"));
        struct dirent *d;
        if (!dir)
                if (should_prune_worktree(d->d_name, &reason, &path, expire))
                        prune_worktree(d->d_name, reason.buf);
                else if (path)
 -                      string_list_append(&kept, path)->util = xstrdup(d->d_name);
 +                      string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
        }
        closedir(dir);
  
        strbuf_add_absolute_path(&main_path, get_git_common_dir());
        /* massage main worktree absolute path to match 'gitdir' content */
        strbuf_strip_suffix(&main_path, "/.");
 -      string_list_append(&kept, strbuf_detach(&main_path, NULL));
 +      string_list_append_nodup(&kept, strbuf_detach(&main_path, NULL));
        prune_dups(&kept);
        string_list_clear(&kept, 1);
  
@@@ -320,7 -319,6 +320,6 @@@ static void copy_filtered_worktree_conf
  
        if (file_exists(from_file)) {
                struct config_set cs = { { 0 } };
-               const char *core_worktree;
                int bare;
  
                if (safe_create_leading_directories(to_file) ||
                                to_file, "core.bare", NULL, "true", 0))
                        error(_("failed to unset '%s' in '%s'"),
                                "core.bare", to_file);
-               if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
+               if (!git_configset_get(&cs, "core.worktree") &&
                        git_config_set_in_file_gently(to_file,
                                                        "core.worktree", NULL))
                        error(_("failed to unset '%s' in '%s'"),
@@@ -630,7 -628,6 +629,7 @@@ static int add(int ac, const char **av
                         N_("try to match the new branch name with a remote-tracking branch")),
                OPT_END()
        };
 +      int ret;
  
        memset(&opts, 0, sizeof(opts));
        opts.checkout = 1;
                die(_("--[no-]track can only be used if a new branch is created"));
        }
  
 -      UNLEAK(path);
 -      UNLEAK(opts);
 -      return add_worktree(path, branch, &opts);
 +      ret = add_worktree(path, branch, &opts);
 +      free(path);
 +      return ret;
  }
  
  static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
@@@ -924,7 -921,7 +923,7 @@@ static int unlock_worktree(int ac, cons
  
  static void validate_no_submodules(const struct worktree *wt)
  {
 -      struct index_state istate = { NULL };
 +      struct index_state istate = INDEX_STATE_INIT(the_repository);
        struct strbuf path = STRBUF_INIT;
        int i, found_submodules = 0;
  
diff --combined config.c
index 0b2679994e1a387241edbc3db710ea383a6dc1db,92d9e7e968ee94299cafa36753bbadc77e3593d1..a30490fd58c0995b57c9a7ec30a08c8ffe123911
+++ b/config.c
@@@ -5,13 -5,11 +5,13 @@@
   * Copyright (C) Johannes Schindelin, 2005
   *
   */
 -#include "cache.h"
 +#include "git-compat-util.h"
 +#include "alloc.h"
  #include "date.h"
  #include "branch.h"
  #include "config.h"
  #include "environment.h"
 +#include "ident.h"
  #include "repository.h"
  #include "lockfile.h"
  #include "exec-cmd.h"
@@@ -23,7 -21,6 +23,7 @@@
  #include "utf8.h"
  #include "dir.h"
  #include "color.h"
 +#include "replace-object.h"
  #include "refs.h"
  #include "worktree.h"
  
@@@ -1163,26 -1160,21 +1163,26 @@@ static int git_parse_signed(const char 
        if (value && *value) {
                char *end;
                intmax_t val;
 -              uintmax_t uval;
 -              uintmax_t factor;
 +              intmax_t factor;
 +
 +              if (max < 0)
 +                      BUG("max must be a positive integer");
  
                errno = 0;
                val = strtoimax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
 +              if (end == value) {
 +                      errno = EINVAL;
 +                      return 0;
 +              }
                factor = get_unit_factor(end);
                if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
 -              uval = val < 0 ? -val : val;
 -              if (unsigned_mult_overflows(factor, uval) ||
 -                  factor * uval > max) {
 +              if ((val < 0 && -max / factor > val) ||
 +                  (val > 0 && max / factor < val)) {
                        errno = ERANGE;
                        return 0;
                }
@@@ -1201,19 -1193,10 +1201,19 @@@ static int git_parse_unsigned(const cha
                uintmax_t val;
                uintmax_t factor;
  
 +              /* negative values would be accepted by strtoumax */
 +              if (strchr(value, '-')) {
 +                      errno = EINVAL;
 +                      return 0;
 +              }
                errno = 0;
                val = strtoumax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
 +              if (end == value) {
 +                      errno = EINVAL;
 +                      return 0;
 +              }
                factor = get_unit_factor(end);
                if (!factor) {
                        errno = EINVAL;
@@@ -1686,7 -1669,7 +1686,7 @@@ static int git_default_core_config(cons
                        comment_line_char = value[0];
                        auto_comment_line_char = 0;
                } else
 -                      return error(_("core.commentChar should only be one character"));
 +                      return error(_("core.commentChar should only be one ASCII character"));
                return 0;
        }
  
@@@ -2292,23 -2275,29 +2292,29 @@@ void read_very_early_config(config_fn_
        config_with_options(cb, data, NULL, &opts);
  }
  
- static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+ RESULT_MUST_BE_USED
+ static int configset_find_element(struct config_set *cs, const char *key,
+                                 struct config_set_element **dest)
  {
        struct config_set_element k;
        struct config_set_element *found_entry;
        char *normalized_key;
+       int ret;
        /*
         * `key` may come from the user, so normalize it before using it
         * for querying entries from the hashmap.
         */
-       if (git_config_parse_key(key, &normalized_key, NULL))
-               return NULL;
+       ret = git_config_parse_key(key, &normalized_key, NULL);
+       if (ret)
+               return ret;
  
        hashmap_entry_init(&k.ent, strhash(normalized_key));
        k.key = normalized_key;
        found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
        free(normalized_key);
-       return found_entry;
+       *dest = found_entry;
+       return 0;
  }
  
  static int configset_add_value(struct config_set *cs, const char *key, const char *value)
        struct string_list_item *si;
        struct configset_list_item *l_item;
        struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
+       int ret;
  
-       e = configset_find_element(cs, key);
+       ret = configset_find_element(cs, key, &e);
+       if (ret)
+               return ret;
        /*
         * Since the keys are being fed by git_config*() callback mechanism, they
         * are already normalized. So simply add them without any further munging.
@@@ -2412,24 -2404,65 +2421,65 @@@ int git_configset_add_file(struct confi
  int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
  {
        const struct string_list *values = NULL;
+       int ret;
        /*
         * Follows "last one wins" semantic, i.e., if there are multiple matches for the
         * queried key in the files of the configset, the value returned will be the last
         * value in the value list for that key.
         */
-       values = git_configset_get_value_multi(cs, key);
+       if ((ret = git_configset_get_value_multi(cs, key, &values)))
+               return ret;
  
-       if (!values)
-               return 1;
        assert(values->nr > 0);
        *value = values->items[values->nr - 1].string;
        return 0;
  }
  
- const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+ int git_configset_get_value_multi(struct config_set *cs, const char *key,
+                                 const struct string_list **dest)
+ {
+       struct config_set_element *e;
+       int ret;
+       if ((ret = configset_find_element(cs, key, &e)))
+               return ret;
+       else if (!e)
+               return 1;
+       *dest = &e->value_list;
+       return 0;
+ }
+ static int check_multi_string(struct string_list_item *item, void *util)
+ {
+       return item->string ? 0 : config_error_nonbool(util);
+ }
+ int git_configset_get_string_multi(struct config_set *cs, const char *key,
+                                  const struct string_list **dest)
+ {
+       int ret;
+       if ((ret = git_configset_get_value_multi(cs, key, dest)))
+               return ret;
+       if ((ret = for_each_string_list((struct string_list *)*dest,
+                                       check_multi_string, (void *)key)))
+               return ret;
+       return 0;
+ }
+ int git_configset_get(struct config_set *cs, const char *key)
  {
-       struct config_set_element *e = configset_find_element(cs, key);
-       return e ? &e->value_list : NULL;
+       struct config_set_element *e;
+       int ret;
+       if ((ret = configset_find_element(cs, key, &e)))
+               return ret;
+       else if (!e)
+               return 1;
+       return 0;
  }
  
  int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
@@@ -2568,6 -2601,12 +2618,12 @@@ void repo_config(struct repository *rep
        configset_iter(repo->config, fn, data);
  }
  
+ int repo_config_get(struct repository *repo, const char *key)
+ {
+       git_config_check_init(repo);
+       return git_configset_get(repo->config, key);
+ }
  int repo_config_get_value(struct repository *repo,
                          const char *key, const char **value)
  {
        return git_configset_get_value(repo->config, key, value);
  }
  
- const struct string_list *repo_config_get_value_multi(struct repository *repo,
-                                                     const char *key)
+ int repo_config_get_value_multi(struct repository *repo, const char *key,
+                               const struct string_list **dest)
+ {
+       git_config_check_init(repo);
+       return git_configset_get_value_multi(repo->config, key, dest);
+ }
+ int repo_config_get_string_multi(struct repository *repo, const char *key,
+                                const struct string_list **dest)
  {
        git_config_check_init(repo);
-       return git_configset_get_value_multi(repo->config, key);
+       return git_configset_get_string_multi(repo->config, key, dest);
  }
  
  int repo_config_get_string(struct repository *repo,
@@@ -2682,14 -2728,25 +2745,25 @@@ void git_config_clear(void
        repo_config_clear(the_repository);
  }
  
+ int git_config_get(const char *key)
+ {
+       return repo_config_get(the_repository, key);
+ }
  int git_config_get_value(const char *key, const char **value)
  {
        return repo_config_get_value(the_repository, key, value);
  }
  
- const struct string_list *git_config_get_value_multi(const char *key)
+ int git_config_get_value_multi(const char *key, const struct string_list **dest)
+ {
+       return repo_config_get_value_multi(the_repository, key, dest);
+ }
+ int git_config_get_string_multi(const char *key,
+                               const struct string_list **dest)
  {
-       return repo_config_get_value_multi(the_repository, key);
+       return repo_config_get_string_multi(the_repository, key, dest);
  }
  
  int git_config_get_string(const char *key, char **dest)
@@@ -2836,7 -2893,8 +2910,8 @@@ void git_die_config(const char *key, co
                error_fn(err, params);
                va_end(params);
        }
-       values = git_config_get_value_multi(key);
+       if (git_config_get_value_multi(key, &values))
+               BUG("for key '%s' we must have a value to report on", key);
        kv_info = values->items[values->nr - 1].util;
        git_die_config_linenr(key, kv_info->filename, kv_info->linenr);
  }
@@@ -3157,7 -3215,7 +3232,7 @@@ int git_config_set_gently(const char *k
  int repo_config_set_worktree_gently(struct repository *r,
                                    const char *key, const char *value)
  {
 -      /* Only use worktree-specific config if it is is already enabled. */
 +      /* Only use worktree-specific config if it is already enabled. */
        if (repository_format_worktree_config) {
                char *file = repo_git_path(r, "config.worktree");
                int ret = git_config_set_multivar_in_file_gently(
diff --combined config.h
index 7606246531a798a0f400a38742323626ae0d9524,65e569e739eca45e19f5b26d3c670eb69feffc4f..4a6e3f19e5db72d7feb631ccf60ca7ffc448db30
+++ b/config.h
@@@ -447,13 -447,43 +447,34 @@@ void git_configset_init(struct config_s
   */
  int git_configset_add_file(struct config_set *cs, const char *filename);
  
 -/**
 - * Parses command line options and environment variables, and adds the
 - * variable-value pairs to the `config_set`. Returns 0 on success, or -1
 - * if there is an error in parsing. The caller decides whether to free
 - * the incomplete configset or continue using it when the function
 - * returns -1.
 - */
 -int git_configset_add_parameters(struct config_set *cs);
 -
  /**
   * Finds and returns the value list, sorted in order of increasing priority
   * for the configuration variable `key` and config set `cs`. When the
-  * configuration variable `key` is not found, returns NULL. The caller
-  * should not free or modify the returned pointer, as it is owned by the cache.
+  * configuration variable `key` is not found, returns 1 without touching
+  * `value`.
+  *
+  * The key will be parsed for validity with git_config_parse_key(), on
+  * error a negative value will be returned.
+  *
+  * The caller should not free or modify the returned pointer, as it is
+  * owned by the cache.
   */
- const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+ RESULT_MUST_BE_USED
+ int git_configset_get_value_multi(struct config_set *cs, const char *key,
+                                 const struct string_list **dest);
+ /**
+  * A validation wrapper for git_configset_get_value_multi() which does
+  * for it what git_configset_get_string() does for
+  * git_configset_get_value().
+  *
+  * The configuration syntax allows for "[section] key", which will
+  * give us a NULL entry in the "struct string_list", as opposed to
+  * "[section] key =" which is the empty string. Most users of the API
+  * are not prepared to handle NULL in a "struct string_list".
+  */
+ int git_configset_get_string_multi(struct config_set *cs, const char *key,
+                                  const struct string_list **dest);
  
  /**
   * Clears `config_set` structure, removes all saved variable-value pairs.
@@@ -465,6 -495,13 +486,13 @@@ void git_configset_clear(struct config_
   * value in the 'dest' pointer.
   */
  
+ /**
+  * git_configset_get() returns negative values on error, see
+  * repo_config_get() below.
+  */
+ RESULT_MUST_BE_USED
+ int git_configset_get(struct config_set *cs, const char *key);
  /*
   * Finds the highest-priority value for the configuration variable `key`
   * and config set `cs`, stores the pointer to it in `value` and returns 0.
@@@ -485,10 -522,22 +513,22 @@@ int git_configset_get_pathname(struct c
  /* Functions for reading a repository's config */
  struct repository;
  void repo_config(struct repository *repo, config_fn_t fn, void *data);
+ /**
+  * Run only the discover part of the repo_config_get_*() functions
+  * below, in addition to 1 if not found, returns negative values on
+  * error (e.g. if the key itself is invalid).
+  */
+ RESULT_MUST_BE_USED
+ int repo_config_get(struct repository *repo, const char *key);
  int repo_config_get_value(struct repository *repo,
                          const char *key, const char **value);
- const struct string_list *repo_config_get_value_multi(struct repository *repo,
-                                                     const char *key);
+ RESULT_MUST_BE_USED
+ int repo_config_get_value_multi(struct repository *repo, const char *key,
+                               const struct string_list **dest);
+ RESULT_MUST_BE_USED
+ int repo_config_get_string_multi(struct repository *repo, const char *key,
+                                const struct string_list **dest);
  int repo_config_get_string(struct repository *repo,
                           const char *key, char **dest);
  int repo_config_get_string_tmp(struct repository *repo,
@@@ -521,8 -570,15 +561,15 @@@ void git_protected_config(config_fn_t f
   * manner, the config API provides two functions `git_config_get_value`
   * and `git_config_get_value_multi`. They both read values from an internal
   * cache generated previously from reading the config files.
+  *
+  * For those git_config_get*() functions that aren't documented,
+  * consult the corresponding repo_config_get*() function's
+  * documentation.
   */
  
+ RESULT_MUST_BE_USED
+ int git_config_get(const char *key);
  /**
   * Finds the highest-priority value for the configuration variable `key`,
   * stores the pointer to it in `value` and returns 0. When the
@@@ -535,10 -591,17 +582,17 @@@ int git_config_get_value(const char *ke
  /**
   * Finds and returns the value list, sorted in order of increasing priority
   * for the configuration variable `key`. When the configuration variable
-  * `key` is not found, returns NULL. The caller should not free or modify
-  * the returned pointer, as it is owned by the cache.
-  */
- const struct string_list *git_config_get_value_multi(const char *key);
+  * `key` is not found, returns 1 without touching `value`.
+  *
+  * The caller should not free or modify the returned pointer, as it is
+  * owned by the cache.
+  */
+ RESULT_MUST_BE_USED
+ int git_config_get_value_multi(const char *key,
+                              const struct string_list **dest);
+ RESULT_MUST_BE_USED
+ int git_config_get_string_multi(const char *key,
+                               const struct string_list **dest);
  
  /**
   * Resets and invalidates the config cache.
diff --combined pack-bitmap.c
index ca7c81b5c9f2770daf79d4fe15b889ac4e8b5251,dd05ab03ca003558f18b49b33d0b973bf157aea6..f8ab6be411d4dad3e37cb96acbff476ba7579441
@@@ -1,7 -1,5 +1,7 @@@
 -#include "cache.h"
 +#include "git-compat-util.h"
 +#include "alloc.h"
  #include "commit.h"
 +#include "hex.h"
  #include "strbuf.h"
  #include "tag.h"
  #include "diff.h"
@@@ -356,8 -354,8 +356,8 @@@ static int open_midx_bitmap_1(struct bi
        if (bitmap_git->pack || bitmap_git->midx) {
                struct strbuf buf = STRBUF_INIT;
                get_midx_filename(&buf, midx->object_dir);
 -              /* ignore extra bitmap file; we can only handle one */
 -              warning(_("ignoring extra bitmap file: '%s'"), buf.buf);
 +              trace2_data_string("bitmap", the_repository,
 +                                 "ignoring extra midx bitmap file", buf.buf);
                close(fd);
                strbuf_release(&buf);
                return -1;
@@@ -413,6 -411,9 +413,6 @@@ static int open_pack_bitmap_1(struct bi
        struct stat st;
        char *bitmap_name;
  
 -      if (open_pack_index(packfile))
 -              return -1;
 -
        bitmap_name = pack_bitmap_filename(packfile);
        fd = git_open(bitmap_name);
  
        }
  
        if (bitmap_git->pack || bitmap_git->midx) {
 -              /* ignore extra bitmap file; we can only handle one */
 -              warning(_("ignoring extra bitmap file: '%s'"), packfile->pack_name);
 +              trace2_data_string("bitmap", the_repository,
 +                                 "ignoring extra bitmap file", packfile->pack_name);
                close(fd);
                return -1;
        }
                return -1;
        }
  
 +      trace2_data_string("bitmap", the_repository, "opened bitmap file",
 +                         packfile->pack_name);
        return 0;
  }
  
@@@ -526,16 -525,11 +526,16 @@@ static int open_pack_bitmap(struct repo
        struct packed_git *p;
        int ret = -1;
  
 -      assert(!bitmap_git->map);
 -
        for (p = get_all_packs(r); p; p = p->next) {
 -              if (open_pack_bitmap_1(bitmap_git, p) == 0)
 +              if (open_pack_bitmap_1(bitmap_git, p) == 0) {
                        ret = 0;
 +                      /*
 +                       * The only reason to keep looking is to report
 +                       * duplicates.
 +                       */
 +                      if (!trace2_is_enabled())
 +                              break;
 +              }
        }
  
        return ret;
@@@ -559,20 -553,11 +559,20 @@@ static int open_midx_bitmap(struct repo
  static int open_bitmap(struct repository *r,
                       struct bitmap_index *bitmap_git)
  {
 +      int found;
 +
        assert(!bitmap_git->map);
  
 -      if (!open_midx_bitmap(r, bitmap_git))
 -              return 0;
 -      return open_pack_bitmap(r, bitmap_git);
 +      found = !open_midx_bitmap(r, bitmap_git);
 +
 +      /*
 +       * these will all be skipped if we opened a midx bitmap; but run it
 +       * anyway if tracing is enabled to report the duplicates
 +       */
 +      if (!found || trace2_is_enabled())
 +              found |= !open_pack_bitmap(r, bitmap_git);
 +
 +      return found ? 0 : -1;
  }
  
  struct bitmap_index *prepare_bitmap_git(struct repository *r)
@@@ -953,8 -938,7 +953,8 @@@ static void show_object(struct object *
        bitmap_set(data->base, bitmap_pos);
  }
  
 -static void show_commit(struct commit *commit, void *data)
 +static void show_commit(struct commit *commit UNUSED,
 +                      void *data UNUSED)
  {
  }
  
@@@ -1943,8 -1927,7 +1943,8 @@@ static void test_bitmap_type(struct bit
                    type_name(bitmap_type));
  }
  
 -static void test_show_object(struct object *object, const char *name,
 +static void test_show_object(struct object *object,
 +                           const char *name UNUSED,
                             void *data)
  {
        struct bitmap_test_data *tdata = data;
@@@ -2318,7 -2301,11 +2318,11 @@@ int bitmap_is_midx(struct bitmap_index 
  
  const struct string_list *bitmap_preferred_tips(struct repository *r)
  {
-       return repo_config_get_value_multi(r, "pack.preferbitmaptips");
+       const struct string_list *dest;
+       if (!repo_config_get_string_multi(r, "pack.preferbitmaptips", &dest))
+               return dest;
+       return NULL;
  }
  
  int bitmap_is_preferred_refname(struct repository *r, const char *refname)
diff --combined submodule.c
index 2a057c35b749a564e793a0f80c553744f5189746,0f6cf864ed98f73f477e9436892c2cd0a15b9cdf..c9579f9a3f8bf6016bac14b47c5015b2970b6a4e
@@@ -1,5 -1,5 +1,5 @@@
 -
 -#include "cache.h"
 +#include "git-compat-util.h"
 +#include "alloc.h"
  #include "repository.h"
  #include "config.h"
  #include "submodule-config.h"
@@@ -7,7 -7,6 +7,7 @@@
  #include "dir.h"
  #include "diff.h"
  #include "commit.h"
 +#include "hex.h"
  #include "revision.h"
  #include "run-command.h"
  #include "diffcore.h"
@@@ -275,8 -274,7 +275,7 @@@ int is_tree_submodule_active(struct rep
        free(key);
  
        /* submodule.active is set */
-       sl = repo_config_get_value_multi(repo, "submodule.active");
-       if (sl) {
+       if (!repo_config_get_string_multi(repo, "submodule.active", &sl)) {
                struct pathspec ps;
                struct strvec args = STRVEC_INIT;
                const struct string_list_item *item;
@@@ -833,7 -831,7 +832,7 @@@ static void changed_submodule_data_clea
  }
  
  static void collect_changed_submodules_cb(struct diff_queue_struct *q,
 -                                        struct diff_options *options,
 +                                        struct diff_options *options UNUSED,
                                          void *data)
  {
        struct collect_changed_submodules_cb_data *me = data;
@@@ -1740,7 -1738,7 +1739,7 @@@ static int get_next_submodule(struct ch
        return 0;
  }
  
 -static int fetch_start_failure(struct strbuf *err,
 +static int fetch_start_failure(struct strbuf *err UNUSED,
                               void *cb, void *task_cb)
  {
        struct submodule_parallel_fetch *spf = cb;
@@@ -1761,7 -1759,7 +1760,7 @@@ static int commit_missing_in_sub(const 
        return type != OBJ_COMMIT;
  }
  
 -static int fetch_finish(int retvalue, struct strbuf *err,
 +static int fetch_finish(int retvalue, struct strbuf *err UNUSED,
                        void *cb, void *task_cb)
  {
        struct submodule_parallel_fetch *spf = cb;
@@@ -2055,6 -2053,14 +2054,6 @@@ void submodule_unset_core_worktree(cons
        strbuf_release(&config_path);
  }
  
 -static const char *get_super_prefix_or_empty(void)
 -{
 -      const char *s = get_super_prefix();
 -      if (!s)
 -              s = "";
 -      return s;
 -}
 -
  static int submodule_has_dirty_index(const struct submodule *sub)
  {
        struct child_process cp = CHILD_PROCESS_INIT;
        return finish_command(&cp);
  }
  
 -static void submodule_reset_index(const char *path)
 +static void submodule_reset_index(const char *path, const char *super_prefix)
  {
        struct child_process cp = CHILD_PROCESS_INIT;
        prepare_submodule_repo_env(&cp.env);
        cp.no_stdin = 1;
        cp.dir = path;
  
 -      strvec_pushf(&cp.args, "--super-prefix=%s%s/",
 -                   get_super_prefix_or_empty(), path);
        /* TODO: determine if this might overwright untracked files */
        strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
 +      strvec_pushf(&cp.args, "--super-prefix=%s%s/",
 +                   (super_prefix ? super_prefix : ""), path);
  
        strvec_push(&cp.args, empty_tree_oid_hex());
  
   * For edge cases (a submodule coming into existence or removing a submodule)
   * pass NULL for old or new respectively.
   */
 -int submodule_move_head(const char *path,
 -                       const char *old_head,
 -                       const char *new_head,
 -                       unsigned flags)
 +int submodule_move_head(const char *path, const char *super_prefix,
 +                      const char *old_head, const char *new_head,
 +                      unsigned flags)
  {
        int ret = 0;
        struct child_process cp = CHILD_PROCESS_INIT;
        if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
                if (old_head) {
                        if (!submodule_uses_gitfile(path))
 -                              absorb_git_dir_into_superproject(path);
 +                              absorb_git_dir_into_superproject(path,
 +                                                               super_prefix);
                } else {
                        struct strbuf gitdir = STRBUF_INIT;
                        submodule_name_to_gitdir(&gitdir, the_repository,
                        strbuf_release(&gitdir);
  
                        /* make sure the index is clean as well */
 -                      submodule_reset_index(path);
 +                      submodule_reset_index(path, super_prefix);
                }
  
                if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
        cp.no_stdin = 1;
        cp.dir = path;
  
 -      strvec_pushf(&cp.args, "--super-prefix=%s%s/",
 -                   get_super_prefix_or_empty(), path);
        strvec_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
 +      strvec_pushf(&cp.args, "--super-prefix=%s%s/",
 +                   (super_prefix ? super_prefix : ""), path);
  
        if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
                strvec_push(&cp.args, "-n");
@@@ -2267,8 -2273,7 +2266,8 @@@ int validate_submodule_git_dir(char *gi
   * Embeds a single submodules git directory into the superprojects git dir,
   * non recursively.
   */
 -static void relocate_single_git_dir_into_superproject(const char *path)
 +static void relocate_single_git_dir_into_superproject(const char *path,
 +                                                    const char *super_prefix)
  {
        char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
        struct strbuf new_gitdir = STRBUF_INIT;
        real_new_git_dir = real_pathdup(new_gitdir.buf, 1);
  
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
 -              get_super_prefix_or_empty(), path,
 +              super_prefix ? super_prefix : "", path,
                real_old_git_dir, real_new_git_dir);
  
        relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
        strbuf_release(&new_gitdir);
  }
  
 -static void absorb_git_dir_into_superproject_recurse(const char *path)
 +static void absorb_git_dir_into_superproject_recurse(const char *path,
 +                                                   const char *super_prefix)
  {
  
        struct child_process cp = CHILD_PROCESS_INIT;
        cp.dir = path;
        cp.git_cmd = 1;
        cp.no_stdin = 1;
 -      strvec_pushf(&cp.args, "--super-prefix=%s%s/",
 -                   get_super_prefix_or_empty(), path);
        strvec_pushl(&cp.args, "submodule--helper",
                     "absorbgitdirs", NULL);
 +      strvec_pushf(&cp.args, "--super-prefix=%s%s/", super_prefix ?
 +                   super_prefix : "", path);
 +
        prepare_submodule_repo_env(&cp.env);
        if (run_command(&cp))
                die(_("could not recurse into submodule '%s'"), path);
   * having its git directory within the working tree to the git dir nested
   * in its superprojects git dir under modules/.
   */
 -void absorb_git_dir_into_superproject(const char *path)
 +void absorb_git_dir_into_superproject(const char *path,
 +                                    const char *super_prefix)
  {
        int err_code;
        const char *sub_git_dir;
                char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
  
                if (!starts_with(real_sub_git_dir, real_common_git_dir))
 -                      relocate_single_git_dir_into_superproject(path);
 +                      relocate_single_git_dir_into_superproject(path, super_prefix);
  
                free(real_sub_git_dir);
                free(real_common_git_dir);
        }
        strbuf_release(&gitdir);
  
 -      absorb_git_dir_into_superproject_recurse(path);
 +      absorb_git_dir_into_superproject_recurse(path, super_prefix);
  }
  
  int get_superproject_working_tree(struct strbuf *buf)
diff --combined t/t0068-for-each-repo.sh
index 3648d439a87eabb37b581cf5e731f9048085703b,48187a40d642de6f05f537b0b174263e04a8f3a2..4b90b74d5d515ec80b18b67e2b6f32780f860f22
@@@ -2,7 -2,6 +2,7 @@@
  
  test_description='git for-each-repo builtin'
  
 +TEST_PASSES_SANITIZE_LEAK=true
  . ./test-lib.sh
  
  test_expect_success 'run based on configured value' '
@@@ -40,4 -39,23 +40,23 @@@ test_expect_success 'do nothing on empt
        git for-each-repo --config=bogus.config -- help --no-such-option
  '
  
+ test_expect_success 'error on bad config keys' '
+       test_expect_code 129 git for-each-repo --config=a &&
+       test_expect_code 129 git for-each-repo --config=a.b. &&
+       test_expect_code 129 git for-each-repo --config="'\''.b"
+ '
+ test_expect_success 'error on NULL value for config keys' '
+       cat >>.git/config <<-\EOF &&
+       [empty]
+               key
+       EOF
+       cat >expect <<-\EOF &&
+       error: missing value for '\''empty.key'\''
+       EOF
+       test_expect_code 129 git for-each-repo --config=empty.key 2>actual.raw &&
+       grep ^error actual.raw >actual &&
+       test_cmp expect actual
+ '
  test_done
diff --combined t/t5304-prune.sh
index d65a5f94b4b557ef9cce4434594729086cb51fad,c8fa962b3970ad03b9ed2df358593dc8afd12f62..5500dd084264d170d00629171502484cbb349a0c
@@@ -29,14 -29,6 +29,14 @@@ test_expect_success setup 
        git gc
  '
  
 +test_expect_success 'bare repo prune is quiet without $GIT_DIR/objects/pack' '
 +      git clone -q --shared --template= --bare . bare.git &&
 +      rmdir bare.git/objects/pack &&
 +      git --git-dir=bare.git prune --no-progress 2>prune.err &&
 +      test_must_be_empty prune.err &&
 +      rm -r bare.git prune.err
 +'
 +
  test_expect_success 'prune stale packs' '
        orig_pack=$(echo .git/objects/pack/*.pack) &&
        >.git/objects/tmp_1.pack &&
@@@ -72,8 -64,16 +72,16 @@@ test_expect_success 'gc: implicit prun
  '
  
  test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
-       git config gc.pruneExpire invalid &&
-       test_must_fail git gc
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       >repo/.git/config &&
+       git -C repo config gc.pruneExpire invalid &&
+       cat >expect <<-\EOF &&
+       error: Invalid gc.pruneexpire: '\''invalid'\''
+       fatal: bad config variable '\''gc.pruneexpire'\'' in file '\''.git/config'\'' at line 2
+       EOF
+       test_must_fail git -C repo gc 2>actual &&
+       test_cmp expect actual
  '
  
  test_expect_success 'gc: start with ok gc.pruneExpire' '
diff --combined t/t5310-pack-bitmaps.sh
index 7d8dee41b0ded364d9b364ab46296c20b2c8eee1,894c750080c589c09370b41102529c505bb603a2..526a5a506eb5b6f1cc46a2ade9118447505c3aa3
@@@ -404,6 -404,26 +404,26 @@@ test_bitmap_cases () 
                )
        '
  
+       test_expect_success 'pack.preferBitmapTips' '
+               git init repo &&
+               test_when_finished "rm -rf repo" &&
+               (
+                       cd repo &&
+                       git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+                       test_commit_bulk --message="%s" 103 &&
+                       cat >>.git/config <<-\EOF &&
+                       [pack]
+                               preferBitmapTips
+                       EOF
+                       cat >expect <<-\EOF &&
+                       error: missing value for '\''pack.preferbitmaptips'\''
+                       EOF
+                       git repack -adb 2>actual &&
+                       test_cmp expect actual
+               )
+       '
        test_expect_success 'complains about multiple pack bitmaps' '
                rm -fr repo &&
                git init repo &&
                        test_line_count = 2 packs &&
                        test_line_count = 2 bitmaps &&
  
 -                      git rev-list --use-bitmap-index HEAD 2>err &&
 -                      grep "ignoring extra bitmap file" err
 +                      GIT_TRACE2_EVENT=$(pwd)/trace2.txt git rev-list --use-bitmap-index HEAD &&
 +                      grep "opened bitmap" trace2.txt &&
 +                      grep "ignoring extra bitmap" trace2.txt
                )
        '
  }
diff --combined t/t7004-tag.sh
index 04a4b44183d70e4c77d4fe7fae9243fd71b34598,f4a31ada79a6ec3b29cff060edbab47296ab2baa..32b312fa80e224e893d58fb36854466dc34e2293
@@@ -792,34 -792,6 +792,34 @@@ test_expect_success 'annotations for bl
        test_cmp expect actual
  '
  
 +# Run this before doing any signing, so the test has the same results
 +# regardless of the GPG prereq.
 +test_expect_success 'git tag --format with ahead-behind' '
 +      test_when_finished git reset --hard tag-one-line &&
 +      git commit --allow-empty -m "left" &&
 +      git tag -a -m left tag-left &&
 +      git reset --hard HEAD~1 &&
 +      git commit --allow-empty -m "right" &&
 +      git tag -a -m left tag-right &&
 +
 +      # Use " !" at the end to demonstrate whitespace
 +      # around empty ahead-behind token for tag-blob.
 +      cat >expect <<-EOF &&
 +      refs/tags/tag-blob  !
 +      refs/tags/tag-left 1 1 !
 +      refs/tags/tag-lines 0 1 !
 +      refs/tags/tag-one-line 0 1 !
 +      refs/tags/tag-right 0 0 !
 +      refs/tags/tag-zero-lines 0 1 !
 +      EOF
 +      git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
 +      grep "refs/tags/tag" actual >actual.focus &&
 +      test_cmp expect actual.focus &&
 +
 +      # Error reported for tags that point to non-commits.
 +      grep "error: object [0-9a-f]* is a blob, not a commit" err
 +'
 +
  # trying to verify annotated non-signed tags:
  
  test_expect_success GPG \
@@@ -1871,6 -1843,23 +1871,23 @@@ test_expect_success 'invalid sort param
        test_must_fail git tag -l "foo*"
  '
  
+ test_expect_success 'version sort handles empty value for versionsort.{prereleaseSuffix,suffix}' '
+       cp .git/config .git/config.orig &&
+       test_when_finished mv .git/config.orig .git/config &&
+       cat >>.git/config <<-\EOF &&
+       [versionsort]
+               prereleaseSuffix
+               suffix
+       EOF
+       cat >expect <<-\EOF &&
+       error: missing value for '\''versionsort.suffix'\''
+       error: missing value for '\''versionsort.prereleasesuffix'\''
+       EOF
+       git tag -l --sort=version:refname 2>actual &&
+       test_cmp expect actual
+ '
  test_expect_success 'version sort with prerelease reordering' '
        test_config versionsort.prereleaseSuffix -rc &&
        git tag foo1.6-rc1 &&