Merge branch 'sb/hashmap-customize-comparison'
authorJunio C Hamano <gitster@pobox.com>
Thu, 13 Jul 2017 23:14:54 +0000 (16:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 13 Jul 2017 23:14:54 +0000 (16:14 -0700)
Update the hashmap API so that data to customize the behaviour of
the comparison function can be specified at the time a hashmap is
initialized.

* sb/hashmap-customize-comparison:
hashmap: migrate documentation from Documentation/technical into header
patch-ids.c: use hashmap correctly
hashmap.h: compare function has access to a data field

1  2 
builtin/describe.c
config.c
refs.c
sha1_file.c
submodule-config.c
diff --combined builtin/describe.c
index 19eacdd170d98ea583cc2eb373247df600be24d7,8868f00ed0059595b5ce5c05d8905f72a637bcb7..89ea1cdd60a215473ecb52ff2a4ce51932e1cf6d
@@@ -54,8 -54,10 +54,10 @@@ static const char *prio_names[] = 
        N_("head"), N_("lightweight"), N_("annotated"),
  };
  
- static int commit_name_cmp(const struct commit_name *cn1,
-               const struct commit_name *cn2, const void *peeled)
+ static int commit_name_cmp(const void *unused_cmp_data,
+                          const struct commit_name *cn1,
+                          const struct commit_name *cn2,
+                          const void *peeled)
  {
        return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled);
  }
@@@ -143,7 -145,7 +145,7 @@@ static int get_name(const char *path, c
                        return 0;
  
                for_each_string_list_item(item, &exclude_patterns) {
 -                      if (!wildmatch(item->string, path + 10, 0, NULL))
 +                      if (!wildmatch(item->string, path + 10, 0))
                                return 0;
                }
        }
                        return 0;
  
                for_each_string_list_item(item, &patterns) {
 -                      if (!wildmatch(item->string, path + 10, 0, NULL))
 +                      if (!wildmatch(item->string, path + 10, 0))
                                break;
  
                        /* If we get here, no pattern matched. */
@@@ -501,7 -503,7 +503,7 @@@ int cmd_describe(int argc, const char *
                return cmd_name_rev(args.argc, args.argv, prefix);
        }
  
-       hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, 0);
+       hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, NULL, 0);
        for_each_rawref(get_name, NULL);
        if (!names.size && !always)
                die(_("No names found, cannot describe anything."));
diff --combined config.c
index a9356c1383861ecf7f33ee0262c8a60bbe20864f,4a31e31ac30f8552e5116f319fecb8cf76182938..231f9a750b96deda8d62f5a5debb384f40481566
+++ b/config.c
@@@ -7,7 -7,6 +7,7 @@@
   */
  #include "cache.h"
  #include "config.h"
 +#include "repository.h"
  #include "lockfile.h"
  #include "exec_cmd.h"
  #include "strbuf.h"
@@@ -73,6 -72,13 +73,6 @@@ static int core_compression_seen
  static int pack_compression_seen;
  static int zlib_compression_seen;
  
 -/*
 - * Default config_set that contains key-value pairs from the usual set of config
 - * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG
 - * config file and the global /etc/gitconfig)
 - */
 -static struct config_set the_config_set;
 -
  static int config_file_fgetc(struct config_source *conf)
  {
        return getc_unlocked(conf->u.file);
@@@ -238,7 -244,7 +238,7 @@@ again
        }
  
        ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
 -                       icase ? WM_CASEFOLD : 0, NULL);
 +                       icase ? WM_CASEFOLD : 0);
  
        if (!ret && !already_tried_absolute) {
                /*
@@@ -1598,6 -1604,31 +1598,6 @@@ int config_with_options(config_fn_t fn
        return do_git_config_sequence(opts, fn, data);
  }
  
 -static void git_config_raw(config_fn_t fn, void *data)
 -{
 -      struct config_options opts = {0};
 -
 -      opts.respect_includes = 1;
 -      if (have_git_dir()) {
 -              opts.commondir = get_git_common_dir();
 -              opts.git_dir = get_git_dir();
 -      }
 -
 -      if (config_with_options(fn, data, NULL, &opts) < 0)
 -              /*
 -               * config_with_options() normally returns only
 -               * zero, as most errors are fatal, and
 -               * non-fatal potential errors are guarded by "if"
 -               * statements that are entered only when no error is
 -               * possible.
 -               *
 -               * If we ever encounter a non-fatal error, it means
 -               * something went really wrong and we should stop
 -               * immediately.
 -               */
 -              die(_("unknown error occurred while reading the configuration files"));
 -}
 -
  static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
  {
        int i, value_index;
@@@ -1651,6 -1682,14 +1651,6 @@@ void read_early_config(config_fn_t cb, 
        strbuf_release(&gitdir);
  }
  
 -static void git_config_check_init(void);
 -
 -void git_config(config_fn_t fn, void *data)
 -{
 -      git_config_check_init();
 -      configset_iter(&the_config_set, fn, data);
 -}
 -
  static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
  {
        struct config_set_element k;
@@@ -1714,15 -1753,18 +1714,18 @@@ static int configset_add_value(struct c
        return 0;
  }
  
- static int config_set_element_cmp(const struct config_set_element *e1,
-                                const struct config_set_element *e2, const void *unused)
+ static int config_set_element_cmp(const void *unused_cmp_data,
+                                 const struct config_set_element *e1,
+                                 const struct config_set_element *e2,
+                                 const void *unused_keydata)
  {
        return strcmp(e1->key, e2->key);
  }
  
  void git_configset_init(struct config_set *cs)
  {
-       hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0);
+       hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp,
+                    NULL, 0);
        cs->hash_initialized = 1;
        cs->list.nr = 0;
        cs->list.alloc = 0;
@@@ -1860,194 -1902,87 +1863,194 @@@ int git_configset_get_pathname(struct c
                return 1;
  }
  
 -static void git_config_check_init(void)
 +/* Functions use to read configuration from a repository */
 +static void repo_read_config(struct repository *repo)
  {
 -      if (the_config_set.hash_initialized)
 +      struct config_options opts;
 +
 +      opts.respect_includes = 1;
 +      opts.commondir = repo->commondir;
 +      opts.git_dir = repo->gitdir;
 +
 +      if (!repo->config)
 +              repo->config = xcalloc(1, sizeof(struct config_set));
 +      else
 +              git_configset_clear(repo->config);
 +
 +      git_configset_init(repo->config);
 +
 +      if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
 +              /*
 +               * config_with_options() normally returns only
 +               * zero, as most errors are fatal, and
 +               * non-fatal potential errors are guarded by "if"
 +               * statements that are entered only when no error is
 +               * possible.
 +               *
 +               * If we ever encounter a non-fatal error, it means
 +               * something went really wrong and we should stop
 +               * immediately.
 +               */
 +              die(_("unknown error occurred while reading the configuration files"));
 +}
 +
 +static void git_config_check_init(struct repository *repo)
 +{
 +      if (repo->config && repo->config->hash_initialized)
                return;
 -      git_configset_init(&the_config_set);
 -      git_config_raw(config_set_callback, &the_config_set);
 +      repo_read_config(repo);
  }
  
 -void git_config_clear(void)
 +static void repo_config_clear(struct repository *repo)
  {
 -      if (!the_config_set.hash_initialized)
 +      if (!repo->config || !repo->config->hash_initialized)
                return;
 -      git_configset_clear(&the_config_set);
 +      git_configset_clear(repo->config);
  }
  
 -int git_config_get_value(const char *key, const char **value)
 +void repo_config(struct repository *repo, config_fn_t fn, void *data)
  {
 -      git_config_check_init();
 -      return git_configset_get_value(&the_config_set, key, value);
 +      git_config_check_init(repo);
 +      configset_iter(repo->config, fn, data);
  }
  
 -const struct string_list *git_config_get_value_multi(const char *key)
 +int repo_config_get_value(struct repository *repo,
 +                        const char *key, const char **value)
  {
 -      git_config_check_init();
 -      return git_configset_get_value_multi(&the_config_set, key);
 +      git_config_check_init(repo);
 +      return git_configset_get_value(repo->config, key, value);
  }
  
 -int git_config_get_string_const(const char *key, const char **dest)
 +const struct string_list *repo_config_get_value_multi(struct repository *repo,
 +                                                    const char *key)
 +{
 +      git_config_check_init(repo);
 +      return git_configset_get_value_multi(repo->config, key);
 +}
 +
 +int repo_config_get_string_const(struct repository *repo,
 +                               const char *key, const char **dest)
 +{
 +      int ret;
 +      git_config_check_init(repo);
 +      ret = git_configset_get_string_const(repo->config, key, dest);
 +      if (ret < 0)
 +              git_die_config(key, NULL);
 +      return ret;
 +}
 +
 +int repo_config_get_string(struct repository *repo,
 +                         const char *key, char **dest)
 +{
 +      git_config_check_init(repo);
 +      return repo_config_get_string_const(repo, key, (const char **)dest);
 +}
 +
 +int repo_config_get_int(struct repository *repo,
 +                      const char *key, int *dest)
 +{
 +      git_config_check_init(repo);
 +      return git_configset_get_int(repo->config, key, dest);
 +}
 +
 +int repo_config_get_ulong(struct repository *repo,
 +                        const char *key, unsigned long *dest)
 +{
 +      git_config_check_init(repo);
 +      return git_configset_get_ulong(repo->config, key, dest);
 +}
 +
 +int repo_config_get_bool(struct repository *repo,
 +                       const char *key, int *dest)
 +{
 +      git_config_check_init(repo);
 +      return git_configset_get_bool(repo->config, key, dest);
 +}
 +
 +int repo_config_get_bool_or_int(struct repository *repo,
 +                              const char *key, int *is_bool, int *dest)
 +{
 +      git_config_check_init(repo);
 +      return git_configset_get_bool_or_int(repo->config, key, is_bool, dest);
 +}
 +
 +int repo_config_get_maybe_bool(struct repository *repo,
 +                             const char *key, int *dest)
 +{
 +      git_config_check_init(repo);
 +      return git_configset_get_maybe_bool(repo->config, key, dest);
 +}
 +
 +int repo_config_get_pathname(struct repository *repo,
 +                           const char *key, const char **dest)
  {
        int ret;
 -      git_config_check_init();
 -      ret = git_configset_get_string_const(&the_config_set, key, dest);
 +      git_config_check_init(repo);
 +      ret = git_configset_get_pathname(repo->config, key, dest);
        if (ret < 0)
                git_die_config(key, NULL);
        return ret;
  }
  
 +/* Functions used historically to read configuration from 'the_repository' */
 +void git_config(config_fn_t fn, void *data)
 +{
 +      repo_config(the_repository, fn, data);
 +}
 +
 +void git_config_clear(void)
 +{
 +      repo_config_clear(the_repository);
 +}
 +
 +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)
 +{
 +      return repo_config_get_value_multi(the_repository, key);
 +}
 +
 +int git_config_get_string_const(const char *key, const char **dest)
 +{
 +      return repo_config_get_string_const(the_repository, key, dest);
 +}
 +
  int git_config_get_string(const char *key, char **dest)
  {
 -      git_config_check_init();
 -      return git_config_get_string_const(key, (const char **)dest);
 +      return repo_config_get_string(the_repository, key, dest);
  }
  
  int git_config_get_int(const char *key, int *dest)
  {
 -      git_config_check_init();
 -      return git_configset_get_int(&the_config_set, key, dest);
 +      return repo_config_get_int(the_repository, key, dest);
  }
  
  int git_config_get_ulong(const char *key, unsigned long *dest)
  {
 -      git_config_check_init();
 -      return git_configset_get_ulong(&the_config_set, key, dest);
 +      return repo_config_get_ulong(the_repository, key, dest);
  }
  
  int git_config_get_bool(const char *key, int *dest)
  {
 -      git_config_check_init();
 -      return git_configset_get_bool(&the_config_set, key, dest);
 +      return repo_config_get_bool(the_repository, key, dest);
  }
  
  int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)
  {
 -      git_config_check_init();
 -      return git_configset_get_bool_or_int(&the_config_set, key, is_bool, dest);
 +      return repo_config_get_bool_or_int(the_repository, key, is_bool, dest);
  }
  
  int git_config_get_maybe_bool(const char *key, int *dest)
  {
 -      git_config_check_init();
 -      return git_configset_get_maybe_bool(&the_config_set, key, dest);
 +      return repo_config_get_maybe_bool(the_repository, key, dest);
  }
  
  int git_config_get_pathname(const char *key, const char **dest)
  {
 -      int ret;
 -      git_config_check_init();
 -      ret = git_configset_get_pathname(&the_config_set, key, dest);
 -      if (ret < 0)
 -              git_die_config(key, NULL);
 -      return ret;
 +      return repo_config_get_pathname(the_repository, key, dest);
  }
  
  int git_config_get_expiry(const char *key, const char **output)
diff --combined refs.c
index 7aae78cb551fd2743982d7f9fc60e6de45df9a11,eef1a7b67de48d1c929921549889112fe1f5d934..ba22f4acefa262cc1029bcf8360edb21c0635953
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -230,7 -230,7 +230,7 @@@ static int filter_refs(const char *refn
  {
        struct ref_filter *filter = (struct ref_filter *)data;
  
 -      if (wildmatch(filter->pattern, refname, 0, NULL))
 +      if (wildmatch(filter->pattern, refname, 0))
                return 0;
        return filter->fn(refname, oid, flags, filter->cb_data);
  }
@@@ -1525,7 -1525,8 +1525,8 @@@ struct ref_store_hash_entr
        char name[FLEX_ARRAY];
  };
  
- static int ref_store_hash_cmp(const void *entry, const void *entry_or_key,
+ static int ref_store_hash_cmp(const void *unused_cmp_data,
+                             const void *entry, const void *entry_or_key,
                              const void *keydata)
  {
        const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
@@@ -1608,7 -1609,7 +1609,7 @@@ static void register_ref_store_map(stru
                                   const char *name)
  {
        if (!map->tablesize)
-               hashmap_init(map, ref_store_hash_cmp, 0);
+               hashmap_init(map, ref_store_hash_cmp, NULL, 0);
  
        if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
                die("BUG: %s ref_store '%s' initialized twice", type, name);
diff --combined sha1_file.c
index 5862386cd0c2b2020890905bd06cd3a9d89c123b,8c523a5b74542b141154372ae07495c35d2abae1..fca165f13ccd145c184eaa8601bd60156d5b073b
@@@ -1964,7 -1964,7 +1964,7 @@@ static int parse_sha1_header_extended(c
         * we're obtaining the type using '--allow-unknown-type'
         * option.
         */
 -      if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
 +      if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0))
                type = 0;
        else if (type < 0)
                die("invalid object type");
@@@ -2002,7 -2002,20 +2002,7 @@@ int parse_sha1_header(const char *hdr, 
        struct object_info oi = OBJECT_INFO_INIT;
  
        oi.sizep = sizep;
 -      return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
 -}
 -
 -static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
 -{
 -      int ret;
 -      git_zstream stream;
 -      char hdr[8192];
 -
 -      ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr));
 -      if (ret < Z_OK || (*type = parse_sha1_header(hdr, size)) < 0)
 -              return NULL;
 -
 -      return unpack_sha1_rest(&stream, hdr, *size, sha1);
 +      return parse_sha1_header_extended(hdr, &oi, 0);
  }
  
  unsigned long get_size_from_delta(struct packed_git *p,
@@@ -2226,6 -2239,107 +2226,6 @@@ unwind
        goto out;
  }
  
 -int packed_object_info(struct packed_git *p, off_t obj_offset,
 -                     struct object_info *oi)
 -{
 -      struct pack_window *w_curs = NULL;
 -      unsigned long size;
 -      off_t curpos = obj_offset;
 -      enum object_type type;
 -
 -      /*
 -       * We always get the representation type, but only convert it to
 -       * a "real" type later if the caller is interested.
 -       */
 -      type = unpack_object_header(p, &w_curs, &curpos, &size);
 -
 -      if (oi->sizep) {
 -              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 -                      off_t tmp_pos = curpos;
 -                      off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
 -                                                         type, obj_offset);
 -                      if (!base_offset) {
 -                              type = OBJ_BAD;
 -                              goto out;
 -                      }
 -                      *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
 -                      if (*oi->sizep == 0) {
 -                              type = OBJ_BAD;
 -                              goto out;
 -                      }
 -              } else {
 -                      *oi->sizep = size;
 -              }
 -      }
 -
 -      if (oi->disk_sizep) {
 -              struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
 -              *oi->disk_sizep = revidx[1].offset - obj_offset;
 -      }
 -
 -      if (oi->typep) {
 -              *oi->typep = packed_to_object_type(p, obj_offset, type, &w_curs, curpos);
 -              if (*oi->typep < 0) {
 -                      type = OBJ_BAD;
 -                      goto out;
 -              }
 -      }
 -
 -      if (oi->delta_base_sha1) {
 -              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 -                      const unsigned char *base;
 -
 -                      base = get_delta_base_sha1(p, &w_curs, curpos,
 -                                                 type, obj_offset);
 -                      if (!base) {
 -                              type = OBJ_BAD;
 -                              goto out;
 -                      }
 -
 -                      hashcpy(oi->delta_base_sha1, base);
 -              } else
 -                      hashclr(oi->delta_base_sha1);
 -      }
 -
 -out:
 -      unuse_pack(&w_curs);
 -      return type;
 -}
 -
 -static void *unpack_compressed_entry(struct packed_git *p,
 -                                  struct pack_window **w_curs,
 -                                  off_t curpos,
 -                                  unsigned long size)
 -{
 -      int st;
 -      git_zstream stream;
 -      unsigned char *buffer, *in;
 -
 -      buffer = xmallocz_gently(size);
 -      if (!buffer)
 -              return NULL;
 -      memset(&stream, 0, sizeof(stream));
 -      stream.next_out = buffer;
 -      stream.avail_out = size + 1;
 -
 -      git_inflate_init(&stream);
 -      do {
 -              in = use_pack(p, w_curs, curpos, &stream.avail_in);
 -              stream.next_in = in;
 -              st = git_inflate(&stream, Z_FINISH);
 -              if (!stream.avail_out)
 -                      break; /* the payload is larger than it should be */
 -              curpos += stream.next_in - in;
 -      } while (st == Z_OK || st == Z_BUF_ERROR);
 -      git_inflate_end(&stream);
 -      if ((st != Z_STREAM_END) || stream.total_out != size) {
 -              free(buffer);
 -              return NULL;
 -      }
 -
 -      return buffer;
 -}
 -
  static struct hashmap delta_base_cache;
  static size_t delta_base_cached;
  
@@@ -2275,7 -2389,8 +2275,8 @@@ static int delta_base_cache_key_eq(cons
        return a->p == b->p && a->base_offset == b->base_offset;
  }
  
- static int delta_base_cache_hash_cmp(const void *va, const void *vb,
+ static int delta_base_cache_hash_cmp(const void *unused_cmp_data,
+                                    const void *va, const void *vb,
                                     const void *vkey)
  {
        const struct delta_base_cache_entry *a = va, *b = vb;
@@@ -2313,10 -2428,8 +2314,10 @@@ static void *cache_or_unpack_entry(stru
        if (!ent)
                return unpack_entry(p, base_offset, type, base_size);
  
 -      *type = ent->type;
 -      *base_size = ent->size;
 +      if (type)
 +              *type = ent->type;
 +      if (base_size)
 +              *base_size = ent->size;
        return xmemdupz(ent->data, ent->size);
  }
  
@@@ -2360,128 -2473,11 +2361,128 @@@ static void add_delta_base_cache(struc
        list_add_tail(&ent->lru, &delta_base_cache_lru);
  
        if (!delta_base_cache.cmpfn)
-               hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, 0);
+               hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0);
        hashmap_entry_init(ent, pack_entry_hash(p, base_offset));
        hashmap_add(&delta_base_cache, ent);
  }
  
 +int packed_object_info(struct packed_git *p, off_t obj_offset,
 +                     struct object_info *oi)
 +{
 +      struct pack_window *w_curs = NULL;
 +      unsigned long size;
 +      off_t curpos = obj_offset;
 +      enum object_type type;
 +
 +      /*
 +       * We always get the representation type, but only convert it to
 +       * a "real" type later if the caller is interested.
 +       */
 +      if (oi->contentp) {
 +              *oi->contentp = cache_or_unpack_entry(p, obj_offset, oi->sizep,
 +                                                    &type);
 +              if (!*oi->contentp)
 +                      type = OBJ_BAD;
 +      } else {
 +              type = unpack_object_header(p, &w_curs, &curpos, &size);
 +      }
 +
 +      if (!oi->contentp && oi->sizep) {
 +              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 +                      off_t tmp_pos = curpos;
 +                      off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
 +                                                         type, obj_offset);
 +                      if (!base_offset) {
 +                              type = OBJ_BAD;
 +                              goto out;
 +                      }
 +                      *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
 +                      if (*oi->sizep == 0) {
 +                              type = OBJ_BAD;
 +                              goto out;
 +                      }
 +              } else {
 +                      *oi->sizep = size;
 +              }
 +      }
 +
 +      if (oi->disk_sizep) {
 +              struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
 +              *oi->disk_sizep = revidx[1].offset - obj_offset;
 +      }
 +
 +      if (oi->typep || oi->typename) {
 +              enum object_type ptot;
 +              ptot = packed_to_object_type(p, obj_offset, type, &w_curs,
 +                                           curpos);
 +              if (oi->typep)
 +                      *oi->typep = ptot;
 +              if (oi->typename) {
 +                      const char *tn = typename(ptot);
 +                      if (tn)
 +                              strbuf_addstr(oi->typename, tn);
 +              }
 +              if (ptot < 0) {
 +                      type = OBJ_BAD;
 +                      goto out;
 +              }
 +      }
 +
 +      if (oi->delta_base_sha1) {
 +              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 +                      const unsigned char *base;
 +
 +                      base = get_delta_base_sha1(p, &w_curs, curpos,
 +                                                 type, obj_offset);
 +                      if (!base) {
 +                              type = OBJ_BAD;
 +                              goto out;
 +                      }
 +
 +                      hashcpy(oi->delta_base_sha1, base);
 +              } else
 +                      hashclr(oi->delta_base_sha1);
 +      }
 +
 +out:
 +      unuse_pack(&w_curs);
 +      return type;
 +}
 +
 +static void *unpack_compressed_entry(struct packed_git *p,
 +                                  struct pack_window **w_curs,
 +                                  off_t curpos,
 +                                  unsigned long size)
 +{
 +      int st;
 +      git_zstream stream;
 +      unsigned char *buffer, *in;
 +
 +      buffer = xmallocz_gently(size);
 +      if (!buffer)
 +              return NULL;
 +      memset(&stream, 0, sizeof(stream));
 +      stream.next_out = buffer;
 +      stream.avail_out = size + 1;
 +
 +      git_inflate_init(&stream);
 +      do {
 +              in = use_pack(p, w_curs, curpos, &stream.avail_in);
 +              stream.next_in = in;
 +              st = git_inflate(&stream, Z_FINISH);
 +              if (!stream.avail_out)
 +                      break; /* the payload is larger than it should be */
 +              curpos += stream.next_in - in;
 +      } while (st == Z_OK || st == Z_BUF_ERROR);
 +      git_inflate_end(&stream);
 +      if ((st != Z_STREAM_END) || stream.total_out != size) {
 +              free(buffer);
 +              return NULL;
 +      }
 +
 +      return buffer;
 +}
 +
  static void *read_object(const unsigned char *sha1, enum object_type *type,
                         unsigned long *size);
  
@@@ -2675,10 -2671,8 +2676,10 @@@ void *unpack_entry(struct packed_git *p
                free(external_base);
        }
  
 -      *final_type = type;
 -      *final_size = size;
 +      if (final_type)
 +              *final_type = type;
 +      if (final_size)
 +              *final_size = size;
  
        unuse_pack(&w_curs);
  
@@@ -2912,7 -2906,6 +2913,7 @@@ static int sha1_loose_object_info(cons
        git_zstream stream;
        char hdr[32];
        struct strbuf hdrbuf = STRBUF_INIT;
 +      unsigned long size_scratch;
  
        if (oi->delta_base_sha1)
                hashclr(oi->delta_base_sha1);
         * return value implicitly indicates whether the
         * object even exists.
         */
 -      if (!oi->typep && !oi->typename && !oi->sizep) {
 +      if (!oi->typep && !oi->typename && !oi->sizep && !oi->contentp) {
                const char *path;
                struct stat st;
                if (stat_sha1_file(sha1, &st, &path) < 0)
        map = map_sha1_file(sha1, &mapsize);
        if (!map)
                return -1;
 +
 +      if (!oi->sizep)
 +              oi->sizep = &size_scratch;
 +
        if (oi->disk_sizep)
                *oi->disk_sizep = mapsize;
 -      if ((flags & LOOKUP_UNKNOWN_OBJECT)) {
 +      if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) {
                if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
                        status = error("unable to unpack %s header with --allow-unknown-type",
                                       sha1_to_hex(sha1));
                                       sha1_to_hex(sha1));
        } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
                status = error("unable to parse %s header", sha1_to_hex(sha1));
 -      git_inflate_end(&stream);
 +
 +      if (status >= 0 && oi->contentp)
 +              *oi->contentp = unpack_sha1_rest(&stream, hdr,
 +                                               *oi->sizep, sha1);
 +      else
 +              git_inflate_end(&stream);
 +
        munmap(map, mapsize);
        if (status && oi->typep)
                *oi->typep = status;
 +      if (oi->sizep == &size_scratch)
 +              oi->sizep = NULL;
        strbuf_release(&hdrbuf);
        return (status < 0) ? status : 0;
  }
  
  int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
  {
 -      struct cached_object *co;
 +      static struct object_info blank_oi = OBJECT_INFO_INIT;
        struct pack_entry e;
        int rtype;
 -      enum object_type real_type;
 -      const unsigned char *real = lookup_replace_object_extended(sha1, flags);
 -
 -      co = find_cached_object(real);
 -      if (co) {
 -              if (oi->typep)
 -                      *(oi->typep) = co->type;
 -              if (oi->sizep)
 -                      *(oi->sizep) = co->size;
 -              if (oi->disk_sizep)
 -                      *(oi->disk_sizep) = 0;
 -              if (oi->delta_base_sha1)
 -                      hashclr(oi->delta_base_sha1);
 -              if (oi->typename)
 -                      strbuf_addstr(oi->typename, typename(co->type));
 -              oi->whence = OI_CACHED;
 -              return 0;
 +      const unsigned char *real = (flags & OBJECT_INFO_LOOKUP_REPLACE) ?
 +                                  lookup_replace_object(sha1) :
 +                                  sha1;
 +
 +      if (!oi)
 +              oi = &blank_oi;
 +
 +      if (!(flags & OBJECT_INFO_SKIP_CACHED)) {
 +              struct cached_object *co = find_cached_object(real);
 +              if (co) {
 +                      if (oi->typep)
 +                              *(oi->typep) = co->type;
 +                      if (oi->sizep)
 +                              *(oi->sizep) = co->size;
 +                      if (oi->disk_sizep)
 +                              *(oi->disk_sizep) = 0;
 +                      if (oi->delta_base_sha1)
 +                              hashclr(oi->delta_base_sha1);
 +                      if (oi->typename)
 +                              strbuf_addstr(oi->typename, typename(co->type));
 +                      if (oi->contentp)
 +                              *oi->contentp = xmemdupz(co->buf, co->size);
 +                      oi->whence = OI_CACHED;
 +                      return 0;
 +              }
        }
  
        if (!find_pack_entry(real, &e)) {
                }
  
                /* Not a loose object; someone else may have just packed it. */
 -              reprepare_packed_git();
 -              if (!find_pack_entry(real, &e))
 +              if (flags & OBJECT_INFO_QUICK) {
                        return -1;
 +              } else {
 +                      reprepare_packed_git();
 +                      if (!find_pack_entry(real, &e))
 +                              return -1;
 +              }
        }
  
 -      /*
 -       * packed_object_info() does not follow the delta chain to
 -       * find out the real type, unless it is given oi->typep.
 -       */
 -      if (oi->typename && !oi->typep)
 -              oi->typep = &real_type;
 +      if (oi == &blank_oi)
 +              /*
 +               * We know that the caller doesn't actually need the
 +               * information below, so return early.
 +               */
 +              return 0;
  
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
                mark_bad_packed_object(e.p, real);
 -              if (oi->typep == &real_type)
 -                      oi->typep = NULL;
                return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
                oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
                                         rtype == OBJ_OFS_DELTA);
        }
 -      if (oi->typename)
 -              strbuf_addstr(oi->typename, typename(*oi->typep));
 -      if (oi->typep == &real_type)
 -              oi->typep = NULL;
  
        return 0;
  }
@@@ -3056,8 -3031,7 +3057,8 @@@ int sha1_object_info(const unsigned cha
  
        oi.typep = &type;
        oi.sizep = sizep;
 -      if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0)
 +      if (sha1_object_info_extended(sha1, &oi,
 +                                    OBJECT_INFO_LOOKUP_REPLACE) < 0)
                return -1;
        return type;
  }
@@@ -3107,15 -3081,28 +3108,15 @@@ int pretend_sha1_file(void *buf, unsign
  static void *read_object(const unsigned char *sha1, enum object_type *type,
                         unsigned long *size)
  {
 -      unsigned long mapsize;
 -      void *map, *buf;
 -      struct cached_object *co;
 -
 -      co = find_cached_object(sha1);
 -      if (co) {
 -              *type = co->type;
 -              *size = co->size;
 -              return xmemdupz(co->buf, co->size);
 -      }
 +      struct object_info oi = OBJECT_INFO_INIT;
 +      void *content;
 +      oi.typep = type;
 +      oi.sizep = size;
 +      oi.contentp = &content;
  
 -      buf = read_packed_sha1(sha1, type, size);
 -      if (buf)
 -              return buf;
 -      map = map_sha1_file(sha1, &mapsize);
 -      if (map) {
 -              buf = unpack_sha1_file(map, mapsize, type, size, sha1);
 -              munmap(map, mapsize);
 -              return buf;
 -      }
 -      reprepare_packed_git();
 -      return read_packed_sha1(sha1, type, size);
 +      if (sha1_object_info_extended(sha1, &oi, 0) < 0)
 +              return NULL;
 +      return content;
  }
  
  /*
  void *read_sha1_file_extended(const unsigned char *sha1,
                              enum object_type *type,
                              unsigned long *size,
 -                            unsigned flag)
 +                            int lookup_replace)
  {
        void *data;
        const struct packed_git *p;
        const char *path;
        struct stat st;
 -      const unsigned char *repl = lookup_replace_object_extended(sha1, flag);
 +      const unsigned char *repl = lookup_replace ? lookup_replace_object(sha1)
 +                                                 : sha1;
  
        errno = 0;
        data = read_object(repl, type, size);
@@@ -3494,10 -3480,18 +3495,10 @@@ int has_sha1_pack(const unsigned char *
  
  int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
  {
 -      struct pack_entry e;
 -
        if (!startup_info->have_repository)
                return 0;
 -      if (find_pack_entry(sha1, &e))
 -              return 1;
 -      if (has_loose_object(sha1))
 -              return 1;
 -      if (flags & HAS_SHA1_QUICK)
 -              return 0;
 -      reprepare_packed_git();
 -      return find_pack_entry(sha1, &e);
 +      return sha1_object_info_extended(sha1, NULL,
 +                                       flags | OBJECT_INFO_SKIP_CACHED) >= 0;
  }
  
  int has_object_file(const struct object_id *oid)
@@@ -3742,32 -3736,22 +3743,32 @@@ void assert_sha1_type(const unsigned ch
                    typename(expect));
  }
  
 -static int for_each_file_in_obj_subdir(int subdir_nr,
 -                                     struct strbuf *path,
 -                                     each_loose_object_fn obj_cb,
 -                                     each_loose_cruft_fn cruft_cb,
 -                                     each_loose_subdir_fn subdir_cb,
 -                                     void *data)
 +int for_each_file_in_obj_subdir(unsigned int subdir_nr,
 +                              struct strbuf *path,
 +                              each_loose_object_fn obj_cb,
 +                              each_loose_cruft_fn cruft_cb,
 +                              each_loose_subdir_fn subdir_cb,
 +                              void *data)
  {
 -      size_t baselen = path->len;
 -      DIR *dir = opendir(path->buf);
 +      size_t origlen, baselen;
 +      DIR *dir;
        struct dirent *de;
        int r = 0;
  
 +      if (subdir_nr > 0xff)
 +              BUG("invalid loose object subdirectory: %x", subdir_nr);
 +
 +      origlen = path->len;
 +      strbuf_complete(path, '/');
 +      strbuf_addf(path, "%02x", subdir_nr);
 +      baselen = path->len;
 +
 +      dir = opendir(path->buf);
        if (!dir) {
 -              if (errno == ENOENT)
 -                      return 0;
 -              return error_errno("unable to open %s", path->buf);
 +              if (errno != ENOENT)
 +                      r = error_errno("unable to open %s", path->buf);
 +              strbuf_setlen(path, origlen);
 +              return r;
        }
  
        while ((de = readdir(dir))) {
        if (!r && subdir_cb)
                r = subdir_cb(subdir_nr, path->buf, data);
  
 +      strbuf_setlen(path, origlen);
 +
        return r;
  }
  
@@@ -3816,12 -3798,15 +3817,12 @@@ int for_each_loose_file_in_objdir_buf(s
                            each_loose_subdir_fn subdir_cb,
                            void *data)
  {
 -      size_t baselen = path->len;
        int r = 0;
        int i;
  
        for (i = 0; i < 256; i++) {
 -              strbuf_addf(path, "/%02x", i);
                r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb,
                                                subdir_cb, data);
 -              strbuf_setlen(path, baselen);
                if (r)
                        break;
        }
diff --combined submodule-config.c
index 37cfcceb950b1777b46abecd5415196c76e11826,0e1126183dbb75d3ebbf391004b2fe9f5708bd76..eeb154a894d6bfdec877d94d5922438b5f934730
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "repository.h"
  #include "config.h"
  #include "submodule-config.h"
  #include "submodule.h"
@@@ -16,7 -15,6 +16,7 @@@
  struct submodule_cache {
        struct hashmap for_path;
        struct hashmap for_name;
 +      unsigned initialized:1;
  };
  
  /*
@@@ -33,32 -31,31 +33,34 @@@ enum lookup_type 
        lookup_path
  };
  
- static int config_path_cmp(const struct submodule_entry *a,
 -static struct submodule_cache the_submodule_cache;
 -static int is_cache_init;
 -
+ static int config_path_cmp(const void *unused_cmp_data,
+                          const struct submodule_entry *a,
                           const struct submodule_entry *b,
-                          const void *unused)
+                          const void *unused_keydata)
  {
        return strcmp(a->config->path, b->config->path) ||
               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  }
  
- static int config_name_cmp(const struct submodule_entry *a,
+ static int config_name_cmp(const void *unused_cmp_data,
+                          const struct submodule_entry *a,
                           const struct submodule_entry *b,
-                          const void *unused)
+                          const void *unused_keydata)
  {
        return strcmp(a->config->name, b->config->name) ||
               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  }
  
 -static void cache_init(struct submodule_cache *cache)
 +static struct submodule_cache *submodule_cache_alloc(void)
 +{
 +      return xcalloc(1, sizeof(struct submodule_cache));
 +}
 +
 +static void submodule_cache_init(struct submodule_cache *cache)
  {
-       hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, 0);
-       hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, 0);
+       hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, NULL, 0);
+       hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, NULL, 0);
 +      cache->initialized = 1;
  }
  
  static void free_one_config(struct submodule_entry *entry)
        free(entry->config);
  }
  
 -static void cache_free(struct submodule_cache *cache)
 +static void submodule_cache_clear(struct submodule_cache *cache)
  {
        struct hashmap_iter iter;
        struct submodule_entry *entry;
  
 +      if (!cache->initialized)
 +              return;
 +
        /*
         * We iterate over the name hash here to be symmetric with the
         * allocation of struct submodule entries. Each is allocated by
  
        hashmap_free(&cache->for_path, 1);
        hashmap_free(&cache->for_name, 1);
 +      cache->initialized = 0;
 +}
 +
 +void submodule_cache_free(struct submodule_cache *cache)
 +{
 +      submodule_cache_clear(cache);
 +      free(cache);
  }
  
  static unsigned int hash_sha1_string(const unsigned char *sha1,
@@@ -509,62 -496,43 +511,62 @@@ out
        return submodule;
  }
  
 -static void ensure_cache_init(void)
 +static void submodule_cache_check_init(struct repository *repo)
  {
 -      if (is_cache_init)
 +      if (repo->submodule_cache && repo->submodule_cache->initialized)
                return;
  
 -      cache_init(&the_submodule_cache);
 -      is_cache_init = 1;
 +      if (!repo->submodule_cache)
 +              repo->submodule_cache = submodule_cache_alloc();
 +
 +      submodule_cache_init(repo->submodule_cache);
  }
  
 -int parse_submodule_config_option(const char *var, const char *value)
 +int submodule_config_option(struct repository *repo,
 +                          const char *var, const char *value)
  {
        struct parse_config_parameter parameter;
 -      parameter.cache = &the_submodule_cache;
 +
 +      submodule_cache_check_init(repo);
 +
 +      parameter.cache = repo->submodule_cache;
        parameter.treeish_name = NULL;
        parameter.gitmodules_sha1 = null_sha1;
        parameter.overwrite = 1;
  
 -      ensure_cache_init();
        return parse_config(var, value, &parameter);
  }
  
 +int parse_submodule_config_option(const char *var, const char *value)
 +{
 +      return submodule_config_option(the_repository, var, value);
 +}
 +
  const struct submodule *submodule_from_name(const unsigned char *treeish_name,
                const char *name)
  {
 -      ensure_cache_init();
 -      return config_from(&the_submodule_cache, treeish_name, name, lookup_name);
 +      submodule_cache_check_init(the_repository);
 +      return config_from(the_repository->submodule_cache, treeish_name, name, lookup_name);
  }
  
  const struct submodule *submodule_from_path(const unsigned char *treeish_name,
                const char *path)
  {
 -      ensure_cache_init();
 -      return config_from(&the_submodule_cache, treeish_name, path, lookup_path);
 +      submodule_cache_check_init(the_repository);
 +      return config_from(the_repository->submodule_cache, treeish_name, path, lookup_path);
 +}
 +
 +const struct submodule *submodule_from_cache(struct repository *repo,
 +                                           const unsigned char *treeish_name,
 +                                           const char *key)
 +{
 +      submodule_cache_check_init(repo);
 +      return config_from(repo->submodule_cache, treeish_name,
 +                         key, lookup_path);
  }
  
  void submodule_free(void)
  {
 -      cache_free(&the_submodule_cache);
 -      is_cache_init = 0;
 +      if (the_repository->submodule_cache)
 +              submodule_cache_clear(the_repository->submodule_cache);
  }