Merge branch 'sb/hashmap-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Fri, 11 Aug 2017 20:27:01 +0000 (13:27 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 11 Aug 2017 20:27:01 +0000 (13:27 -0700)
Many uses of comparision callback function the hashmap API uses
cast the callback function type when registering it to
hashmap_init(), which defeats the compile time type checking when
the callback interface changes (e.g. gaining more parameters).
The callback implementations have been updated to take "void *"
pointers and cast them to the type they expect instead.

* sb/hashmap-cleanup:
t/helper/test-hashmap: use custom data instead of duplicate cmp functions
name-hash.c: drop hashmap_cmp_fn cast
submodule-config.c: drop hashmap_cmp_fn cast
remote.c: drop hashmap_cmp_fn cast
patch-ids.c: drop hashmap_cmp_fn cast
convert/sub-process: drop cast to hashmap_cmp_fn
config.c: drop hashmap_cmp_fn cast
builtin/describe: drop hashmap_cmp_fn cast
builtin/difftool.c: drop hashmap_cmp_fn cast
attr.c: drop hashmap_cmp_fn cast

1  2 
builtin/describe.c
config.c
convert.c
remote.c
submodule-config.c
diff --combined builtin/describe.c
index 89ea1cdd60a215473ecb52ff2a4ce51932e1cf6d,f2f2edf935abf58a3a118c73d80b0f457a8f3986..9c13c6817bd784fb9c0e3eff781911577ef7ef8c
@@@ -55,10 -55,13 +55,13 @@@ static const char *prio_names[] = 
  };
  
  static int commit_name_cmp(const void *unused_cmp_data,
-                          const struct commit_name *cn1,
-                          const struct commit_name *cn2,
+                          const void *entry,
+                          const void *entry_or_key,
                           const void *peeled)
  {
+       const struct commit_name *cn1 = entry;
+       const struct commit_name *cn2 = entry_or_key;
        return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled);
  }
  
@@@ -145,7 -148,7 +148,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. */
@@@ -503,7 -506,7 +506,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, NULL, 0);
+       hashmap_init(&names, 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 e04500d13ba256e1c2a1a15e1b71718d0298900a,30ff700629a72f87977369bccefa5be382fd7257..9ec8c4488229a4f6a15550fd80496ef1b5bb85fe
+++ 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"
@@@ -16,7 -15,6 +16,7 @@@
  #include "string-list.h"
  #include "utf8.h"
  #include "dir.h"
 +#include "color.h"
  
  struct config_source {
        struct config_source *prev;
@@@ -74,6 -72,13 +74,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);
@@@ -239,7 -244,7 +239,7 @@@ again
        }
  
        ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
 -                       icase ? WM_CASEFOLD : 0, NULL);
 +                       icase ? WM_CASEFOLD : 0);
  
        if (!ret && !already_tried_absolute) {
                /*
@@@ -1351,9 -1356,6 +1351,9 @@@ int git_default_config(const char *var
        if (starts_with(var, "advice."))
                return git_default_advice_config(var, value);
  
 +      if (git_color_config(var, value, dummy) < 0)
 +              return -1;
 +
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
                pager_use_color = git_config_bool(var,value);
                return 0;
@@@ -1464,9 -1466,9 +1464,9 @@@ int git_config_from_mem(config_fn_t fn
        return do_config_from(&top, fn, data);
  }
  
 -int git_config_from_blob_sha1(config_fn_t fn,
 +int git_config_from_blob_oid(config_fn_t fn,
                              const char *name,
 -                            const unsigned char *sha1,
 +                            const struct object_id *oid,
                              void *data)
  {
        enum object_type type;
        unsigned long size;
        int ret;
  
 -      buf = read_sha1_file(sha1, &type, &size);
 +      buf = read_sha1_file(oid->hash, &type, &size);
        if (!buf)
                return error("unable to load config blob object '%s'", name);
        if (type != OBJ_BLOB) {
@@@ -1492,11 -1494,11 +1492,11 @@@ static int git_config_from_blob_ref(con
                                    const char *name,
                                    void *data)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
 -      if (get_sha1(name, sha1) < 0)
 +      if (get_oid(name, &oid) < 0)
                return error("unable to resolve config blob '%s'", name);
 -      return git_config_from_blob_sha1(fn, name, sha1, data);
 +      return git_config_from_blob_oid(fn, name, &oid, data);
  }
  
  const char *git_etc_gitconfig(void)
@@@ -1602,6 -1604,31 +1602,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;
@@@ -1655,6 -1682,14 +1655,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;
@@@ -1719,17 -1754,19 +1719,19 @@@ static int configset_add_value(struct c
  }
  
  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 *entry,
+                                 const void *entry_or_key,
                                  const void *unused_keydata)
  {
+       const struct config_set_element *e1 = entry;
+       const struct config_set_element *e2 = entry_or_key;
        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,
-                    NULL, 0);
+       hashmap_init(&cs->config_hash, config_set_element_cmp, NULL, 0);
        cs->hash_initialized = 1;
        cs->list.nr = 0;
        cs->list.alloc = 0;
@@@ -1867,194 -1904,87 +1869,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();
 -      ret = git_configset_get_string_const(&the_config_set, key, dest);
 +      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(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 convert.c
index 972bf8aec27594a94ad756431e551c2f30796c4d,04966c723c052d60c8f2954cfa1d7bab218e5b49..dbdbb24e4d37e1f4d4db8e479d48125c90a071a4
+++ b/convert.c
@@@ -501,7 -501,6 +501,7 @@@ static int apply_single_file_filter(con
  
  #define CAP_CLEAN    (1u<<0)
  #define CAP_SMUDGE   (1u<<1)
 +#define CAP_DELAY    (1u<<2)
  
  struct cmd2process {
        struct subprocess_entry subprocess; /* must be the first member! */
@@@ -513,7 -512,7 +513,7 @@@ static struct hashmap subprocess_map
  
  static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
  {
 -      int err;
 +      int err, i;
        struct cmd2process *entry = (struct cmd2process *)subprocess;
        struct string_list cap_list = STRING_LIST_INIT_NODUP;
        char *cap_buf;
        struct child_process *process = &subprocess->process;
        const char *cmd = subprocess->cmd;
  
 +      static const struct {
 +              const char *name;
 +              unsigned int cap;
 +      } known_caps[] = {
 +              { "clean",  CAP_CLEAN  },
 +              { "smudge", CAP_SMUDGE },
 +              { "delay",  CAP_DELAY  },
 +      };
 +
        sigchain_push(SIGPIPE, SIG_IGN);
  
        err = packet_writel(process->in, "git-filter-client", "version=2", NULL);
        if (err)
                goto done;
  
 -      err = packet_writel(process->in, "capability=clean", "capability=smudge", NULL);
 +      for (i = 0; i < ARRAY_SIZE(known_caps); ++i) {
 +              err = packet_write_fmt_gently(
 +                      process->in, "capability=%s\n", known_caps[i].name);
 +              if (err)
 +                      goto done;
 +      }
 +      err = packet_flush_gently(process->in);
 +      if (err)
 +              goto done;
  
        for (;;) {
                cap_buf = packet_read_line(process->out, NULL);
                        continue;
  
                cap_name = cap_list.items[1].string;
 -              if (!strcmp(cap_name, "clean")) {
 -                      entry->supported_capabilities |= CAP_CLEAN;
 -              } else if (!strcmp(cap_name, "smudge")) {
 -                      entry->supported_capabilities |= CAP_SMUDGE;
 -              } else {
 -                      warning(
 -                              "external filter '%s' requested unsupported filter capability '%s'",
 -                              cmd, cap_name
 -                      );
 -              }
 +              i = ARRAY_SIZE(known_caps) - 1;
 +              while (i >= 0 && strcmp(cap_name, known_caps[i].name))
 +                      i--;
 +
 +              if (i >= 0)
 +                      entry->supported_capabilities |= known_caps[i].cap;
 +              else
 +                      warning("external filter '%s' requested unsupported filter capability '%s'",
 +                      cmd, cap_name);
  
                string_list_clear(&cap_list, 0);
        }
@@@ -587,36 -570,11 +587,36 @@@ done
        return err;
  }
  
 +static void handle_filter_error(const struct strbuf *filter_status,
 +                              struct cmd2process *entry,
 +                              const unsigned int wanted_capability) {
 +      if (!strcmp(filter_status->buf, "error"))
 +              ; /* The filter signaled a problem with the file. */
 +      else if (!strcmp(filter_status->buf, "abort") && wanted_capability) {
 +              /*
 +               * The filter signaled a permanent problem. Don't try to filter
 +               * files with the same command for the lifetime of the current
 +               * Git process.
 +               */
 +               entry->supported_capabilities &= ~wanted_capability;
 +      } else {
 +              /*
 +               * Something went wrong with the protocol filter.
 +               * Force shutdown and restart if another blob requires filtering.
 +               */
 +              error("external filter '%s' failed", entry->subprocess.cmd);
 +              subprocess_stop(&subprocess_map, &entry->subprocess);
 +              free(entry);
 +      }
 +}
 +
  static int apply_multi_file_filter(const char *path, const char *src, size_t len,
                                   int fd, struct strbuf *dst, const char *cmd,
 -                                 const unsigned int wanted_capability)
 +                                 const unsigned int wanted_capability,
 +                                 struct delayed_checkout *dco)
  {
        int err;
 +      int can_delay = 0;
        struct cmd2process *entry;
        struct child_process *process;
        struct strbuf nbuf = STRBUF_INIT;
  
        if (!subprocess_map_initialized) {
                subprocess_map_initialized = 1;
-               hashmap_init(&subprocess_map, (hashmap_cmp_fn) cmd2process_cmp,
-                            NULL, 0);
+               hashmap_init(&subprocess_map, cmd2process_cmp, NULL, 0);
                entry = NULL;
        } else {
                entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd);
        }
        process = &entry->subprocess.process;
  
 -      if (!(wanted_capability & entry->supported_capabilities))
 +      if (!(entry->supported_capabilities & wanted_capability))
                return 0;
  
 -      if (CAP_CLEAN & wanted_capability)
 +      if (wanted_capability & CAP_CLEAN)
                filter_type = "clean";
 -      else if (CAP_SMUDGE & wanted_capability)
 +      else if (wanted_capability & CAP_SMUDGE)
                filter_type = "smudge";
        else
                die("unexpected filter type");
        if (err)
                goto done;
  
 +      if ((entry->supported_capabilities & CAP_DELAY) &&
 +          dco && dco->state == CE_CAN_DELAY) {
 +              can_delay = 1;
 +              err = packet_write_fmt_gently(process->in, "can-delay=1\n");
 +              if (err)
 +                      goto done;
 +      }
 +
        err = packet_flush_gently(process->in);
        if (err)
                goto done;
        if (err)
                goto done;
  
 -      err = strcmp(filter_status.buf, "success");
 +      if (can_delay && !strcmp(filter_status.buf, "delayed")) {
 +              string_list_insert(&dco->filters, cmd);
 +              string_list_insert(&dco->paths, path);
 +      } else {
 +              /* The filter got the blob and wants to send us a response. */
 +              err = strcmp(filter_status.buf, "success");
 +              if (err)
 +                      goto done;
 +
 +              err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
 +              if (err)
 +                      goto done;
 +
 +              err = subprocess_read_status(process->out, &filter_status);
 +              if (err)
 +                      goto done;
 +
 +              err = strcmp(filter_status.buf, "success");
 +      }
 +
 +done:
 +      sigchain_pop(SIGPIPE);
 +
 +      if (err)
 +              handle_filter_error(&filter_status, entry, wanted_capability);
 +      else
 +              strbuf_swap(dst, &nbuf);
 +      strbuf_release(&nbuf);
 +      return !err;
 +}
 +
 +
 +int async_query_available_blobs(const char *cmd, struct string_list *available_paths)
 +{
 +      int err;
 +      char *line;
 +      struct cmd2process *entry;
 +      struct child_process *process;
 +      struct strbuf filter_status = STRBUF_INIT;
 +
 +      assert(subprocess_map_initialized);
 +      entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd);
 +      if (!entry) {
 +              error("external filter '%s' is not available anymore although "
 +                    "not all paths have been filtered", cmd);
 +              return 0;
 +      }
 +      process = &entry->subprocess.process;
 +      sigchain_push(SIGPIPE, SIG_IGN);
 +
 +      err = packet_write_fmt_gently(
 +              process->in, "command=list_available_blobs\n");
        if (err)
                goto done;
  
 -      err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
 +      err = packet_flush_gently(process->in);
        if (err)
                goto done;
  
 +      while ((line = packet_read_line(process->out, NULL))) {
 +              const char *path;
 +              if (skip_prefix(line, "pathname=", &path))
 +                      string_list_insert(available_paths, xstrdup(path));
 +              else
 +                      ; /* ignore unknown keys */
 +      }
 +
        err = subprocess_read_status(process->out, &filter_status);
        if (err)
                goto done;
  done:
        sigchain_pop(SIGPIPE);
  
 -      if (err) {
 -              if (!strcmp(filter_status.buf, "error")) {
 -                      /* The filter signaled a problem with the file. */
 -              } else if (!strcmp(filter_status.buf, "abort")) {
 -                      /*
 -                       * The filter signaled a permanent problem. Don't try to filter
 -                       * files with the same command for the lifetime of the current
 -                       * Git process.
 -                       */
 -                       entry->supported_capabilities &= ~wanted_capability;
 -              } else {
 -                      /*
 -                       * Something went wrong with the protocol filter.
 -                       * Force shutdown and restart if another blob requires filtering.
 -                       */
 -                      error("external filter '%s' failed", cmd);
 -                      subprocess_stop(&subprocess_map, &entry->subprocess);
 -                      free(entry);
 -              }
 -      } else {
 -              strbuf_swap(dst, &nbuf);
 -      }
 -      strbuf_release(&nbuf);
 +      if (err)
 +              handle_filter_error(&filter_status, entry, 0);
        return !err;
  }
  
@@@ -787,8 -698,7 +786,8 @@@ static struct convert_driver 
  
  static int apply_filter(const char *path, const char *src, size_t len,
                        int fd, struct strbuf *dst, struct convert_driver *drv,
 -                      const unsigned int wanted_capability)
 +                      const unsigned int wanted_capability,
 +                      struct delayed_checkout *dco)
  {
        const char *cmd = NULL;
  
        if (!dst)
                return 1;
  
 -      if ((CAP_CLEAN & wanted_capability) && !drv->process && drv->clean)
 +      if ((wanted_capability & CAP_CLEAN) && !drv->process && drv->clean)
                cmd = drv->clean;
 -      else if ((CAP_SMUDGE & wanted_capability) && !drv->process && drv->smudge)
 +      else if ((wanted_capability & CAP_SMUDGE) && !drv->process && drv->smudge)
                cmd = drv->smudge;
  
        if (cmd && *cmd)
                return apply_single_file_filter(path, src, len, fd, dst, cmd);
        else if (drv->process && *drv->process)
 -              return apply_multi_file_filter(path, src, len, fd, dst, drv->process, wanted_capability);
 +              return apply_multi_file_filter(path, src, len, fd, dst,
 +                      drv->process, wanted_capability, dco);
  
        return 0;
  }
@@@ -1148,7 -1057,7 +1147,7 @@@ int would_convert_to_git_filter_fd(cons
        if (!ca.drv->required)
                return 0;
  
 -      return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN);
 +      return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN, NULL);
  }
  
  const char *get_convert_attr_ascii(const char *path)
@@@ -1186,7 -1095,7 +1185,7 @@@ int convert_to_git(const struct index_s
  
        convert_attrs(&ca, path);
  
 -      ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN);
 +      ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN, NULL);
        if (!ret && ca.drv && ca.drv->required)
                die("%s: clean filter '%s' failed", path, ca.drv->name);
  
@@@ -1212,7 -1121,7 +1211,7 @@@ void convert_to_git_filter_fd(const str
        assert(ca.drv);
        assert(ca.drv->clean || ca.drv->process);
  
 -      if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN))
 +      if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL))
                die("%s: clean filter '%s' failed", path, ca.drv->name);
  
        crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
  
  static int convert_to_working_tree_internal(const char *path, const char *src,
                                            size_t len, struct strbuf *dst,
 -                                          int normalizing)
 +                                          int normalizing, struct delayed_checkout *dco)
  {
        int ret = 0, ret_filter = 0;
        struct conv_attrs ca;
                }
        }
  
 -      ret_filter = apply_filter(path, src, len, -1, dst, ca.drv, CAP_SMUDGE);
 +      ret_filter = apply_filter(
 +              path, src, len, -1, dst, ca.drv, CAP_SMUDGE, dco);
        if (!ret_filter && ca.drv && ca.drv->required)
                die("%s: smudge filter %s failed", path, ca.drv->name);
  
        return ret | ret_filter;
  }
  
 +int async_convert_to_working_tree(const char *path, const char *src,
 +                                size_t len, struct strbuf *dst,
 +                                void *dco)
 +{
 +      return convert_to_working_tree_internal(path, src, len, dst, 0, dco);
 +}
 +
  int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
  {
 -      return convert_to_working_tree_internal(path, src, len, dst, 0);
 +      return convert_to_working_tree_internal(path, src, len, dst, 0, NULL);
  }
  
  int renormalize_buffer(const struct index_state *istate, const char *path,
                       const char *src, size_t len, struct strbuf *dst)
  {
 -      int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
 +      int ret = convert_to_working_tree_internal(path, src, len, dst, 1, NULL);
        if (ret) {
                src = dst->buf;
                len = dst->len;
diff --combined remote.c
index 6a1be31bb168c86721364f19d39f227a639e48b8,3efa35855824032d582e21f95d7a397e29dffb8f..43c317e4e9f6e67aa3e1d0ae245d097bdba664db
+++ b/remote.c
@@@ -134,10 -134,14 +134,14 @@@ struct remotes_hash_key 
  };
  
  static int remotes_hash_cmp(const void *unused_cmp_data,
-                           const struct remote *a,
-                           const struct remote *b,
-                           const struct remotes_hash_key *key)
+                           const void *entry,
+                           const void *entry_or_key,
+                           const void *keydata)
  {
+       const struct remote *a = entry;
+       const struct remote *b = entry_or_key;
+       const struct remotes_hash_key *key = keydata;
        if (key)
                return strncmp(a->name, key->str, key->len) || a->name[key->len];
        else
  static inline void init_remotes_hash(void)
  {
        if (!remotes_hash.cmpfn)
-               hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, NULL, 0);
+               hashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);
  }
  
  static struct remote *make_remote(const char *name, int len)
@@@ -1081,7 -1085,7 +1085,7 @@@ static int try_explicit_object_name(con
                return 0;
        }
  
 -      if (get_sha1(name, oid.hash))
 +      if (get_oid(name, &oid))
                return -1;
  
        if (match) {
@@@ -2297,8 -2301,8 +2301,8 @@@ static int parse_push_cas_option(struc
        if (!*colon)
                entry->use_tracking = 1;
        else if (!colon[1])
 -              hashclr(entry->expect);
 -      else if (get_sha1(colon + 1, entry->expect))
 +              oidclr(&entry->expect);
 +      else if (get_oid(colon + 1, &entry->expect))
                return error("cannot parse expected object name '%s'", colon + 1);
        return 0;
  }
@@@ -2345,7 -2349,7 +2349,7 @@@ static void apply_cas(struct push_cas_o
                        continue;
                ref->expect_old_sha1 = 1;
                if (!entry->use_tracking)
 -                      hashcpy(ref->old_oid_expect.hash, cas->entry[i].expect);
 +                      oidcpy(&ref->old_oid_expect, &entry->expect);
                else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
                        oidclr(&ref->old_oid_expect);
                return;
diff --combined submodule-config.c
index d48403f25eef4ea549591fdc0633b67ec3401af9,edc8dd04b6041df78c2793d98b68f7ef0cad92c6..bede338c8539f18081168bacd0b562a927e9532c
@@@ -1,10 -1,8 +1,10 @@@
  #include "cache.h"
 +#include "repository.h"
  #include "config.h"
  #include "submodule-config.h"
  #include "submodule.h"
  #include "strbuf.h"
 +#include "parse-options.h"
  
  /*
   * submodule cache lookup structure
@@@ -17,7 -15,6 +17,7 @@@
  struct submodule_cache {
        struct hashmap for_path;
        struct hashmap for_name;
 +      unsigned initialized:1;
  };
  
  /*
@@@ -34,34 -31,37 +34,40 @@@ enum lookup_type 
        lookup_path
  };
  
 -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 *entry,
+                          const void *entry_or_key,
                           const void *unused_keydata)
  {
+       const struct submodule_entry *a = entry;
+       const struct submodule_entry *b = entry_or_key;
        return strcmp(a->config->path, b->config->path) ||
               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  }
  
  static int config_name_cmp(const void *unused_cmp_data,
-                          const struct submodule_entry *a,
-                          const struct submodule_entry *b,
+                          const void *entry,
+                          const void *entry_or_key,
                           const void *unused_keydata)
  {
+       const struct submodule_entry *a = entry;
+       const struct submodule_entry *b = entry_or_key;
        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, NULL, 0);
-       hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, NULL, 0);
+       hashmap_init(&cache->for_path, config_path_cmp, NULL, 0);
+       hashmap_init(&cache->for_name, 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,
@@@ -253,27 -243,6 +259,27 @@@ int parse_fetch_recurse_submodules_arg(
        return parse_fetch_recurse(opt, arg, 1);
  }
  
 +int option_fetch_parse_recurse_submodules(const struct option *opt,
 +                                        const char *arg, int unset)
 +{
 +      int *v;
 +
 +      if (!opt->value)
 +              return -1;
 +
 +      v = opt->value;
 +
 +      if (unset) {
 +              *v = RECURSE_SUBMODULES_OFF;
 +      } else {
 +              if (arg)
 +                      *v = parse_fetch_recurse_submodules_arg(opt->long_name, arg);
 +              else
 +                      *v = RECURSE_SUBMODULES_ON;
 +      }
 +      return 0;
 +}
 +
  static int parse_update_recurse(const char *opt, const char *arg,
                                int die_on_error)
  {
@@@ -441,19 -410,19 +447,19 @@@ static int parse_config(const char *var
        return ret;
  }
  
 -int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
 -                                    unsigned char *gitmodules_sha1,
 +int gitmodule_oid_from_commit(const struct object_id *treeish_name,
 +                                    struct object_id *gitmodules_oid,
                                      struct strbuf *rev)
  {
        int ret = 0;
  
 -      if (is_null_sha1(treeish_name)) {
 -              hashclr(gitmodules_sha1);
 +      if (is_null_oid(treeish_name)) {
 +              oidclr(gitmodules_oid);
                return 1;
        }
  
 -      strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(treeish_name));
 -      if (get_sha1(rev->buf, gitmodules_sha1) >= 0)
 +      strbuf_addf(rev, "%s:.gitmodules", oid_to_hex(treeish_name));
 +      if (get_oid(rev->buf, gitmodules_oid) >= 0)
                ret = 1;
  
        return ret;
   * revisions.
   */
  static const struct submodule *config_from(struct submodule_cache *cache,
 -              const unsigned char *treeish_name, const char *key,
 +              const struct object_id *treeish_name, const char *key,
                enum lookup_type lookup_type)
  {
        struct strbuf rev = STRBUF_INIT;
        unsigned long config_size;
        char *config = NULL;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        enum object_type type;
        const struct submodule *submodule = NULL;
        struct parse_config_parameter parameter;
                return entry->config;
        }
  
 -      if (!gitmodule_sha1_from_commit(treeish_name, sha1, &rev))
 +      if (!gitmodule_oid_from_commit(treeish_name, &oid, &rev))
                goto out;
  
        switch (lookup_type) {
        case lookup_name:
 -              submodule = cache_lookup_name(cache, sha1, key);
 +              submodule = cache_lookup_name(cache, oid.hash, key);
                break;
        case lookup_path:
 -              submodule = cache_lookup_path(cache, sha1, key);
 +              submodule = cache_lookup_path(cache, oid.hash, key);
                break;
        }
        if (submodule)
                goto out;
  
 -      config = read_sha1_file(sha1, &type, &config_size);
 +      config = read_sha1_file(oid.hash, &type, &config_size);
        if (!config || type != OBJ_BLOB)
                goto out;
  
        /* fill the submodule config into the cache */
        parameter.cache = cache;
 -      parameter.treeish_name = treeish_name;
 -      parameter.gitmodules_sha1 = sha1;
 +      parameter.treeish_name = treeish_name->hash;
 +      parameter.gitmodules_sha1 = oid.hash;
        parameter.overwrite = 0;
        git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
                        config, config_size, &parameter);
  
        switch (lookup_type) {
        case lookup_name:
 -              return cache_lookup_name(cache, sha1, key);
 +              return cache_lookup_name(cache, oid.hash, key);
        case lookup_path:
 -              return cache_lookup_path(cache, sha1, key);
 +              return cache_lookup_path(cache, oid.hash, key);
        default:
                return NULL;
        }
@@@ -533,62 -502,43 +539,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);
  }
  
 -const struct submodule *submodule_from_name(const unsigned char *treeish_name,
 +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 struct object_id *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 struct submodule *submodule_from_path(const struct object_id *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 struct object_id *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);
  }