Merge branch 'jk/unused-parameter-fixes'
authorJunio C Hamano <gitster@pobox.com>
Sun, 18 Nov 2018 09:23:53 +0000 (18:23 +0900)
committerJunio C Hamano <gitster@pobox.com>
Sun, 18 Nov 2018 09:23:53 +0000 (18:23 +0900)
Various functions have been audited for "-Wunused-parameter" warnings
and bugs in them got fixed.

* jk/unused-parameter-fixes:
midx: double-check large object write loop
assert NOARG/NONEG behavior of parse-options callbacks
parse-options: drop OPT_DATE()
apply: return -1 from option callback instead of calling exit(1)
cat-file: report an error on multiple --batch options
tag: mark "--message" option with NONEG
show-branch: mark --reflog option as NONEG
format-patch: mark "--no-numbered" option with NONEG
status: mark --find-renames option with NONEG
cat-file: mark batch options with NONEG
pack-objects: mark index-version option as NONEG
ls-files: mark exclude options as NONEG
am: handle --no-patch-format option
apply: mark include/exclude options as NONEG

1  2 
builtin/am.c
builtin/cat-file.c
builtin/commit.c
builtin/fetch.c
builtin/grep.c
builtin/pack-objects.c
builtin/update-index.c
midx.c
diff --combined builtin/am.c
index dc576a33728f5c2264acf66fcbe3243284144781,dcb880b699a5c17311f0dbc083b9a3eda3a6b0fd..8f27f3375b1e92f2ef026e0a1530ca8b6b3235bf
@@@ -260,6 -260,32 +260,6 @@@ static int read_state_file(struct strbu
        die_errno(_("could not read '%s'"), am_path(state, file));
  }
  
 -/**
 - * Take a series of KEY='VALUE' lines where VALUE part is
 - * sq-quoted, and append <KEY, VALUE> at the end of the string list
 - */
 -static int parse_key_value_squoted(char *buf, struct string_list *list)
 -{
 -      while (*buf) {
 -              struct string_list_item *item;
 -              char *np;
 -              char *cp = strchr(buf, '=');
 -              if (!cp)
 -                      return -1;
 -              np = strchrnul(cp, '\n');
 -              *cp++ = '\0';
 -              item = string_list_append(list, buf);
 -
 -              buf = np + (*np == '\n');
 -              *np = '\0';
 -              cp = sq_dequote(cp);
 -              if (!cp)
 -                      return -1;
 -              item->util = xstrdup(cp);
 -      }
 -      return 0;
 -}
 -
  /**
   * Reads and parses the state directory's "author-script" file, and sets
   * state->author_name, state->author_email and state->author_date accordingly.
   * script, and thus if the file differs from what this function expects, it is
   * better to bail out than to do something that the user does not expect.
   */
 -static int read_author_script(struct am_state *state)
 +static int read_am_author_script(struct am_state *state)
  {
        const char *filename = am_path(state, "author-script");
 -      struct strbuf buf = STRBUF_INIT;
 -      struct string_list kv = STRING_LIST_INIT_DUP;
 -      int retval = -1; /* assume failure */
 -      int fd;
  
        assert(!state->author_name);
        assert(!state->author_email);
        assert(!state->author_date);
  
 -      fd = open(filename, O_RDONLY);
 -      if (fd < 0) {
 -              if (errno == ENOENT)
 -                      return 0;
 -              die_errno(_("could not open '%s' for reading"), filename);
 -      }
 -      strbuf_read(&buf, fd, 0);
 -      close(fd);
 -      if (parse_key_value_squoted(buf.buf, &kv))
 -              goto finish;
 -
 -      if (kv.nr != 3 ||
 -          strcmp(kv.items[0].string, "GIT_AUTHOR_NAME") ||
 -          strcmp(kv.items[1].string, "GIT_AUTHOR_EMAIL") ||
 -          strcmp(kv.items[2].string, "GIT_AUTHOR_DATE"))
 -              goto finish;
 -      state->author_name = kv.items[0].util;
 -      state->author_email = kv.items[1].util;
 -      state->author_date = kv.items[2].util;
 -      retval = 0;
 -finish:
 -      string_list_clear(&kv, !!retval);
 -      strbuf_release(&buf);
 -      return retval;
 +      return read_author_script(filename, &state->author_name,
 +                                &state->author_email, &state->author_date, 1);
  }
  
  /**
@@@ -359,7 -411,7 +359,7 @@@ static void am_load(struct am_state *st
                BUG("state file 'last' does not exist");
        state->last = strtol(sb.buf, NULL, 10);
  
 -      if (read_author_script(state) < 0)
 +      if (read_am_author_script(state) < 0)
                die(_("could not parse author script"));
  
        read_commit_msg(state);
@@@ -2113,7 -2165,9 +2113,9 @@@ static int parse_opt_patchformat(const 
  {
        int *opt_value = opt->value;
  
-       if (!strcmp(arg, "mbox"))
+       if (unset)
+               *opt_value = PATCH_FORMAT_UNKNOWN;
+       else if (!strcmp(arg, "mbox"))
                *opt_value = PATCH_FORMAT_MBOX;
        else if (!strcmp(arg, "stgit"))
                *opt_value = PATCH_FORMAT_STGIT;
diff --combined builtin/cat-file.c
index 0d403eb77d37d3b409d7d8ef030c12b91fe63da6,1cdd357d523eaaba6c86a78ac21cc068e87d371b..657fe926e2ae884012a8987e7a5753f4277384d7
@@@ -50,13 -50,6 +50,13 @@@ static int filter_object(const char *pa
        return 0;
  }
  
 +static int stream_blob(const struct object_id *oid)
 +{
 +      if (stream_blob_to_fd(1, oid, NULL, 0))
 +              die("unable to stream %s to stdout", oid_to_hex(oid));
 +      return 0;
 +}
 +
  static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                        int unknown_type)
  {
                }
  
                if (type == OBJ_BLOB)
 -                      return stream_blob_to_fd(1, &oid, NULL, 0);
 +                      return stream_blob(&oid);
                buf = read_object_file(&oid, &type, &size);
                if (!buf)
                        die("Cannot read object %s", obj_name);
                                oidcpy(&blob_oid, &oid);
  
                        if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB)
 -                              return stream_blob_to_fd(1, &blob_oid, NULL, 0);
 +                              return stream_blob(&blob_oid);
                        /*
                         * we attempted to dereference a tag to a blob
                         * and failed; there may be new dereference
@@@ -326,9 -319,8 +326,9 @@@ static void print_object_or_die(struct 
                                BUG("invalid cmdmode: %c", opt->cmdmode);
                        batch_write(opt, contents, size);
                        free(contents);
 -              } else if (stream_blob_to_fd(1, oid, NULL, 0) < 0)
 -                      die("unable to stream %s to stdout", oid_to_hex(oid));
 +              } else {
 +                      stream_blob(oid);
 +              }
        }
        else {
                enum object_type type;
@@@ -603,8 -595,10 +603,10 @@@ static int batch_option_callback(const 
  {
        struct batch_options *bo = opt->value;
  
+       BUG_ON_OPT_NEG(unset);
        if (bo->enabled) {
-               return 1;
+               return error(_("only one batch option may be specified"));
        }
  
        bo->enabled = 1;
@@@ -639,10 -633,12 +641,12 @@@ int cmd_cat_file(int argc, const char *
                OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
                { OPTION_CALLBACK, 0, "batch", &batch, "format",
                        N_("show info and content of objects fed from the standard input"),
-                       PARSE_OPT_OPTARG, batch_option_callback },
+                       PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+                       batch_option_callback },
                { OPTION_CALLBACK, 0, "batch-check", &batch, "format",
                        N_("show info about objects fed from the standard input"),
-                       PARSE_OPT_OPTARG, batch_option_callback },
+                       PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+                       batch_option_callback },
                OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
                         N_("follow in-tree symlinks (used with --batch or --batch-check)")),
                OPT_BOOL(0, "batch-all-objects", &batch.all_objects,
diff --combined builtin/commit.c
index 96d336ec3d8044918fb260d6cf8c03296cf32ed1,500f793fa6947f6309ce4ea82d15410e35b2ff20..c021b119bb9df106ab7a860e032ea5dd35529401
@@@ -161,6 -161,9 +161,9 @@@ static int opt_parse_m(const struct opt
  static int opt_parse_rename_score(const struct option *opt, const char *arg, int unset)
  {
        const char **value = opt->value;
+       BUG_ON_OPT_NEG(unset);
        if (arg != NULL && *arg == '=')
                arg = arg + 1;
  
@@@ -1335,7 -1338,7 +1338,7 @@@ int cmd_status(int argc, const char **a
                OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")),
                { OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg,
                  N_("n"), N_("detect renames, optionally set similarity index"),
-                 PARSE_OPT_OPTARG, opt_parse_rename_score },
+                 PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score },
                OPT_END(),
        };
  
        if (status_format != STATUS_FORMAT_PORCELAIN &&
            status_format != STATUS_FORMAT_PORCELAIN_V2)
                progress_flag = REFRESH_PROGRESS;
 -      read_index_preload(&the_index, &s.pathspec, progress_flag);
 +      read_index(&the_index);
        refresh_index(&the_index,
                      REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
                      &s.pathspec, NULL, NULL);
diff --combined builtin/fetch.c
index 6ec7c07d1dc111e6370dad498bf7453b5cd2c92e,354ed8bdef3a6511ddb580d97b75df60b77ac6ca..e0140327aab23654c69e7388c23a07b98b8ff913
@@@ -98,6 -98,8 +98,8 @@@ static int git_fetch_config(const char 
  
  static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
  {
+       BUG_ON_OPT_NEG(unset);
        /*
         * "git fetch --refmap='' origin foo"
         * can be used to tell the command not to store anywhere
@@@ -223,6 -225,18 +225,6 @@@ static void add_merge_config(struct re
        }
  }
  
 -static int add_existing(const char *refname, const struct object_id *oid,
 -                      int flag, void *cbdata)
 -{
 -      struct string_list *list = (struct string_list *)cbdata;
 -      struct string_list_item *item = string_list_insert(list, refname);
 -      struct object_id *old_oid = xmalloc(sizeof(*old_oid));
 -
 -      oidcpy(old_oid, oid);
 -      item->util = old_oid;
 -      return 0;
 -}
 -
  static int will_fetch(struct ref **head, const unsigned char *sha1)
  {
        struct ref *rm = *head;
        return 0;
  }
  
 +struct refname_hash_entry {
 +      struct hashmap_entry ent; /* must be the first member */
 +      struct object_id oid;
 +      char refname[FLEX_ARRAY];
 +};
 +
 +static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data,
 +                                const void *e1_,
 +                                const void *e2_,
 +                                const void *keydata)
 +{
 +      const struct refname_hash_entry *e1 = e1_;
 +      const struct refname_hash_entry *e2 = e2_;
 +
 +      return strcmp(e1->refname, keydata ? keydata : e2->refname);
 +}
 +
 +static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
 +                                                 const char *refname,
 +                                                 const struct object_id *oid)
 +{
 +      struct refname_hash_entry *ent;
 +      size_t len = strlen(refname);
 +
 +      FLEX_ALLOC_MEM(ent, refname, refname, len);
 +      hashmap_entry_init(ent, strhash(refname));
 +      oidcpy(&ent->oid, oid);
 +      hashmap_add(map, ent);
 +      return ent;
 +}
 +
 +static int add_one_refname(const char *refname,
 +                         const struct object_id *oid,
 +                         int flag, void *cbdata)
 +{
 +      struct hashmap *refname_map = cbdata;
 +
 +      (void) refname_hash_add(refname_map, refname, oid);
 +      return 0;
 +}
 +
 +static void refname_hash_init(struct hashmap *map)
 +{
 +      hashmap_init(map, refname_hash_entry_cmp, NULL, 0);
 +}
 +
 +static int refname_hash_exists(struct hashmap *map, const char *refname)
 +{
 +      return !!hashmap_get_from_hash(map, strhash(refname), refname);
 +}
 +
  static void find_non_local_tags(const struct ref *refs,
                                struct ref **head,
                                struct ref ***tail)
  {
 -      struct string_list existing_refs = STRING_LIST_INIT_DUP;
 -      struct string_list remote_refs = STRING_LIST_INIT_NODUP;
 +      struct hashmap existing_refs;
 +      struct hashmap remote_refs;
 +      struct string_list remote_refs_list = STRING_LIST_INIT_NODUP;
 +      struct string_list_item *remote_ref_item;
        const struct ref *ref;
 -      struct string_list_item *item = NULL;
 +      struct refname_hash_entry *item = NULL;
 +
 +      refname_hash_init(&existing_refs);
 +      refname_hash_init(&remote_refs);
  
 -      for_each_ref(add_existing, &existing_refs);
 +      for_each_ref(add_one_refname, &existing_refs);
        for (ref = refs; ref; ref = ref->next) {
                if (!starts_with(ref->name, "refs/tags/"))
                        continue;
                            !has_object_file_with_flags(&ref->old_oid,
                                                        OBJECT_INFO_QUICK) &&
                            !will_fetch(head, ref->old_oid.hash) &&
 -                          !has_sha1_file_with_flags(item->util,
 +                          !has_sha1_file_with_flags(item->oid.hash,
                                                      OBJECT_INFO_QUICK) &&
 -                          !will_fetch(head, item->util))
 -                              item->util = NULL;
 +                          !will_fetch(head, item->oid.hash))
 +                              oidclr(&item->oid);
                        item = NULL;
                        continue;
                }
                 * fetch.
                 */
                if (item &&
 -                  !has_sha1_file_with_flags(item->util, OBJECT_INFO_QUICK) &&
 -                  !will_fetch(head, item->util))
 -                      item->util = NULL;
 +                  !has_sha1_file_with_flags(item->oid.hash, OBJECT_INFO_QUICK) &&
 +                  !will_fetch(head, item->oid.hash))
 +                      oidclr(&item->oid);
  
                item = NULL;
  
                /* skip duplicates and refs that we already have */
 -              if (string_list_has_string(&remote_refs, ref->name) ||
 -                  string_list_has_string(&existing_refs, ref->name))
 +              if (refname_hash_exists(&remote_refs, ref->name) ||
 +                  refname_hash_exists(&existing_refs, ref->name))
                        continue;
  
 -              item = string_list_insert(&remote_refs, ref->name);
 -              item->util = (void *)&ref->old_oid;
 +              item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
 +              string_list_insert(&remote_refs_list, ref->name);
        }
 -      string_list_clear(&existing_refs, 1);
 +      hashmap_free(&existing_refs, 1);
  
        /*
         * We may have a final lightweight tag that needs to be
         * checked to see if it needs fetching.
         */
        if (item &&
 -          !has_sha1_file_with_flags(item->util, OBJECT_INFO_QUICK) &&
 -          !will_fetch(head, item->util))
 -              item->util = NULL;
 +          !has_sha1_file_with_flags(item->oid.hash, OBJECT_INFO_QUICK) &&
 +          !will_fetch(head, item->oid.hash))
 +              oidclr(&item->oid);
  
        /*
 -       * For all the tags in the remote_refs string list,
 +       * For all the tags in the remote_refs_list,
         * add them to the list of refs to be fetched
         */
 -      for_each_string_list_item(item, &remote_refs) {
 +      for_each_string_list_item(remote_ref_item, &remote_refs_list) {
 +              const char *refname = remote_ref_item->string;
 +
 +              item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
 +              if (!item)
 +                      BUG("unseen remote ref?");
 +
                /* Unless we have already decided to ignore this item... */
 -              if (item->util)
 -              {
 -                      struct ref *rm = alloc_ref(item->string);
 -                      rm->peer_ref = alloc_ref(item->string);
 -                      oidcpy(&rm->old_oid, item->util);
 +              if (!is_null_oid(&item->oid)) {
 +                      struct ref *rm = alloc_ref(item->refname);
 +                      rm->peer_ref = alloc_ref(item->refname);
 +                      oidcpy(&rm->old_oid, &item->oid);
                        **tail = rm;
                        *tail = &rm->next;
                }
        }
 -
 -      string_list_clear(&remote_refs, 0);
 +      hashmap_free(&remote_refs, 1);
 +      string_list_clear(&remote_refs_list, 0);
  }
  
  static struct ref *get_ref_map(struct remote *remote,
        /* opportunistically-updated references: */
        struct ref *orefs = NULL, **oref_tail = &orefs;
  
 -      struct string_list existing_refs = STRING_LIST_INIT_DUP;
 +      struct hashmap existing_refs;
  
        if (rs->nr) {
                struct refspec *fetch_refspec;
  
        ref_map = ref_remove_duplicates(ref_map);
  
 -      for_each_ref(add_existing, &existing_refs);
 +      refname_hash_init(&existing_refs);
 +      for_each_ref(add_one_refname, &existing_refs);
 +
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref) {
 -                      struct string_list_item *peer_item =
 -                              string_list_lookup(&existing_refs,
 -                                                 rm->peer_ref->name);
 +                      const char *refname = rm->peer_ref->name;
 +                      struct refname_hash_entry *peer_item;
 +
 +                      peer_item = hashmap_get_from_hash(&existing_refs,
 +                                                        strhash(refname),
 +                                                        refname);
                        if (peer_item) {
 -                              struct object_id *old_oid = peer_item->util;
 +                              struct object_id *old_oid = &peer_item->oid;
                                oidcpy(&rm->peer_ref->old_oid, old_oid);
                        }
                }
        }
 -      string_list_clear(&existing_refs, 1);
 +      hashmap_free(&existing_refs, 1);
  
        return ref_map;
  }
diff --combined builtin/grep.c
index cca87cf8709aed25d0fff230a436ae7f04c1811b,33c8b61595652889f15bef21bb4f7d1af6429170..71df52a333cbc756b057e93bf3d2affca8157860
@@@ -34,6 -34,7 +34,6 @@@ static int recurse_submodules
  #define GREP_NUM_THREADS_DEFAULT 8
  static int num_threads;
  
 -#ifndef NO_PTHREADS
  static pthread_t *threads;
  
  /* We use one producer thread and THREADS consumer
@@@ -69,11 -70,13 +69,11 @@@ static pthread_mutex_t grep_mutex
  
  static inline void grep_lock(void)
  {
 -      assert(num_threads);
        pthread_mutex_lock(&grep_mutex);
  }
  
  static inline void grep_unlock(void)
  {
 -      assert(num_threads);
        pthread_mutex_unlock(&grep_mutex);
  }
  
@@@ -231,9 -234,6 +231,9 @@@ static int wait_all(void
        int hit = 0;
        int i;
  
 +      if (!HAVE_THREADS)
 +              BUG("Never call this function unless you have started threads");
 +
        grep_lock();
        all_work_added = 1;
  
  
        return hit;
  }
 -#else /* !NO_PTHREADS */
 -
 -static int wait_all(void)
 -{
 -      return 0;
 -}
 -#endif
  
  static int grep_cmd_config(const char *var, const char *value, void *cb)
  {
                if (num_threads < 0)
                        die(_("invalid number of threads specified (%d) for %s"),
                            num_threads, var);
 -#ifdef NO_PTHREADS
 -              else if (num_threads && num_threads != 1) {
 +              else if (!HAVE_THREADS && num_threads > 1) {
                        /*
                         * TRANSLATORS: %s is the configuration
                         * variable for tweaking threads, currently
                         * grep.threads
                         */
                        warning(_("no threads support, ignoring %s"), var);
 -                      num_threads = 0;
 +                      num_threads = 1;
                }
 -#endif
        }
  
        if (!strcmp(var, "submodule.recurse"))
@@@ -321,14 -330,17 +321,14 @@@ static int grep_oid(struct grep_opt *op
        grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
        strbuf_release(&pathbuf);
  
 -#ifndef NO_PTHREADS
 -      if (num_threads) {
 +      if (num_threads > 1) {
                /*
                 * add_work() copies gs and thus assumes ownership of
                 * its fields, so do not call grep_source_clear()
                 */
                add_work(opt, &gs);
                return 0;
 -      } else
 -#endif
 -      {
 +      } else {
                int hit;
  
                hit = grep_source(opt, &gs);
@@@ -351,14 -363,17 +351,14 @@@ static int grep_file(struct grep_opt *o
        grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
        strbuf_release(&buf);
  
 -#ifndef NO_PTHREADS
 -      if (num_threads) {
 +      if (num_threads > 1) {
                /*
                 * add_work() copies gs and thus assumes ownership of
                 * its fields, so do not call grep_source_clear()
                 */
                add_work(opt, &gs);
                return 0;
 -      } else
 -#endif
 -      {
 +      } else {
                int hit;
  
                hit = grep_source(opt, &gs);
@@@ -407,23 -422,11 +407,23 @@@ static int grep_submodule(struct grep_o
        struct repository submodule;
        int hit;
  
 -      if (!is_submodule_active(superproject, path))
 +      /*
 +       * NEEDSWORK: submodules functions need to be protected because they
 +       * access the object store via config_from_gitmodules(): the latter
 +       * uses get_oid() which, for now, relies on the global the_repository
 +       * object.
 +       */
 +      grep_read_lock();
 +
 +      if (!is_submodule_active(superproject, path)) {
 +              grep_read_unlock();
                return 0;
 +      }
  
 -      if (repo_submodule_init(&submodule, superproject, path))
 +      if (repo_submodule_init(&submodule, superproject, path)) {
 +              grep_read_unlock();
                return 0;
 +      }
  
        repo_read_gitmodules(&submodule);
  
         * store is no longer global and instead is a member of the repository
         * object.
         */
 -      grep_read_lock();
        add_to_alternates_memory(submodule.objects->objectdir);
        grep_read_unlock();
  
@@@ -708,11 -712,14 +708,14 @@@ static int context_callback(const struc
  static int file_callback(const struct option *opt, const char *arg, int unset)
  {
        struct grep_opt *grep_opt = opt->value;
-       int from_stdin = !strcmp(arg, "-");
+       int from_stdin;
        FILE *patterns;
        int lno = 0;
        struct strbuf sb = STRBUF_INIT;
  
+       BUG_ON_OPT_NEG(unset);
+       from_stdin = !strcmp(arg, "-");
        patterns = from_stdin ? stdin : fopen(arg, "r");
        if (!patterns)
                die_errno(_("cannot open '%s'"), arg);
  static int not_callback(const struct option *opt, const char *arg, int unset)
  {
        struct grep_opt *grep_opt = opt->value;
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
        append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT);
        return 0;
  }
  static int and_callback(const struct option *opt, const char *arg, int unset)
  {
        struct grep_opt *grep_opt = opt->value;
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
        append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND);
        return 0;
  }
  static int open_callback(const struct option *opt, const char *arg, int unset)
  {
        struct grep_opt *grep_opt = opt->value;
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
        append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN);
        return 0;
  }
  static int close_callback(const struct option *opt, const char *arg, int unset)
  {
        struct grep_opt *grep_opt = opt->value;
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
        append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN);
        return 0;
  }
@@@ -762,6 -777,7 +773,7 @@@ static int pattern_callback(const struc
                            int unset)
  {
        struct grep_opt *grep_opt = opt->value;
+       BUG_ON_OPT_NEG(unset);
        append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN);
        return 0;
  }
@@@ -1034,35 -1050,39 +1046,35 @@@ int cmd_grep(int argc, const char **arg
        pathspec.recursive = 1;
        pathspec.recurse_submodules = !!recurse_submodules;
  
 -#ifndef NO_PTHREADS
 -      if (list.nr || cached || show_in_pager)
 -              num_threads = 0;
 -      else if (num_threads == 0)
 -              num_threads = GREP_NUM_THREADS_DEFAULT;
 -      else if (num_threads < 0)
 -              die(_("invalid number of threads specified (%d)"), num_threads);
 -      if (num_threads == 1)
 -              num_threads = 0;
 -#else
 -      if (num_threads)
 +      if (list.nr || cached || show_in_pager) {
 +              if (num_threads > 1)
 +                      warning(_("invalid option combination, ignoring --threads"));
 +              num_threads = 1;
 +      } else if (!HAVE_THREADS && num_threads > 1) {
                warning(_("no threads support, ignoring --threads"));
 -      num_threads = 0;
 -#endif
 +              num_threads = 1;
 +      } else if (num_threads < 0)
 +              die(_("invalid number of threads specified (%d)"), num_threads);
 +      else if (num_threads == 0)
 +              num_threads = HAVE_THREADS ? GREP_NUM_THREADS_DEFAULT : 1;
  
 -      if (!num_threads)
 +      if (num_threads > 1) {
 +              if (!HAVE_THREADS)
 +                      BUG("Somebody got num_threads calculation wrong!");
 +              if (!(opt.name_only || opt.unmatch_name_only || opt.count)
 +                  && (opt.pre_context || opt.post_context ||
 +                      opt.file_break || opt.funcbody))
 +                      skip_first_line = 1;
 +              start_threads(&opt);
 +      } else {
                /*
                 * The compiled patterns on the main path are only
                 * used when not using threading. Otherwise
 -               * start_threads() below calls compile_grep_patterns()
 +               * start_threads() above calls compile_grep_patterns()
                 * for each thread.
                 */
                compile_grep_patterns(&opt);
 -
 -#ifndef NO_PTHREADS
 -      if (num_threads) {
 -              if (!(opt.name_only || opt.unmatch_name_only || opt.count)
 -                  && (opt.pre_context || opt.post_context ||
 -                      opt.file_break || opt.funcbody))
 -                      skip_first_line = 1;
 -              start_threads(&opt);
        }
 -#endif
  
        if (show_in_pager && (cached || list.nr))
                die(_("--open-files-in-pager only works on the worktree"));
                hit = grep_objects(&opt, &pathspec, &list);
        }
  
 -      if (num_threads)
 +      if (num_threads > 1)
                hit |= wait_all();
        if (hit && show_in_pager)
                run_pager(&opt, prefix);
diff --combined builtin/pack-objects.c
index 2193054d2776b1d0d9f440b6035faacbfd0e4042,6718e62c15ded347f122a985711a965d74bdf7b8..794714e4f8703c0efed76fc37006fba05496f660
@@@ -1953,6 -1953,8 +1953,6 @@@ static int delta_cacheable(unsigned lon
        return 0;
  }
  
 -#ifndef NO_PTHREADS
 -
  /* Protect access to object database */
  static pthread_mutex_t read_mutex;
  #define read_lock()           pthread_mutex_lock(&read_mutex)
@@@ -1977,6 -1979,16 +1977,6 @@@ static pthread_mutex_t progress_mutex
   * ahead in the list because they can be stolen and would need
   * progress_mutex for protection.
   */
 -#else
 -
 -#define read_lock()           (void)0
 -#define read_unlock()         (void)0
 -#define cache_lock()          (void)0
 -#define cache_unlock()                (void)0
 -#define progress_lock()               (void)0
 -#define progress_unlock()     (void)0
 -
 -#endif
  
  /*
   * Return the size of the object without doing any delta
@@@ -2335,6 -2347,8 +2335,6 @@@ static void find_deltas(struct object_e
        free(array);
  }
  
 -#ifndef NO_PTHREADS
 -
  static void try_to_free_from_threads(size_t size)
  {
        read_lock();
@@@ -2563,6 -2577,10 +2563,6 @@@ static void ll_find_deltas(struct objec
        free(p);
  }
  
 -#else
 -#define ll_find_deltas(l, s, w, d, p) find_deltas(l, &s, w, d, p)
 -#endif
 -
  static void add_tag_chain(const struct object_id *oid)
  {
        struct tag *tag;
@@@ -2715,10 -2733,12 +2715,10 @@@ static int git_pack_config(const char *
                if (delta_search_threads < 0)
                        die(_("invalid number of threads specified (%d)"),
                            delta_search_threads);
 -#ifdef NO_PTHREADS
 -              if (delta_search_threads != 1) {
 +              if (!HAVE_THREADS && delta_search_threads != 1) {
                        warning(_("no threads support, ignoring %s"), k);
                        delta_search_threads = 0;
                }
 -#endif
                return 0;
        }
        if (!strcmp(k, "pack.indexversion")) {
@@@ -3087,7 -3107,6 +3087,7 @@@ static void get_object_list(int ac, con
  
        repo_init_revisions(the_repository, &revs, NULL);
        save_commit_buffer = 0;
 +      revs.allow_exclude_promisor_objects_opt = 1;
        setup_revisions(ac, av, &revs, NULL);
  
        /* make sure shallows are read */
@@@ -3187,6 -3206,9 +3187,9 @@@ static int option_parse_index_version(c
  {
        char *c;
        const char *val = arg;
+       BUG_ON_OPT_NEG(unset);
        pack_idx_opts.version = strtoul(val, &c, 10);
        if (pack_idx_opts.version > 2)
                die(_("unsupported index version %s"), val);
@@@ -3233,7 -3255,7 +3236,7 @@@ int cmd_pack_objects(int argc, const ch
                         N_("similar to --all-progress when progress meter is shown")),
                { OPTION_CALLBACK, 0, "index-version", NULL, N_("<version>[,<offset>]"),
                  N_("write the pack index file in the specified idx format version"),
-                 0, option_parse_index_version },
+                 PARSE_OPT_NONEG, option_parse_index_version },
                OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit,
                              N_("maximum size of each output pack file")),
                OPT_BOOL(0, "local", &local,
        if (!delta_search_threads)      /* --threads=0 means autodetect */
                delta_search_threads = online_cpus();
  
 -#ifdef NO_PTHREADS
 -      if (delta_search_threads != 1)
 +      if (!HAVE_THREADS && delta_search_threads != 1)
                warning(_("no threads support, ignoring --threads"));
 -#endif
        if (!pack_to_stdout && !pack_size_limit)
                pack_size_limit = pack_size_limit_cfg;
        if (pack_to_stdout && pack_size_limit)
diff --combined builtin/update-index.c
index 0e1dcf0438f7b2ec37b404d3f2acb7bd70650b67,faa16c61f10629866982ecce9f9dc9224bd79216..31e7cce3013a3b3f358834f24848fc73b67f8e8b
@@@ -782,7 -782,7 +782,7 @@@ struct refresh_params 
  static int refresh(struct refresh_params *o, unsigned int flag)
  {
        setup_work_tree();
 -      read_cache_preload(NULL);
 +      read_cache();
        *o->has_errors |= refresh_cache(o->flags | flag);
        return 0;
  }
  static int refresh_callback(const struct option *opt,
                                const char *arg, int unset)
  {
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
        return refresh(opt->value, 0);
  }
  
  static int really_refresh_callback(const struct option *opt,
                                const char *arg, int unset)
  {
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
        return refresh(opt->value, REFRESH_REALLY);
  }
  
@@@ -803,6 -807,7 +807,7 @@@ static int chmod_callback(const struct 
                                const char *arg, int unset)
  {
        char *flip = opt->value;
+       BUG_ON_OPT_NEG(unset);
        if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2])
                return error("option 'chmod' expects \"+x\" or \"-x\"");
        *flip = arg[0];
  static int resolve_undo_clear_callback(const struct option *opt,
                                const char *arg, int unset)
  {
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
        resolve_undo_clear();
        return 0;
  }
@@@ -847,6 -854,8 +854,8 @@@ static int cacheinfo_callback(struct pa
        unsigned int mode;
        const char *path;
  
+       BUG_ON_OPT_NEG(unset);
        if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, &oid, &path)) {
                if (add_cacheinfo(mode, &oid, path, 0))
                        die("git update-index: --cacheinfo cannot add %s", path);
@@@ -869,6 -878,8 +878,8 @@@ static int stdin_cacheinfo_callback(str
  {
        int *nul_term_line = opt->value;
  
+       BUG_ON_OPT_NEG(unset);
        if (ctx->argc != 1)
                return error("option '%s' must be the last argument", opt->long_name);
        allow_add = allow_replace = allow_remove = 1;
@@@ -881,6 -892,8 +892,8 @@@ static int stdin_callback(struct parse_
  {
        int *read_from_stdin = opt->value;
  
+       BUG_ON_OPT_NEG(unset);
        if (ctx->argc != 1)
                return error("option '%s' must be the last argument", opt->long_name);
        *read_from_stdin = 1;
  }
  
  static int unresolve_callback(struct parse_opt_ctx_t *ctx,
-                               const struct option *opt, int flags)
+                               const struct option *opt, int unset)
  {
        int *has_errors = opt->value;
        const char *prefix = startup_info->prefix;
  
+       BUG_ON_OPT_NEG(unset);
        /* consume remaining arguments. */
        *has_errors = do_unresolve(ctx->argc, ctx->argv,
                                prefix, prefix ? strlen(prefix) : 0);
  }
  
  static int reupdate_callback(struct parse_opt_ctx_t *ctx,
-                               const struct option *opt, int flags)
+                               const struct option *opt, int unset)
  {
        int *has_errors = opt->value;
        const char *prefix = startup_info->prefix;
  
+       BUG_ON_OPT_NEG(unset);
        /* consume remaining arguments. */
        setup_work_tree();
        *has_errors = do_reupdate(ctx->argc, ctx->argv,
diff --combined midx.c
index a50b117b777d2659a6d930997ced4f43fbbc3e46,ecd583666ad6e8a756e291dfcd1eb7ec715ada2c..730ff84dff6cb23e4ba2218e956629dc1465744b
--- 1/midx.c
--- 2/midx.c
+++ b/midx.c
@@@ -176,13 -176,9 +176,13 @@@ cleanup_fail
        return NULL;
  }
  
 -static void close_midx(struct multi_pack_index *m)
 +void close_midx(struct multi_pack_index *m)
  {
        uint32_t i;
 +
 +      if (!m)
 +              return;
 +
        munmap((unsigned char *)m->data, m->data_len);
        close(m->fd);
        m->fd = -1;
        for (i = 0; i < m->num_packs; i++) {
                if (m->packs[i]) {
                        close_pack(m->packs[i]);
 -                      free(m->packs);
 +                      free(m->packs[i]);
                }
        }
        FREE_AND_NULL(m->packs);
@@@ -335,14 -331,9 +335,14 @@@ int prepare_multi_pack_index_one(struc
        struct multi_pack_index *m;
        struct multi_pack_index *m_search;
        int config_value;
 +      static int env_value = -1;
 +
 +      if (env_value < 0)
 +              env_value = git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0);
  
 -      if (repo_config_get_bool(r, "core.multipackindex", &config_value) ||
 -          !config_value)
 +      if (!env_value &&
 +          (repo_config_get_bool(r, "core.multipackindex", &config_value) ||
 +          !config_value))
                return 0;
  
        for (m_search = r->objects->multi_pack_index; m_search; m_search = m_search->next)
@@@ -721,12 -712,18 +721,18 @@@ static size_t write_midx_object_offsets
  static size_t write_midx_large_offsets(struct hashfile *f, uint32_t nr_large_offset,
                                       struct pack_midx_entry *objects, uint32_t nr_objects)
  {
-       struct pack_midx_entry *list = objects;
+       struct pack_midx_entry *list = objects, *end = objects + nr_objects;
        size_t written = 0;
  
        while (nr_large_offset) {
-               struct pack_midx_entry *obj = list++;
-               uint64_t offset = obj->offset;
+               struct pack_midx_entry *obj;
+               uint64_t offset;
+               if (list >= end)
+                       BUG("too many large-offset objects");
+               obj = list++;
+               offset = obj->offset;
  
                if (!(offset >> 31))
                        continue;
@@@ -923,14 -920,9 +929,14 @@@ cleanup
        return 0;
  }
  
 -void clear_midx_file(const char *object_dir)
 +void clear_midx_file(struct repository *r)
  {
 -      char *midx = get_midx_filename(object_dir);
 +      char *midx = get_midx_filename(r->objects->objectdir);
 +
 +      if (r->objects && r->objects->multi_pack_index) {
 +              close_midx(r->objects->multi_pack_index);
 +              r->objects->multi_pack_index = NULL;
 +      }
  
        if (remove_path(midx)) {
                UNLEAK(midx);