Merge branch 'jk/loose-object-cache'
authorJunio C Hamano <gitster@pobox.com>
Fri, 4 Jan 2019 21:33:32 +0000 (13:33 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 4 Jan 2019 21:33:32 +0000 (13:33 -0800)
Code clean-up with optimization for the codepath that checks
(non-)existence of loose objects.

* jk/loose-object-cache:
odb_load_loose_cache: fix strbuf leak
fetch-pack: drop custom loose object cache
sha1-file: use loose object cache for quick existence check
object-store: provide helpers for loose_objects_cache
sha1-file: use an object_directory for the main object dir
handle alternates paths the same as the main object dir
sha1_file_name(): overwrite buffer instead of appending
rename "alternate_object_database" to "object_directory"
submodule--helper: prefer strip_suffix() to ends_with()
fsck: do not reuse child_process structs

1  2 
builtin/fsck.c
builtin/grep.c
builtin/submodule--helper.c
environment.c
fetch-pack.c
http.c
midx.c
packfile.c
path.c
sha1-file.c
diff --combined builtin/fsck.c
index a281d7cd123b830bf89c2e21ccbcb10f55f40ab9,15338bd178c69f3c5b084395c39bbbd46757464e..c03a9c42db90a5e85b21bec13247fb813b1a900d
@@@ -19,7 -19,6 +19,7 @@@
  #include "packfile.h"
  #include "object-store.h"
  #include "run-command.h"
 +#include "worktree.h"
  
  #define REACHABLE 0x0001
  #define SEEN      0x0002
@@@ -37,6 -36,8 +37,6 @@@ static int check_strict
  static int keep_cache_objects;
  static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
  static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
 -static struct object_id head_oid;
 -static const char *head_points_at;
  static int errors_found;
  static int write_lost_and_found;
  static int verbose;
@@@ -51,24 -52,16 +51,24 @@@ static int name_objects
  
  static const char *describe_object(struct object *obj)
  {
 -      static struct strbuf buf = STRBUF_INIT;
 -      char *name = name_objects ?
 -              lookup_decoration(fsck_walk_options.object_names, obj) : NULL;
 +      static struct strbuf bufs[] = {
 +              STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
 +      };
 +      static int b = 0;
 +      struct strbuf *buf;
 +      char *name = NULL;
  
 -      strbuf_reset(&buf);
 -      strbuf_addstr(&buf, oid_to_hex(&obj->oid));
 +      if (name_objects)
 +              name = lookup_decoration(fsck_walk_options.object_names, obj);
 +
 +      buf = bufs + b;
 +      b = (b + 1) % ARRAY_SIZE(bufs);
 +      strbuf_reset(buf);
 +      strbuf_addstr(buf, oid_to_hex(&obj->oid));
        if (name)
 -              strbuf_addf(&buf, " (%s)", name);
 +              strbuf_addf(buf, " (%s)", name);
  
 -      return buf.buf;
 +      return buf->buf;
  }
  
  static const char *printable_type(struct object *obj)
@@@ -84,7 -77,7 +84,7 @@@
  
        ret = type_name(obj->type);
        if (!ret)
 -              ret = "unknown";
 +              ret = _("unknown");
  
        return ret;
  }
@@@ -112,32 -105,25 +112,32 @@@ static int fsck_config(const char *var
        return git_default_config(var, value, cb);
  }
  
 -static void objreport(struct object *obj, const char *msg_type,
 -                      const char *err)
 -{
 -      fprintf(stderr, "%s in %s %s: %s\n",
 -              msg_type, printable_type(obj), describe_object(obj), err);
 -}
 -
  static int objerror(struct object *obj, const char *err)
  {
        errors_found |= ERROR_OBJECT;
 -      objreport(obj, "error", err);
 +      /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 +      fprintf_ln(stderr, _("error in %s %s: %s"),
 +                 printable_type(obj), describe_object(obj), err);
        return -1;
  }
  
  static int fsck_error_func(struct fsck_options *o,
        struct object *obj, int type, const char *message)
  {
 -      objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
 -      return (type == FSCK_WARN) ? 0 : 1;
 +      switch (type) {
 +      case FSCK_WARN:
 +              /* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
 +              fprintf_ln(stderr, _("warning in %s %s: %s"),
 +                         printable_type(obj), describe_object(obj), message);
 +              return 0;
 +      case FSCK_ERROR:
 +              /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 +              fprintf_ln(stderr, _("error in %s %s: %s"),
 +                         printable_type(obj), describe_object(obj), message);
 +              return 1;
 +      default:
 +              BUG("%d (FSCK_IGNORE?) should never trigger this callback", type);
 +      }
  }
  
  static struct object_array pending;
@@@ -153,18 -139,17 +153,18 @@@ static int mark_object(struct object *o
         */
        if (!obj) {
                /* ... these references to parent->fld are safe here */
 -              printf("broken link from %7s %s\n",
 -                         printable_type(parent), describe_object(parent));
 -              printf("broken link from %7s %s\n",
 -                         (type == OBJ_ANY ? "unknown" : type_name(type)), "unknown");
 +              printf_ln(_("broken link from %7s %s"),
 +                        printable_type(parent), describe_object(parent));
 +              printf_ln(_("broken link from %7s %s"),
 +                        (type == OBJ_ANY ? _("unknown") : type_name(type)),
 +                        _("unknown"));
                errors_found |= ERROR_REACHABLE;
                return 1;
        }
  
        if (type != OBJ_ANY && obj->type != type)
                /* ... and the reference to parent is safe here */
 -              objerror(parent, "wrong object type in link");
 +              objerror(parent, _("wrong object type in link"));
  
        if (obj->flags & REACHABLE)
                return 0;
  
        if (!(obj->flags & HAS_OBJ)) {
                if (parent && !has_object_file(&obj->oid)) {
 -                      printf("broken link from %7s %s\n",
 -                               printable_type(parent), describe_object(parent));
 -                      printf("              to %7s %s\n",
 -                               printable_type(obj), describe_object(obj));
 +                      printf_ln(_("broken link from %7s %s\n"
 +                                  "              to %7s %s"),
 +                                printable_type(parent),
 +                                describe_object(parent),
 +                                printable_type(obj),
 +                                describe_object(obj));
                        errors_found |= ERROR_REACHABLE;
                }
                return 1;
@@@ -249,8 -232,8 +249,8 @@@ static void check_reachable_object(stru
                        return;
                if (has_object_pack(&obj->oid))
                        return; /* it is in pack - forget about it */
 -              printf("missing %s %s\n", printable_type(obj),
 -                      describe_object(obj));
 +              printf_ln(_("missing %s %s"), printable_type(obj),
 +                        describe_object(obj));
                errors_found |= ERROR_REACHABLE;
                return;
        }
@@@ -275,8 -258,8 +275,8 @@@ static void check_unreachable_object(st
         * since this is something that is prunable.
         */
        if (show_unreachable) {
 -              printf("unreachable %s %s\n", printable_type(obj),
 -                      describe_object(obj));
 +              printf_ln(_("unreachable %s %s"), printable_type(obj),
 +                        describe_object(obj));
                return;
        }
  
         */
        if (!(obj->flags & USED)) {
                if (show_dangling)
 -                      printf("dangling %s %s\n", printable_type(obj),
 -                             describe_object(obj));
 +                      printf_ln(_("dangling %s %s"), printable_type(obj),
 +                                describe_object(obj));
                if (write_lost_and_found) {
                        char *filename = git_pathdup("lost-found/%s/%s",
                                obj->type == OBJ_COMMIT ? "commit" : "other",
                        FILE *f;
  
                        if (safe_create_leading_directories_const(filename)) {
 -                              error("Could not create lost-found");
 +                              error(_("could not create lost-found"));
                                free(filename);
                                return;
                        }
                        f = xfopen(filename, "w");
                        if (obj->type == OBJ_BLOB) {
                                if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
 -                                      die_errno("Could not write '%s'", filename);
 +                                      die_errno(_("could not write '%s'"), filename);
                        } else
                                fprintf(f, "%s\n", describe_object(obj));
                        if (fclose(f))
 -                              die_errno("Could not finish '%s'",
 +                              die_errno(_("could not finish '%s'"),
                                          filename);
                        free(filename);
                }
  static void check_object(struct object *obj)
  {
        if (verbose)
 -              fprintf(stderr, "Checking %s\n", describe_object(obj));
 +              fprintf_ln(stderr, _("Checking %s"), describe_object(obj));
  
        if (obj->flags & REACHABLE)
                check_reachable_object(obj);
@@@ -349,7 -332,7 +349,7 @@@ static void check_connectivity(void
        /* Look up all the requirements, warn about missing objects.. */
        max = get_max_object_index();
        if (verbose)
 -              fprintf(stderr, "Checking connectivity (%d objects)\n", max);
 +              fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
  
        for (i = 0; i < max; i++) {
                struct object *obj = get_indexed_object(i);
@@@ -368,11 -351,11 +368,11 @@@ static int fsck_obj(struct object *obj
        obj->flags |= SEEN;
  
        if (verbose)
 -              fprintf(stderr, "Checking %s %s\n",
 -                      printable_type(obj), describe_object(obj));
 +              fprintf_ln(stderr, _("Checking %s %s"),
 +                         printable_type(obj), describe_object(obj));
  
        if (fsck_walk(obj, NULL, &fsck_obj_options))
 -              objerror(obj, "broken links");
 +              objerror(obj, _("broken links"));
        err = fsck_object(obj, buffer, size, &fsck_obj_options);
        if (err)
                goto out;
                struct commit *commit = (struct commit *) obj;
  
                if (!commit->parents && show_root)
 -                      printf("root %s\n", describe_object(&commit->object));
 +                      printf_ln(_("root %s"),
 +                                describe_object(&commit->object));
        }
  
        if (obj->type == OBJ_TAG) {
                struct tag *tag = (struct tag *) obj;
  
                if (show_tags && tag->tagged) {
 -                      printf("tagged %s %s", printable_type(tag->tagged),
 -                              describe_object(tag->tagged));
 -                      printf(" (%s) in %s\n", tag->tag,
 -                              describe_object(&tag->object));
 +                      printf_ln(_("tagged %s %s (%s) in %s"),
 +                                printable_type(tag->tagged),
 +                                describe_object(tag->tagged),
 +                                tag->tag,
 +                                describe_object(&tag->object));
                }
        }
  
@@@ -417,8 -398,7 +417,8 @@@ static int fsck_obj_buffer(const struc
                                  eaten);
        if (!obj) {
                errors_found |= ERROR_OBJECT;
 -              return error("%s: object corrupt or missing", oid_to_hex(oid));
 +              return error(_("%s: object corrupt or missing"),
 +                           oid_to_hex(oid));
        }
        obj->flags &= ~(REACHABLE | SEEN);
        obj->flags |= HAS_OBJ;
@@@ -442,8 -422,7 +442,8 @@@ static void fsck_handle_reflog_oid(cons
                        obj->flags |= USED;
                        mark_object_reachable(obj);
                } else if (!is_promisor_object(oid)) {
 -                      error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
 +                      error(_("%s: invalid reflog entry %s"),
 +                            refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
        }
@@@ -456,8 -435,8 +456,8 @@@ static int fsck_handle_reflog_ent(struc
        const char *refname = cb_data;
  
        if (verbose)
 -              fprintf(stderr, "Checking reflog %s->%s\n",
 -                      oid_to_hex(ooid), oid_to_hex(noid));
 +              fprintf_ln(stderr, _("Checking reflog %s->%s"),
 +                         oid_to_hex(ooid), oid_to_hex(noid));
  
        fsck_handle_reflog_oid(refname, ooid, 0);
        fsck_handle_reflog_oid(refname, noid, timestamp);
  static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
                              int flag, void *cb_data)
  {
 -      for_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname);
 +      struct strbuf refname = STRBUF_INIT;
 +
 +      strbuf_worktree_ref(cb_data, &refname, logname);
 +      for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);
 +      strbuf_release(&refname);
        return 0;
  }
  
@@@ -490,14 -465,13 +490,14 @@@ static int fsck_handle_ref(const char *
                         default_refs++;
                         return 0;
                }
 -              error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
 +              error(_("%s: invalid sha1 pointer %s"),
 +                    refname, oid_to_hex(oid));
                errors_found |= ERROR_REACHABLE;
                /* We'll continue with the rest despite the error.. */
                return 0;
        }
        if (obj->type != OBJ_COMMIT && is_branch(refname)) {
 -              error("%s: not a commit", refname);
 +              error(_("%s: not a commit"), refname);
                errors_found |= ERROR_REFS;
        }
        default_refs++;
        return 0;
  }
  
 +static int fsck_head_link(const char *head_ref_name,
 +                        const char **head_points_at,
 +                        struct object_id *head_oid);
 +
  static void get_default_heads(void)
  {
 -      if (head_points_at && !is_null_oid(&head_oid))
 -              fsck_handle_ref("HEAD", &head_oid, 0, NULL);
 +      struct worktree **worktrees, **p;
 +      const char *head_points_at;
 +      struct object_id head_oid;
 +
        for_each_rawref(fsck_handle_ref, NULL);
 -      if (include_reflogs)
 -              for_each_reflog(fsck_handle_reflog, NULL);
 +
 +      worktrees = get_worktrees(0);
 +      for (p = worktrees; *p; p++) {
 +              struct worktree *wt = *p;
 +              struct strbuf ref = STRBUF_INIT;
 +
 +              strbuf_worktree_ref(wt, &ref, "HEAD");
 +              fsck_head_link(ref.buf, &head_points_at, &head_oid);
 +              if (head_points_at && !is_null_oid(&head_oid))
 +                      fsck_handle_ref(ref.buf, &head_oid, 0, NULL);
 +              strbuf_release(&ref);
 +
 +              if (include_reflogs)
 +                      refs_for_each_reflog(get_worktree_ref_store(wt),
 +                                           fsck_handle_reflog, wt);
 +      }
 +      free_worktrees(worktrees);
  
        /*
         * Not having any default heads isn't really fatal, but
         * "show_unreachable" flag.
         */
        if (!default_refs) {
 -              fprintf(stderr, "notice: No default references\n");
 +              fprintf_ln(stderr, _("notice: No default references"));
                show_unreachable = 0;
        }
  }
@@@ -567,7 -520,7 +567,7 @@@ static int fsck_loose(const struct obje
  
        if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
                errors_found |= ERROR_OBJECT;
 -              error("%s: object corrupt or missing: %s",
 +              error(_("%s: object corrupt or missing: %s"),
                      oid_to_hex(oid), path);
                return 0; /* keep checking other objects */
        }
  
        if (!obj) {
                errors_found |= ERROR_OBJECT;
 -              error("%s: object could not be parsed: %s",
 +              error(_("%s: object could not be parsed: %s"),
                      oid_to_hex(oid), path);
                if (!eaten)
                        free(contents);
  static int fsck_cruft(const char *basename, const char *path, void *data)
  {
        if (!starts_with(basename, "tmp_obj_"))
 -              fprintf(stderr, "bad sha1 file: %s\n", path);
 +              fprintf_ln(stderr, _("bad sha1 file: %s"), path);
        return 0;
  }
  
@@@ -615,7 -568,7 +615,7 @@@ static void fsck_object_dir(const char 
        struct progress *progress = NULL;
  
        if (verbose)
 -              fprintf(stderr, "Checking object directory\n");
 +              fprintf_ln(stderr, _("Checking object directory"));
  
        if (show_progress)
                progress = start_progress(_("Checking object directories"), 256);
        stop_progress(&progress);
  }
  
 -static int fsck_head_link(void)
 +static int fsck_head_link(const char *head_ref_name,
 +                        const char **head_points_at,
 +                        struct object_id *head_oid)
  {
        int null_is_error = 0;
  
        if (verbose)
 -              fprintf(stderr, "Checking HEAD link\n");
 +              fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
  
 -      head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL);
 -      if (!head_points_at) {
 +      *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
 +      if (!*head_points_at) {
                errors_found |= ERROR_REFS;
 -              return error("Invalid HEAD");
 +              return error(_("invalid %s"), head_ref_name);
        }
 -      if (!strcmp(head_points_at, "HEAD"))
 +      if (!strcmp(*head_points_at, head_ref_name))
                /* detached HEAD */
                null_is_error = 1;
 -      else if (!starts_with(head_points_at, "refs/heads/")) {
 +      else if (!starts_with(*head_points_at, "refs/heads/")) {
                errors_found |= ERROR_REFS;
 -              return error("HEAD points to something strange (%s)",
 -                           head_points_at);
 +              return error(_("%s points to something strange (%s)"),
 +                           head_ref_name, *head_points_at);
        }
 -      if (is_null_oid(&head_oid)) {
 +      if (is_null_oid(head_oid)) {
                if (null_is_error) {
                        errors_found |= ERROR_REFS;
 -                      return error("HEAD: detached HEAD points at nothing");
 +                      return error(_("%s: detached HEAD points at nothing"),
 +                                   head_ref_name);
                }
 -              fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
 -                      head_points_at + 11);
 +              fprintf_ln(stderr,
 +                         _("notice: %s points to an unborn branch (%s)"),
 +                         head_ref_name, *head_points_at + 11);
        }
        return 0;
  }
@@@ -667,12 -616,12 +667,12 @@@ static int fsck_cache_tree(struct cache
        int err = 0;
  
        if (verbose)
 -              fprintf(stderr, "Checking cache tree\n");
 +              fprintf_ln(stderr, _("Checking cache tree"));
  
        if (0 <= it->entry_count) {
                struct object *obj = parse_object(the_repository, &it->oid);
                if (!obj) {
 -                      error("%s: invalid sha1 pointer in cache-tree",
 +                      error(_("%s: invalid sha1 pointer in cache-tree"),
                              oid_to_hex(&it->oid));
                        errors_found |= ERROR_REFS;
                        return 1;
                                obj, xstrdup(":"));
                mark_object_reachable(obj);
                if (obj->type != OBJ_TREE)
 -                      err |= objerror(obj, "non-tree in cache-tree");
 +                      err |= objerror(obj, _("non-tree in cache-tree"));
        }
        for (i = 0; i < it->subtree_nr; i++)
                err |= fsck_cache_tree(it->down[i]->cache_tree);
@@@ -739,7 -688,7 +739,7 @@@ static struct option fsck_opts[] = 
  int cmd_fsck(int argc, const char **argv, const char *prefix)
  {
        int i;
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
  
        /* fsck knows how to handle missing promisor objects */
        fetch_if_missing = 0;
  
        git_config(fsck_config, NULL);
  
 -      fsck_head_link();
        if (connectivity_only) {
                for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
                for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
        } else {
-               struct alternate_object_database *alt_odb_list;
-               fsck_object_dir(get_object_directory());
                prepare_alt_odb(the_repository);
-               alt_odb_list = the_repository->objects->alt_odb_list;
-               for (alt = alt_odb_list; alt; alt = alt->next)
-                       fsck_object_dir(alt->path);
+               for (odb = the_repository->objects->odb; odb; odb = odb->next)
+                       fsck_object_dir(odb->path);
  
                if (check_full) {
                        struct packed_git *p;
                        if (!obj || !(obj->flags & HAS_OBJ)) {
                                if (is_promisor_object(&oid))
                                        continue;
 -                              error("%s: object missing", oid_to_hex(&oid));
 +                              error(_("%s: object missing"), oid_to_hex(&oid));
                                errors_found |= ERROR_OBJECT;
                                continue;
                        }
                        mark_object_reachable(obj);
                        continue;
                }
 -              error("invalid parameter: expected sha1, got '%s'", arg);
 +              error(_("invalid parameter: expected sha1, got '%s'"), arg);
                errors_found |= ERROR_OBJECT;
        }
  
                struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
                const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
  
-               commit_graph_verify.argv = verify_argv;
-               commit_graph_verify.git_cmd = 1;
-               if (run_command(&commit_graph_verify))
-                       errors_found |= ERROR_COMMIT_GRAPH;
                prepare_alt_odb(the_repository);
-               for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
+               for (odb = the_repository->objects->odb; odb; odb = odb->next) {
+                       child_process_init(&commit_graph_verify);
+                       commit_graph_verify.argv = verify_argv;
+                       commit_graph_verify.git_cmd = 1;
                        verify_argv[2] = "--object-dir";
-                       verify_argv[3] = alt->path;
+                       verify_argv[3] = odb->path;
                        if (run_command(&commit_graph_verify))
                                errors_found |= ERROR_COMMIT_GRAPH;
                }
                struct child_process midx_verify = CHILD_PROCESS_INIT;
                const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
  
-               midx_verify.argv = midx_argv;
-               midx_verify.git_cmd = 1;
-               if (run_command(&midx_verify))
-                       errors_found |= ERROR_COMMIT_GRAPH;
                prepare_alt_odb(the_repository);
-               for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
+               for (odb = the_repository->objects->odb; odb; odb = odb->next) {
+                       child_process_init(&midx_verify);
+                       midx_verify.argv = midx_argv;
+                       midx_verify.git_cmd = 1;
                        midx_argv[2] = "--object-dir";
-                       midx_argv[3] = alt->path;
+                       midx_argv[3] = odb->path;
                        if (run_command(&midx_verify))
                                errors_found |= ERROR_COMMIT_GRAPH;
                }
diff --combined builtin/grep.c
index 71df52a333cbc756b057e93bf3d2affca8157860,714c8d91bab9ea32c4646b5e4366805cdb8c1fab..bad9c0a3d5134a69d9388e169793b03c9952d9bf
@@@ -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.
         */
-       add_to_alternates_memory(submodule.objects->objectdir);
 -      grep_read_lock();
+       add_to_alternates_memory(submodule.objects->odb->path);
        grep_read_unlock();
  
        if (oid) {
@@@ -708,14 -712,11 +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;
  }
@@@ -773,7 -766,6 +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;
  }
@@@ -1046,35 -1038,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);
index d38113a31aeb3838190b7339475c7454e45f3a89,3ae451bc461540e5f24df86c6b0b271231f4f554..b45514be317eafb765a8943241b5d880f65e3117
@@@ -1265,19 -1265,20 +1265,20 @@@ struct submodule_alternate_setup 
        SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
  
  static int add_possible_reference_from_superproject(
-               struct alternate_object_database *alt, void *sas_cb)
+               struct object_directory *odb, void *sas_cb)
  {
        struct submodule_alternate_setup *sas = sas_cb;
+       size_t len;
  
        /*
         * If the alternate object store is another repository, try the
         * standard layout with .git/(modules/<name>)+/objects
         */
-       if (ends_with(alt->path, "/objects")) {
+       if (strip_suffix(odb->path, "/objects", &len)) {
                char *sm_alternate;
                struct strbuf sb = STRBUF_INIT;
                struct strbuf err = STRBUF_INIT;
-               strbuf_add(&sb, alt->path, strlen(alt->path) - strlen("objects"));
+               strbuf_add(&sb, odb->path, len);
  
                /*
                 * We need to end the new path with '/' to mark it as a dir,
                 * as the last part of a missing submodule reference would
                 * be taken as a file name.
                 */
-               strbuf_addf(&sb, "modules/%s/", sas->submodule_name);
+               strbuf_addf(&sb, "/modules/%s/", sas->submodule_name);
  
                sm_alternate = compute_alternate_path(sb.buf, &err);
                if (sm_alternate) {
@@@ -2141,45 -2142,6 +2142,45 @@@ static int check_name(int argc, const c
        return 0;
  }
  
 +static int module_config(int argc, const char **argv, const char *prefix)
 +{
 +      enum {
 +              CHECK_WRITEABLE = 1
 +      } command = 0;
 +
 +      struct option module_config_options[] = {
 +              OPT_CMDMODE(0, "check-writeable", &command,
 +                          N_("check if it is safe to write to the .gitmodules file"),
 +                          CHECK_WRITEABLE),
 +              OPT_END()
 +      };
 +      const char *const git_submodule_helper_usage[] = {
 +              N_("git submodule--helper config name [value]"),
 +              N_("git submodule--helper config --check-writeable"),
 +              NULL
 +      };
 +
 +      argc = parse_options(argc, argv, prefix, module_config_options,
 +                           git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
 +
 +      if (argc == 1 && command == CHECK_WRITEABLE)
 +              return is_writing_gitmodules_ok() ? 0 : -1;
 +
 +      /* Equivalent to ACTION_GET in builtin/config.c */
 +      if (argc == 2)
 +              return print_config_from_gitmodules(the_repository, argv[1]);
 +
 +      /* Equivalent to ACTION_SET in builtin/config.c */
 +      if (argc == 3) {
 +              if (!is_writing_gitmodules_ok())
 +                      die(_("please make sure that the .gitmodules file is in the working tree"));
 +
 +              return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
 +      }
 +
 +      usage_with_options(git_submodule_helper_usage, module_config_options);
 +}
 +
  #define SUPPORT_SUPER_PREFIX (1<<0)
  
  struct cmd_struct {
@@@ -2209,7 -2171,6 +2210,7 @@@ static struct cmd_struct commands[] = 
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
        {"is-active", is_active, 0},
        {"check-name", check_name, 0},
 +      {"config", module_config, 0},
  };
  
  int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --combined environment.c
index 346559770773e923766c06593ada84c72815061f,441ce56690fccff1a1baeb5c088914320df915c3..0e37741d838256d196b2ec9f3ca4e54395c66d31
@@@ -33,7 -33,6 +33,7 @@@ int ref_paranoia = -1
  int repository_format_precious_objects;
  char *repository_format_partial_clone;
  const char *core_partial_clone_filter_default;
 +int repository_format_worktree_config;
  const char *git_commit_encoding;
  const char *git_log_output_encoding;
  const char *apply_default_whitespace;
@@@ -72,6 -71,7 +72,6 @@@ int core_apply_sparse_checkout
  int merge_log_config = -1;
  int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
  unsigned long pack_size_limit_cfg;
 -enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
  enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
  
  #ifndef PROTECT_HFS_DEFAULT
@@@ -274,9 -274,9 +274,9 @@@ const char *get_git_work_tree(void
  
  char *get_object_directory(void)
  {
-       if (!the_repository->objects->objectdir)
+       if (!the_repository->objects->odb)
                BUG("git environment hasn't been setup");
-       return the_repository->objects->objectdir;
+       return the_repository->objects->odb->path;
  }
  
  int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
diff --combined fetch-pack.c
index 9691046e6445837beefde3d8ae5e842063842575,25a88f4eb27f730f4d3a702251ace3a03748cf1e..dd6700bda9240f2278e663f9147df1c0808d2efc
@@@ -636,23 -636,6 +636,6 @@@ struct loose_object_iter 
        struct ref *refs;
  };
  
- /*
-  *  If the number of refs is not larger than the number of loose objects,
-  *  this function stops inserting.
-  */
- static int add_loose_objects_to_set(const struct object_id *oid,
-                                   const char *path,
-                                   void *data)
- {
-       struct loose_object_iter *iter = data;
-       oidset_insert(iter->loose_object_set, oid);
-       if (iter->refs == NULL)
-               return 1;
-       iter->refs = iter->refs->next;
-       return 0;
- }
  /*
   * Mark recent commits available locally and reachable from a local ref as
   * COMPLETE. If args->no_dependents is false, also mark COMPLETE remote refs as
@@@ -670,30 -653,14 +653,14 @@@ static void mark_complete_and_common_re
        struct ref *ref;
        int old_save_commit_buffer = save_commit_buffer;
        timestamp_t cutoff = 0;
-       struct oidset loose_oid_set = OIDSET_INIT;
-       int use_oidset = 0;
-       struct loose_object_iter iter = {&loose_oid_set, *refs};
-       /* Enumerate all loose objects or know refs are not so many. */
-       use_oidset = !for_each_loose_object(add_loose_objects_to_set,
-                                           &iter, 0);
  
        save_commit_buffer = 0;
  
        for (ref = *refs; ref; ref = ref->next) {
                struct object *o;
-               unsigned int flags = OBJECT_INFO_QUICK;
  
-               if (use_oidset &&
-                   !oidset_contains(&loose_oid_set, &ref->old_oid)) {
-                       /*
-                        * I know this does not exist in the loose form,
-                        * so check if it exists in a non-loose form.
-                        */
-                       flags |= OBJECT_INFO_IGNORE_LOOSE;
-               }
-               if (!has_object_file_with_flags(&ref->old_oid, flags))
+               if (!has_object_file_with_flags(&ref->old_oid,
+                                               OBJECT_INFO_QUICK))
                        continue;
                o = parse_object(the_repository, &ref->old_oid);
                if (!o)
                }
        }
  
-       oidset_clear(&loose_oid_set);
        if (!args->deepen) {
                for_each_ref(mark_complete_oid, NULL);
                for_each_cached_alternate(NULL, mark_alternate_complete);
@@@ -1248,18 -1213,6 +1213,18 @@@ static int process_acks(struct fetch_ne
            reader->status != PACKET_READ_DELIM)
                die(_("error processing acks: %d"), reader->status);
  
 +      /*
 +       * If an "acknowledgments" section is sent, a packfile is sent if and
 +       * only if "ready" was sent in this section. The other sections
 +       * ("shallow-info" and "wanted-refs") are sent only if a packfile is
 +       * sent. Therefore, a DELIM is expected if "ready" is sent, and a FLUSH
 +       * otherwise.
 +       */
 +      if (received_ready && reader->status != PACKET_READ_DELIM)
 +              die(_("expected packfile to be sent after 'ready'"));
 +      if (!received_ready && reader->status != PACKET_READ_FLUSH)
 +              die(_("expected no other sections to be sent after no 'ready'"));
 +
        /* return 0 if no common, 1 if there are common, or 2 if ready */
        return received_ready ? 2 : (received_ack ? 1 : 0);
  }
diff --combined http.c
index b8258fc78fddbb05192f3f4cfed3eac1cd406834,46c2e7a27596194ed2b6d9876ddfac157230ddda..0b6807cef9aa0994be486581ecf18f7829ee4e19
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -48,7 -48,6 +48,7 @@@ char curl_errorstr[CURL_ERROR_SIZE]
  
  static int curl_ssl_verify = -1;
  static int curl_ssl_try;
 +static const char *curl_http_version = NULL;
  static const char *ssl_cert;
  static const char *ssl_cipherlist;
  static const char *ssl_version;
@@@ -285,9 -284,6 +285,9 @@@ static void process_curl_messages(void
  
  static int http_options(const char *var, const char *value, void *cb)
  {
 +      if (!strcmp("http.version", var)) {
 +              return git_config_string(&curl_http_version, var, value);
 +      }
        if (!strcmp("http.sslverify", var)) {
                curl_ssl_verify = git_config_bool(var, value);
                return 0;
@@@ -793,31 -789,6 +793,31 @@@ static long get_curl_allowed_protocols(
  }
  #endif
  
 +#if LIBCURL_VERSION_NUM >=0x072f00
 +static int get_curl_http_version_opt(const char *version_string, long *opt)
 +{
 +      int i;
 +      static struct {
 +              const char *name;
 +              long opt_token;
 +      } choice[] = {
 +              { "HTTP/1.1", CURL_HTTP_VERSION_1_1 },
 +              { "HTTP/2", CURL_HTTP_VERSION_2 }
 +      };
 +
 +      for (i = 0; i < ARRAY_SIZE(choice); i++) {
 +              if (!strcmp(version_string, choice[i].name)) {
 +                      *opt = choice[i].opt_token;
 +                      return 0;
 +              }
 +      }
 +
 +      warning("unknown value given to http.version: '%s'", version_string);
 +      return -1; /* not found */
 +}
 +
 +#endif
 +
  static CURL *get_curl_handle(void)
  {
        CURL *result = curl_easy_init();
                curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
        }
  
 +#if LIBCURL_VERSION_NUM >= 0x072f00 // 7.47.0
 +    if (curl_http_version) {
 +              long opt;
 +              if (!get_curl_http_version_opt(curl_http_version, &opt)) {
 +                      /* Set request use http version */
 +                      curl_easy_setopt(result, CURLOPT_HTTP_VERSION, opt);
 +              }
 +    }
 +#endif
 +
  #if LIBCURL_VERSION_NUM >= 0x070907
        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
  #endif
  #if LIBCURL_VERSION_NUM >= 0x072c00
                curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
  #else
 -              warning(_("CURLSSLOPT_NO_REVOKE not suported with cURL < 7.44.0"));
 +              warning(_("CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"));
  #endif
        }
  
@@@ -2353,7 -2314,7 +2353,7 @@@ struct http_object_request *new_http_ob
        hashcpy(freq->sha1, sha1);
        freq->localfile = -1;
  
-       sha1_file_name(the_repository, &filename, sha1);
+       loose_object_path(the_repository, &filename, sha1);
        strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf);
  
        strbuf_addf(&prevfile, "%s.prev", filename.buf);
@@@ -2504,7 -2465,7 +2504,7 @@@ int finish_http_object_request(struct h
                unlink_or_warn(freq->tmpfile.buf);
                return -1;
        }
-       sha1_file_name(the_repository, &filename, freq->sha1);
+       loose_object_path(the_repository, &filename, freq->sha1);
        freq->rename = finalize_object_file(freq->tmpfile.buf, filename.buf);
        strbuf_release(&filename);
  
diff --combined midx.c
index 2a6a24fcd7eff1a074c37e6c556f2143ef134c19,4fac0cd08ab9b2a78096c57518d2ea8cd1db96a2..8a505fd423efccf9986079542b47967cc93df7dc
--- 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);
@@@ -202,7 -198,7 +202,7 @@@ int prepare_midx_pack(struct multi_pack
        struct strbuf pack_name = STRBUF_INIT;
  
        if (pack_int_id >= m->num_packs)
 -              die(_("bad pack-int-id: %u (%u total packs"),
 +              die(_("bad pack-int-id: %u (%u total packs)"),
                    pack_int_id, m->num_packs);
  
        if (m->packs[pack_int_id])
@@@ -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,18 -712,12 +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;
@@@ -929,14 -914,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(r->objects->objectdir);
 -      char *midx = get_midx_filename(object_dir);
++      char *midx = get_midx_filename(r->objects->odb->path);
 +
 +      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);
diff --combined packfile.c
index d1e6683ffe877d9bf1b0996f25f0720fdffe983a,91fd40efb01720a92707f1f441bac895585bb377..8c6b47cc777708d4f8e450337f2b9d205f43591b
@@@ -345,11 -345,6 +345,11 @@@ void close_all_packs(struct raw_object_
                        BUG("want to close pack marked 'do-not-close'");
                else
                        close_pack(p);
 +
 +      if (o->multi_pack_index) {
 +              close_midx(o->multi_pack_index);
 +              o->multi_pack_index = NULL;
 +      }
  }
  
  /*
@@@ -971,16 -966,16 +971,16 @@@ static void prepare_packed_git_mru(stru
  
  static void prepare_packed_git(struct repository *r)
  {
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
  
        if (r->objects->packed_git_initialized)
                return;
-       prepare_multi_pack_index_one(r, r->objects->objectdir, 1);
-       prepare_packed_git_one(r, r->objects->objectdir, 1);
        prepare_alt_odb(r);
-       for (alt = r->objects->alt_odb_list; alt; alt = alt->next) {
-               prepare_multi_pack_index_one(r, alt->path, 0);
-               prepare_packed_git_one(r, alt->path, 0);
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               int local = (odb == r->objects->odb);
+               prepare_multi_pack_index_one(r, odb->path, local);
+               prepare_packed_git_one(r, odb->path, local);
        }
        rearrange_packed_git(r);
  
  
  void reprepare_packed_git(struct repository *r)
  {
+       struct object_directory *odb;
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               oid_array_clear(&odb->loose_objects_cache);
+               memset(&odb->loose_objects_subdir_seen, 0,
+                      sizeof(odb->loose_objects_subdir_seen));
+       }
        r->objects->approximate_object_count_valid = 0;
        r->objects->packed_git_initialized = 0;
        prepare_packed_git(r);
diff --combined path.c
index dc3294c71e1e72ae2aae9164c790e27d1237e956,e8609cf56df16a7e2b706dc3bd001f0977fde939..03ab712839a7878e3d3b5d79c698f4e2bb29c8b1
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -108,7 -108,6 +108,7 @@@ struct common_dir 
  
  static struct common_dir common_list[] = {
        { 0, 1, 0, "branches" },
 +      { 0, 1, 0, "common" },
        { 0, 1, 0, "hooks" },
        { 0, 1, 0, "info" },
        { 0, 0, 1, "info/sparse-checkout" },
        { 0, 1, 0, "objects" },
        { 0, 1, 0, "refs" },
        { 0, 1, 1, "refs/bisect" },
 +      { 0, 1, 1, "refs/worktree" },
        { 0, 1, 0, "remotes" },
        { 0, 1, 0, "worktrees" },
        { 0, 1, 0, "rr-cache" },
@@@ -385,7 -383,7 +385,7 @@@ static void adjust_git_path(const struc
                strbuf_splice(buf, 0, buf->len,
                              repo->index_file, strlen(repo->index_file));
        else if (dir_prefix(base, "objects"))
-               replace_dir(buf, git_dir_len + 7, repo->objects->objectdir);
+               replace_dir(buf, git_dir_len + 7, repo->objects->odb->path);
        else if (git_hooks_path && dir_prefix(base, "hooks"))
                replace_dir(buf, git_dir_len + 5, git_hooks_path);
        else if (repo->different_commondir)
diff --combined sha1-file.c
index 5bd11c85bc563618b59dad3ff06e5ae5296c892b,b8efc4fc8ee7c419dca13927f3070965d9a3f19d..5a272f70de149a2102a1db8f1b3165f3f20da935
@@@ -346,25 -346,21 +346,21 @@@ static void fill_sha1_path(struct strbu
        }
  }
  
- void sha1_file_name(struct repository *r, struct strbuf *buf, const unsigned char *sha1)
+ static const char *odb_loose_path(struct object_directory *odb,
+                                 struct strbuf *buf,
+                                 const unsigned char *sha1)
  {
-       strbuf_addstr(buf, r->objects->objectdir);
+       strbuf_reset(buf);
+       strbuf_addstr(buf, odb->path);
        strbuf_addch(buf, '/');
        fill_sha1_path(buf, sha1);
+       return buf->buf;
  }
  
- struct strbuf *alt_scratch_buf(struct alternate_object_database *alt)
- {
-       strbuf_setlen(&alt->scratch, alt->base_len);
-       return &alt->scratch;
- }
- static const char *alt_sha1_path(struct alternate_object_database *alt,
-                                const unsigned char *sha1)
+ const char *loose_object_path(struct repository *r, struct strbuf *buf,
+                             const unsigned char *sha1)
  {
-       struct strbuf *buf = alt_scratch_buf(alt);
-       fill_sha1_path(buf, sha1);
-       return buf->buf;
+       return odb_loose_path(r->objects->odb, buf, sha1);
  }
  
  /*
@@@ -374,7 -370,7 +370,7 @@@ static int alt_odb_usable(struct raw_ob
                          struct strbuf *path,
                          const char *normalized_objdir)
  {
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
  
        /* Detect cases where alternate disappeared */
        if (!is_directory(path->buf)) {
         * Prevent the common mistake of listing the same
         * thing twice, or object directory itself.
         */
-       for (alt = o->alt_odb_list; alt; alt = alt->next) {
-               if (!fspathcmp(path->buf, alt->path))
+       for (odb = o->odb; odb; odb = odb->next) {
+               if (!fspathcmp(path->buf, odb->path))
                        return 0;
        }
        if (!fspathcmp(path->buf, normalized_objdir))
   * Prepare alternate object database registry.
   *
   * The variable alt_odb_list points at the list of struct
-  * alternate_object_database.  The elements on this list come from
+  * object_directory.  The elements on this list come from
   * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
   * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
   * whose contents is similar to that environment variable but can be
@@@ -419,7 -415,7 +415,7 @@@ static void read_info_alternates(struc
  static int link_alt_odb_entry(struct repository *r, const char *entry,
        const char *relative_base, int depth, const char *normalized_objdir)
  {
-       struct alternate_object_database *ent;
+       struct object_directory *ent;
        struct strbuf pathbuf = STRBUF_INIT;
  
        if (!is_absolute_path(entry) && relative_base) {
                return -1;
        }
  
-       ent = alloc_alt_odb(pathbuf.buf);
+       ent = xcalloc(1, sizeof(*ent));
+       ent->path = xstrdup(pathbuf.buf);
  
        /* add the alternate entry */
-       *r->objects->alt_odb_tail = ent;
-       r->objects->alt_odb_tail = &(ent->next);
+       *r->objects->odb_tail = ent;
+       r->objects->odb_tail = &(ent->next);
        ent->next = NULL;
  
        /* recursively add alternates */
@@@ -505,7 -502,7 +502,7 @@@ static void link_alt_odb_entries(struc
                return;
        }
  
-       strbuf_add_absolute_path(&objdirbuf, r->objects->objectdir);
+       strbuf_add_absolute_path(&objdirbuf, r->objects->odb->path);
        if (strbuf_normalize_path(&objdirbuf) < 0)
                die(_("unable to normalize object directory: %s"),
                    objdirbuf.buf);
@@@ -540,18 -537,6 +537,6 @@@ static void read_info_alternates(struc
        free(path);
  }
  
- struct alternate_object_database *alloc_alt_odb(const char *dir)
- {
-       struct alternate_object_database *ent;
-       FLEX_ALLOC_STR(ent, path, dir);
-       strbuf_init(&ent->scratch, 0);
-       strbuf_addf(&ent->scratch, "%s/", dir);
-       ent->base_len = ent->scratch.len;
-       return ent;
- }
  void add_to_alternates_file(const char *reference)
  {
        struct lock_file lock = LOCK_INIT;
                fprintf_or_die(out, "%s\n", reference);
                if (commit_lock_file(&lock))
                        die_errno(_("unable to move new alternates file into place"));
-               if (the_repository->objects->alt_odb_tail)
+               if (the_repository->objects->loaded_alternates)
                        link_alt_odb_entries(the_repository, reference,
                                             '\n', NULL, 0);
        }
@@@ -684,11 -669,11 +669,11 @@@ out
  
  int foreach_alt_odb(alt_odb_fn fn, void *cb)
  {
-       struct alternate_object_database *ent;
+       struct object_directory *ent;
        int r = 0;
  
        prepare_alt_odb(the_repository);
-       for (ent = the_repository->objects->alt_odb_list; ent; ent = ent->next) {
+       for (ent = the_repository->objects->odb->next; ent; ent = ent->next) {
                r = fn(ent, cb);
                if (r)
                        break;
  
  void prepare_alt_odb(struct repository *r)
  {
-       if (r->objects->alt_odb_tail)
+       if (r->objects->loaded_alternates)
                return;
  
-       r->objects->alt_odb_tail = &r->objects->alt_odb_list;
        link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);
  
-       read_info_alternates(r, r->objects->objectdir, 0);
+       read_info_alternates(r, r->objects->odb->path, 0);
+       r->objects->loaded_alternates = 1;
  }
  
  /* Returns 1 if we have successfully freshened the file, 0 otherwise. */
@@@ -731,23 -716,27 +716,27 @@@ int check_and_freshen_file(const char *
        return 1;
  }
  
- static int check_and_freshen_local(const struct object_id *oid, int freshen)
+ static int check_and_freshen_odb(struct object_directory *odb,
+                                const struct object_id *oid,
+                                int freshen)
  {
-       static struct strbuf buf = STRBUF_INIT;
-       strbuf_reset(&buf);
-       sha1_file_name(the_repository, &buf, oid->hash);
+       static struct strbuf path = STRBUF_INIT;
+       odb_loose_path(odb, &path, oid->hash);
+       return check_and_freshen_file(path.buf, freshen);
+ }
  
-       return check_and_freshen_file(buf.buf, freshen);
+ static int check_and_freshen_local(const struct object_id *oid, int freshen)
+ {
+       return check_and_freshen_odb(the_repository->objects->odb, oid, freshen);
  }
  
  static int check_and_freshen_nonlocal(const struct object_id *oid, int freshen)
  {
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
        prepare_alt_odb(the_repository);
-       for (alt = the_repository->objects->alt_odb_list; alt; alt = alt->next) {
-               const char *path = alt_sha1_path(alt, oid->hash);
-               if (check_and_freshen_file(path, freshen))
+       for (odb = the_repository->objects->odb->next; odb; odb = odb->next) {
+               if (check_and_freshen_odb(odb, oid, freshen))
                        return 1;
        }
        return 0;
@@@ -833,7 -822,7 +822,7 @@@ int check_object_signature(const struc
                return -1;
  
        /* Generate the header */
 -      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(obj_type), size) + 1;
 +      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(obj_type), (uintmax_t)size) + 1;
  
        /* Sha1.. */
        the_hash_algo->init_fn(&c);
@@@ -888,25 -877,17 +877,17 @@@ int git_open_cloexec(const char *name, 
   *
   * The "path" out-parameter will give the path of the object we found (if any).
   * Note that it may point to static storage and is only valid until another
-  * call to sha1_file_name(), etc.
+  * call to stat_sha1_file().
   */
  static int stat_sha1_file(struct repository *r, const unsigned char *sha1,
                          struct stat *st, const char **path)
  {
-       struct alternate_object_database *alt;
+       struct object_directory *odb;
        static struct strbuf buf = STRBUF_INIT;
  
-       strbuf_reset(&buf);
-       sha1_file_name(r, &buf, sha1);
-       *path = buf.buf;
-       if (!lstat(*path, st))
-               return 0;
        prepare_alt_odb(r);
-       errno = ENOENT;
-       for (alt = r->objects->alt_odb_list; alt; alt = alt->next) {
-               *path = alt_sha1_path(alt, sha1);
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               *path = odb_loose_path(odb, &buf, sha1);
                if (!lstat(*path, st))
                        return 0;
        }
@@@ -922,25 -903,17 +903,17 @@@ static int open_sha1_file(struct reposi
                          const unsigned char *sha1, const char **path)
  {
        int fd;
-       struct alternate_object_database *alt;
-       int most_interesting_errno;
+       struct object_directory *odb;
+       int most_interesting_errno = ENOENT;
        static struct strbuf buf = STRBUF_INIT;
  
-       strbuf_reset(&buf);
-       sha1_file_name(r, &buf, sha1);
-       *path = buf.buf;
-       fd = git_open(*path);
-       if (fd >= 0)
-               return fd;
-       most_interesting_errno = errno;
        prepare_alt_odb(r);
-       for (alt = r->objects->alt_odb_list; alt; alt = alt->next) {
-               *path = alt_sha1_path(alt, sha1);
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               *path = odb_loose_path(odb, &buf, sha1);
                fd = git_open(*path);
                if (fd >= 0)
                        return fd;
                if (most_interesting_errno == ENOENT)
                        most_interesting_errno = errno;
        }
        return -1;
  }
  
+ static int quick_has_loose(struct repository *r,
+                          const unsigned char *sha1)
+ {
+       int subdir_nr = sha1[0];
+       struct object_id oid;
+       struct object_directory *odb;
+       hashcpy(oid.hash, sha1);
+       prepare_alt_odb(r);
+       for (odb = r->objects->odb; odb; odb = odb->next) {
+               odb_load_loose_cache(odb, subdir_nr);
+               if (oid_array_lookup(&odb->loose_objects_cache, &oid) >= 0)
+                       return 1;
+       }
+       return 0;
+ }
  /*
   * Map the loose object at "path" if it is not NULL, or the path found by
   * searching for a loose object named "sha1".
@@@ -1198,6 -1189,8 +1189,8 @@@ static int sha1_loose_object_info(struc
        if (!oi->typep && !oi->type_name && !oi->sizep && !oi->contentp) {
                const char *path;
                struct stat st;
+               if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
+                       return quick_has_loose(r, sha1) ? 0 : -1;
                if (stat_sha1_file(r, sha1, &st, &path) < 0)
                        return -1;
                if (oi->disk_sizep)
@@@ -1492,7 -1485,7 +1485,7 @@@ static void write_object_file_prepare(c
        git_hash_ctx c;
  
        /* Generate the header */
 -      *hdrlen = xsnprintf(hdr, *hdrlen, "%s %lu", type, len)+1;
 +      *hdrlen = xsnprintf(hdr, *hdrlen, "%s %"PRIuMAX , type, (uintmax_t)len)+1;
  
        /* Sha1.. */
        the_hash_algo->init_fn(&c);
@@@ -1626,8 -1619,7 +1619,7 @@@ static int write_loose_object(const str
        static struct strbuf tmp_file = STRBUF_INIT;
        static struct strbuf filename = STRBUF_INIT;
  
-       strbuf_reset(&filename);
-       sha1_file_name(the_repository, &filename, oid->hash);
+       loose_object_path(the_repository, &filename, oid->hash);
  
        fd = create_tmpfile(&tmp_file, filename.buf);
        if (fd < 0) {
@@@ -1758,7 -1750,7 +1750,7 @@@ int force_object_loose(const struct obj
        buf = read_object(oid->hash, &type, &len);
        if (!buf)
                return error(_("cannot read sha1_file for %s"), oid_to_hex(oid));
 -      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(type), len) + 1;
 +      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1;
        ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime);
        free(buf);
  
@@@ -2134,43 -2126,50 +2126,50 @@@ int for_each_loose_file_in_objdir(cons
        return r;
  }
  
- struct loose_alt_odb_data {
-       each_loose_object_fn *cb;
-       void *data;
- };
- static int loose_from_alt_odb(struct alternate_object_database *alt,
-                             void *vdata)
+ int for_each_loose_object(each_loose_object_fn cb, void *data,
+                         enum for_each_object_flags flags)
  {
-       struct loose_alt_odb_data *data = vdata;
-       struct strbuf buf = STRBUF_INIT;
-       int r;
+       struct object_directory *odb;
  
-       strbuf_addstr(&buf, alt->path);
-       r = for_each_loose_file_in_objdir_buf(&buf,
-                                             data->cb, NULL, NULL,
-                                             data->data);
-       strbuf_release(&buf);
-       return r;
+       prepare_alt_odb(the_repository);
+       for (odb = the_repository->objects->odb; odb; odb = odb->next) {
+               int r = for_each_loose_file_in_objdir(odb->path, cb, NULL,
+                                                     NULL, data);
+               if (r)
+                       return r;
+               if (flags & FOR_EACH_OBJECT_LOCAL_ONLY)
+                       break;
+       }
+       return 0;
  }
  
int for_each_loose_object(each_loose_object_fn cb, void *data,
-                         enum for_each_object_flags flags)
static int append_loose_object(const struct object_id *oid, const char *path,
+                              void *data)
  {
-       struct loose_alt_odb_data alt;
-       int r;
+       oid_array_append(data, oid);
+       return 0;
+ }
  
-       r = for_each_loose_file_in_objdir(get_object_directory(),
-                                         cb, NULL, NULL, data);
-       if (r)
-               return r;
+ void odb_load_loose_cache(struct object_directory *odb, int subdir_nr)
+ {
+       struct strbuf buf = STRBUF_INIT;
  
-       if (flags & FOR_EACH_OBJECT_LOCAL_ONLY)
-               return 0;
+       if (subdir_nr < 0 ||
+           subdir_nr >= ARRAY_SIZE(odb->loose_objects_subdir_seen))
+               BUG("subdir_nr out of range");
  
-       alt.cb = cb;
-       alt.data = data;
-       return foreach_alt_odb(loose_from_alt_odb, &alt);
+       if (odb->loose_objects_subdir_seen[subdir_nr])
+               return;
+       strbuf_addstr(&buf, odb->path);
+       for_each_file_in_obj_subdir(subdir_nr, &buf,
+                                   append_loose_object,
+                                   NULL, NULL,
+                                   &odb->loose_objects_cache);
+       odb->loose_objects_subdir_seen[subdir_nr] = 1;
+       strbuf_release(&buf);
  }
  
  static int check_stream_sha1(git_zstream *stream,
         * see the comment in unpack_sha1_rest for details.
         */
        while (total_read <= size &&
 -             (status == Z_OK || status == Z_BUF_ERROR)) {
 +             (status == Z_OK ||
 +              (status == Z_BUF_ERROR && !stream->avail_out))) {
                stream->next_out = buf;
                stream->avail_out = sizeof(buf);
                if (size - total_read < stream->avail_out)