Merge branch 'cc/multi-promisor'
authorJunio C Hamano <gitster@pobox.com>
Wed, 18 Sep 2019 18:50:09 +0000 (11:50 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 18 Sep 2019 18:50:09 +0000 (11:50 -0700)
Teach the lazy clone machinery that there can be more than one
promisor remote and consult them in order when downloading missing
objects on demand.

* cc/multi-promisor:
Move core_partial_clone_filter_default to promisor-remote.c
Move repository_format_partial_clone to promisor-remote.c
Remove fetch-object.{c,h} in favor of promisor-remote.{c,h}
remote: add promisor and partial clone config to the doc
partial-clone: add multiple remotes in the doc
t0410: test fetching from many promisor remotes
builtin/fetch: remove unique promisor remote limitation
promisor-remote: parse remote.*.partialclonefilter
Use promisor_remote_get_direct() and has_promisor_remote()
promisor-remote: use repository_format_partial_clone
promisor-remote: add promisor_remote_reinit()
promisor-remote: implement promisor_remote_get_direct()
Add initial support for many promisor remotes
fetch-object: make functions return an error code
t0410: remove pipes after git commands

16 files changed:
1  2 
Makefile
builtin/cat-file.c
builtin/fetch.c
builtin/gc.c
builtin/repack.c
cache-tree.c
cache.h
config.c
connected.c
diff.c
packfile.c
sha1-file.c
t/t0410-partial-clone.sh
t/t5601-clone.sh
t/t5616-partial-clone.sh
unpack-trees.c
diff --combined Makefile
index ad71ae12194e1d27f2adcd4014d0048ee4a5ebcd,9b0baa72390e94bc0c8588ee77987344e1c05788..f879697ea3b23f6ad6308afb33ae0f08ec262591
+++ b/Makefile
@@@ -704,7 -704,6 +704,7 @@@ TEST_BUILTINS_OBJS += test-config.
  TEST_BUILTINS_OBJS += test-ctype.o
  TEST_BUILTINS_OBJS += test-date.o
  TEST_BUILTINS_OBJS += test-delta.o
 +TEST_BUILTINS_OBJS += test-dir-iterator.o
  TEST_BUILTINS_OBJS += test-drop-caches.o
  TEST_BUILTINS_OBJS += test-dump-cache-tree.o
  TEST_BUILTINS_OBJS += test-dump-fsmonitor.o
@@@ -722,7 -721,6 +722,7 @@@ TEST_BUILTINS_OBJS += test-lazy-init-na
  TEST_BUILTINS_OBJS += test-match-trees.o
  TEST_BUILTINS_OBJS += test-mergesort.o
  TEST_BUILTINS_OBJS += test-mktemp.o
 +TEST_BUILTINS_OBJS += test-oidmap.o
  TEST_BUILTINS_OBJS += test-online-cpus.o
  TEST_BUILTINS_OBJS += test-parse-options.o
  TEST_BUILTINS_OBJS += test-path-utils.o
@@@ -773,11 -771,9 +773,11 @@@ BUILT_INS += git-format-patch$
  BUILT_INS += git-fsck-objects$X
  BUILT_INS += git-init$X
  BUILT_INS += git-merge-subtree$X
 +BUILT_INS += git-restore$X
  BUILT_INS += git-show$X
  BUILT_INS += git-stage$X
  BUILT_INS += git-status$X
 +BUILT_INS += git-switch$X
  BUILT_INS += git-whatchanged$X
  
  # what 'all' will build and 'install' will install in gitexecdir,
@@@ -884,7 -880,6 +884,6 @@@ LIB_OBJS += ewah/ewah_io.
  LIB_OBJS += ewah/ewah_rlw.o
  LIB_OBJS += exec-cmd.o
  LIB_OBJS += fetch-negotiator.o
- LIB_OBJS += fetch-object.o
  LIB_OBJS += fetch-pack.o
  LIB_OBJS += fsck.o
  LIB_OBJS += fsmonitor.o
@@@ -948,6 -943,7 +947,7 @@@ LIB_OBJS += preload-index.
  LIB_OBJS += pretty.o
  LIB_OBJS += prio-queue.o
  LIB_OBJS += progress.o
+ LIB_OBJS += promisor-remote.o
  LIB_OBJS += prompt.o
  LIB_OBJS += protocol.o
  LIB_OBJS += quote.o
@@@ -965,7 -961,6 +965,7 @@@ LIB_OBJS += refspec.
  LIB_OBJS += ref-filter.o
  LIB_OBJS += remote.o
  LIB_OBJS += replace-object.o
 +LIB_OBJS += repo-settings.o
  LIB_OBJS += repository.o
  LIB_OBJS += rerere.o
  LIB_OBJS += resolve-undo.o
@@@ -1064,7 -1059,6 +1064,7 @@@ BUILTIN_OBJS += builtin/diff-index.
  BUILTIN_OBJS += builtin/diff-tree.o
  BUILTIN_OBJS += builtin/diff.o
  BUILTIN_OBJS += builtin/difftool.o
 +BUILTIN_OBJS += builtin/env--helper.o
  BUILTIN_OBJS += builtin/fast-export.o
  BUILTIN_OBJS += builtin/fetch-pack.o
  BUILTIN_OBJS += builtin/fetch.o
@@@ -1241,7 -1235,7 +1241,7 @@@ endi
  
  ifdef SANE_TOOL_PATH
  SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH))
 -BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix $(SANE_TOOL_PATH_SQ)|'
 +BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix "$(SANE_TOOL_PATH_SQ)"|'
  PATH := $(SANE_TOOL_PATH):${PATH}
  else
  BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
@@@ -2721,7 -2715,7 +2721,7 @@@ bin-wrappers/%: wrap-for-bin.s
        @mkdir -p bin-wrappers
        $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
             -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
 -           -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%,$(@F))|' < $< > $@ && \
 +           -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X)))|' < $< > $@ && \
        chmod +x $@
  
  # GNU make supports exporting all variables by "export" without parameters.
@@@ -2864,33 -2858,6 +2864,33 @@@ install: al
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
 +ifdef MSVC
 +      # We DO NOT install the individual foo.o.pdb files because they
 +      # have already been rolled up into the exe's pdb file.
 +      # We DO NOT have pdb files for the builtin commands (like git-status.exe)
 +      # because it is just a copy/hardlink of git.exe, rather than a unique binary.
 +      $(INSTALL) git.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) git-shell.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) git-upload-pack.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) git-credential-store.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-daemon.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-fast-import.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-http-backend.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-http-fetch.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-http-push.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-imap-send.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-remote-http.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-remote-testsvn.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-sh-i18n--envsubst.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +      $(INSTALL) git-show-index.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +ifndef DEBUG
 +      $(INSTALL) $(vcpkg_rel_bin)/*.dll '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) $(vcpkg_rel_bin)/*.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
 +else
 +      $(INSTALL) $(vcpkg_dbg_bin)/*.dll '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) $(vcpkg_dbg_bin)/*.pdb '$(DESTDIR_SQ)$(bindir_SQ)'
 +endif
 +endif
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mergetools_instdir_SQ)'
        $(INSTALL) -m 644 mergetools/* '$(DESTDIR_SQ)$(mergetools_instdir_SQ)'
@@@ -3103,19 -3070,6 +3103,19 @@@ endi
        $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS
        $(RM) GIT-USER-AGENT GIT-PREFIX
        $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS
 +ifdef MSVC
 +      $(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS))
 +      $(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS))
 +      $(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS))
 +      $(RM) $(patsubst %.exe,%.ipdb,$(OTHER_PROGRAMS))
 +      $(RM) $(patsubst %.exe,%.pdb,$(PROGRAMS))
 +      $(RM) $(patsubst %.exe,%.iobj,$(PROGRAMS))
 +      $(RM) $(patsubst %.exe,%.ipdb,$(PROGRAMS))
 +      $(RM) $(patsubst %.exe,%.pdb,$(TEST_PROGRAMS))
 +      $(RM) $(patsubst %.exe,%.iobj,$(TEST_PROGRAMS))
 +      $(RM) $(patsubst %.exe,%.ipdb,$(TEST_PROGRAMS))
 +      $(RM) compat/vcbuild/MSVC-DEFS-GEN
 +endif
  
  .PHONY: all install profile-clean cocciclean clean strip
  .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
diff --combined builtin/cat-file.c
index 995d47c85aad24a645786ed6480bd659c755997e,85ae10bf0b81bf2bb536548528dc177517f3fb49..d6a1aa74cd41fe17fdb9d92f6b55fb7d67b31726
@@@ -15,6 -15,7 +15,7 @@@
  #include "sha1-array.h"
  #include "packfile.h"
  #include "object-store.h"
+ #include "promisor-remote.h"
  
  struct batch_options {
        int enabled;
@@@ -172,8 -173,7 +173,8 @@@ static int cat_one_file(int opt, const 
                         * fall-back to the usual case.
                         */
                }
 -              buf = read_object_with_reference(&oid, exp_type, &size, NULL);
 +              buf = read_object_with_reference(the_repository,
 +                                               &oid, exp_type, &size, NULL);
                break;
  
        default:
@@@ -524,8 -524,8 +525,8 @@@ static int batch_objects(struct batch_o
        if (opt->all_objects) {
                struct object_cb_data cb;
  
-               if (repository_format_partial_clone)
-                       warning("This repository has extensions.partialClone set. Some objects may not be loaded.");
+               if (has_promisor_remote())
+                       warning("This repository uses promisor remotes. Some objects may not be loaded.");
  
                cb.opt = opt;
                cb.expand = &data;
diff --combined builtin/fetch.c
index 54d6b018929159f7eb1649dbaf33eb1b7f251140,5657d054ec55f71cbf8f117e93516f6193cb4bf3..538f0e72073fdc92af1b0d79604856cf80a045f6
  #include "packfile.h"
  #include "list-objects-filter-options.h"
  #include "commit-reach.h"
 +#include "branch.h"
+ #include "promisor-remote.h"
  
 +#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
 +
  static const char * const builtin_fetch_usage[] = {
        N_("git fetch [<options>] [<repository> [<refspec>...]]"),
        N_("git fetch [<options>] <group>"),
@@@ -42,8 -40,6 +43,8 @@@ enum 
  };
  
  static int fetch_prune_config = -1; /* unspecified */
 +static int fetch_show_forced_updates = 1;
 +static uint64_t forced_updates_ms = 0;
  static int prune = -1; /* unspecified */
  #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
  
@@@ -51,10 -47,8 +52,10 @@@ static int fetch_prune_tags_config = -1
  static int prune_tags = -1; /* unspecified */
  #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
  
 -static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
 +static int all, append, dry_run, force, keep, multiple, update_head_ok;
 +static int verbosity, deepen_relative, set_upstream;
  static int progress = -1;
 +static int enable_auto_gc = 1;
  static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
  static int max_children = 1;
  static enum transport_family family;
@@@ -86,11 -80,6 +87,11 @@@ static int git_fetch_config(const char 
                return 0;
        }
  
 +      if (!strcmp(k, "fetch.showforcedupdates")) {
 +              fetch_show_forced_updates = git_config_bool(k, v);
 +              return 0;
 +      }
 +
        if (!strcmp(k, "submodule.recurse")) {
                int r = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
@@@ -125,8 -114,6 +126,8 @@@ static struct option builtin_fetch_opti
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "all", &all,
                 N_("fetch from all remotes")),
 +      OPT_BOOL(0, "set-upstream", &set_upstream,
 +               N_("set upstream for git pull/fetch")),
        OPT_BOOL('a', "append", &append,
                 N_("append to .git/FETCH_HEAD instead of overwriting")),
        OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
        OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
                        N_("report that we have only objects reachable from this object")),
        OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
 +      OPT_BOOL(0, "auto-gc", &enable_auto_gc,
 +               N_("run 'gc --auto' after fetching")),
 +      OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
 +               N_("check for forced-updates on all updated branches")),
        OPT_END()
  };
  
@@@ -257,7 -240,6 +258,7 @@@ static int will_fetch(struct ref **head
  struct refname_hash_entry {
        struct hashmap_entry ent; /* must be the first member */
        struct object_id oid;
 +      int ignore;
        char refname[FLEX_ARRAY];
  };
  
@@@ -306,11 -288,6 +307,11 @@@ static int refname_hash_exists(struct h
        return !!hashmap_get_from_hash(map, strhash(refname), refname);
  }
  
 +static void clear_item(struct refname_hash_entry *item)
 +{
 +      item->ignore = 1;
 +}
 +
  static void find_non_local_tags(const struct ref *refs,
                                struct ref **head,
                                struct ref ***tail)
                            !will_fetch(head, ref->old_oid.hash) &&
                            !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                            !will_fetch(head, item->oid.hash))
 -                              oidclr(&item->oid);
 +                              clear_item(item);
                        item = NULL;
                        continue;
                }
                if (item &&
                    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                    !will_fetch(head, item->oid.hash))
 -                      oidclr(&item->oid);
 +                      clear_item(item);
  
                item = NULL;
  
        if (item &&
            !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
            !will_fetch(head, item->oid.hash))
 -              oidclr(&item->oid);
 +              clear_item(item);
  
        /*
         * For all the tags in the remote_refs_list,
         */
        for_each_string_list_item(remote_ref_item, &remote_refs_list) {
                const char *refname = remote_ref_item->string;
 +              struct ref *rm;
  
                item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
                if (!item)
                        BUG("unseen remote ref?");
  
                /* Unless we have already decided to ignore this item... */
 -              if (!is_null_oid(&item->oid)) {
 -                      struct ref *rm = alloc_ref(item->refname);
 -                      rm->peer_ref = alloc_ref(item->refname);
 -                      oidcpy(&rm->old_oid, &item->oid);
 -                      **tail = rm;
 -                      *tail = &rm->next;
 -              }
 +              if (item->ignore)
 +                      continue;
 +
 +              rm = alloc_ref(item->refname);
 +              rm->peer_ref = alloc_ref(item->refname);
 +              oidcpy(&rm->old_oid, &item->oid);
 +              **tail = rm;
 +              *tail = &rm->next;
        }
        hashmap_free(&remote_refs, 1);
        string_list_clear(&remote_refs_list, 0);
@@@ -725,7 -700,6 +726,7 @@@ static int update_local_ref(struct ref 
        enum object_type type;
        struct branch *current_branch = branch_get(NULL);
        const char *pretty_ref = prettify_refname(ref->name);
 +      int fast_forward = 0;
  
        type = oid_object_info(the_repository, &ref->new_oid, NULL);
        if (type < 0)
                return r;
        }
  
 -      if (in_merge_bases(current, updated)) {
 +      if (fetch_show_forced_updates) {
 +              uint64_t t_before = getnanotime();
 +              fast_forward = in_merge_bases(current, updated);
 +              forced_updates_ms += (getnanotime() - t_before) / 1000000;
 +      } else {
 +              fast_forward = 1;
 +      }
 +
 +      if (fast_forward) {
                struct strbuf quickref = STRBUF_INIT;
                int r;
 +
                strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "..");
                strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
@@@ -854,15 -819,6 +855,15 @@@ static int iterate_ref_map(void *cb_dat
        return 0;
  }
  
 +static const char warn_show_forced_updates[] =
 +N_("Fetch normally indicates which branches had a forced update,\n"
 +   "but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
 +   "flag or run 'git config fetch.showForcedUpdates true'.");
 +static const char warn_time_show_forced_updates[] =
 +N_("It took %.2f seconds to check forced updates. You can use\n"
 +   "'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
 +   " to avoid this check.\n");
 +
  static int store_updated_refs(const char *raw_url, const char *remote_name,
                              int connectivity_checked, struct ref *ref_map)
  {
                      " 'git remote prune %s' to remove any old, conflicting "
                      "branches"), remote_name);
  
 +      if (advice_fetch_show_forced_updates) {
 +              if (!fetch_show_forced_updates) {
 +                      warning(_(warn_show_forced_updates));
 +              } else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
 +                      warning(_(warn_time_show_forced_updates),
 +                              forced_updates_ms / 1000.0);
 +              }
 +      }
 +
   abort:
        strbuf_release(&note);
        free(url);
@@@ -1371,51 -1318,6 +1372,51 @@@ static int do_fetch(struct transport *t
                retcode = 1;
                goto cleanup;
        }
 +
 +      if (set_upstream) {
 +              struct branch *branch = branch_get("HEAD");
 +              struct ref *rm;
 +              struct ref *source_ref = NULL;
 +
 +              /*
 +               * We're setting the upstream configuration for the
 +               * current branch. The relevent upstream is the
 +               * fetched branch that is meant to be merged with the
 +               * current one, i.e. the one fetched to FETCH_HEAD.
 +               *
 +               * When there are several such branches, consider the
 +               * request ambiguous and err on the safe side by doing
 +               * nothing and just emit a warning.
 +               */
 +              for (rm = ref_map; rm; rm = rm->next) {
 +                      if (!rm->peer_ref) {
 +                              if (source_ref) {
 +                                      warning(_("multiple branch detected, incompatible with --set-upstream"));
 +                                      goto skip;
 +                              } else {
 +                                      source_ref = rm;
 +                              }
 +                      }
 +              }
 +              if (source_ref) {
 +                      if (!strcmp(source_ref->name, "HEAD") ||
 +                          starts_with(source_ref->name, "refs/heads/"))
 +                              install_branch_config(0,
 +                                                    branch->name,
 +                                                    transport->remote->name,
 +                                                    source_ref->name);
 +                      else if (starts_with(source_ref->name, "refs/remotes/"))
 +                              warning(_("not setting upstream for a remote remote-tracking branch"));
 +                      else if (starts_with(source_ref->name, "refs/tags/"))
 +                              warning(_("not setting upstream for a remote tag"));
 +                      else
 +                              warning(_("unknown branch type"));
 +              } else {
 +                      warning(_("no source branch found.\n"
 +                              "you need to specify exactly one branch with the --set-upstream option."));
 +              }
 +      }
 + skip:
        free_refs(ref_map);
  
        /* if neither --no-tags nor --tags was specified, do automated tag
@@@ -1523,7 -1425,7 +1524,7 @@@ static int fetch_multiple(struct string
                        return errcode;
        }
  
 -      argv_array_pushl(&argv, "fetch", "--append", NULL);
 +      argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
        add_options_to_argv(&argv);
  
        for (i = 0; i < list->nr; i++) {
@@@ -1559,37 -1461,27 +1560,27 @@@ static inline void fetch_one_setup_part
         * If no prior partial clone/fetch and the current fetch DID NOT
         * request a partial-fetch, do a normal fetch.
         */
-       if (!repository_format_partial_clone && !filter_options.choice)
+       if (!has_promisor_remote() && !filter_options.choice)
                return;
  
        /*
-        * If this is the FIRST partial-fetch request, we enable partial
-        * on this repo and remember the given filter-spec as the default
-        * for subsequent fetches to this remote.
+        * If this is a partial-fetch request, we enable partial on
+        * this repo if not already enabled and remember the given
+        * filter-spec as the default for subsequent fetches to this
+        * remote.
         */
-       if (!repository_format_partial_clone && filter_options.choice) {
+       if (filter_options.choice) {
                partial_clone_register(remote->name, &filter_options);
                return;
        }
  
-       /*
-        * We are currently limited to only ONE promisor remote and only
-        * allow partial-fetches from the promisor remote.
-        */
-       if (strcmp(remote->name, repository_format_partial_clone)) {
-               if (filter_options.choice)
-                       die(_("--filter can only be used with the remote "
-                             "configured in extensions.partialClone"));
-               return;
-       }
        /*
         * Do a partial-fetch from the promisor remote using either the
         * explicitly given filter-spec or inherit the filter-spec from
         * the config.
         */
        if (!filter_options.choice)
-               partial_clone_get_default_filter_spec(&filter_options);
+               partial_clone_get_default_filter_spec(&filter_options, remote->name);
        return;
  }
  
@@@ -1710,7 -1602,7 +1701,7 @@@ int cmd_fetch(int argc, const char **ar
        if (depth || deepen_since || deepen_not.nr)
                deepen = 1;
  
-       if (filter_options.choice && !repository_format_partial_clone)
+       if (filter_options.choice && !has_promisor_remote())
                die("--filter can only be used when extensions.partialClone is set");
  
        if (all) {
        }
  
        if (remote) {
-               if (filter_options.choice || repository_format_partial_clone)
+               if (filter_options.choice || has_promisor_remote())
                        fetch_one_setup_partial(remote);
                result = fetch_one(remote, argc, argv, prune_tags_ok);
        } else {
  
        string_list_clear(&list, 0);
  
 -      close_all_packs(the_repository->objects);
 +      close_object_store(the_repository->objects);
  
 -      argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
 -      if (verbosity < 0)
 -              argv_array_push(&argv_gc_auto, "--quiet");
 -      run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
 -      argv_array_clear(&argv_gc_auto);
 +      if (enable_auto_gc) {
 +              argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
 +              if (verbosity < 0)
 +                      argv_array_push(&argv_gc_auto, "--quiet");
 +              run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
 +              argv_array_clear(&argv_gc_auto);
 +      }
  
        return result;
  }
diff --combined builtin/gc.c
index a22b6ff683465a3addeb059c89775e080197f155,824a8832b5293c3e204cdd3f3cf183ee001833b1..fadb45489f34a760f4c8f6c96c3cc5a5c5c115bd
@@@ -27,6 -27,7 +27,7 @@@
  #include "pack-objects.h"
  #include "blob.h"
  #include "tree.h"
+ #include "promisor-remote.h"
  
  #define FAILED_RUN "failed to run %s"
  
@@@ -41,6 -42,7 +42,6 @@@ static int aggressive_depth = 50
  static int aggressive_window = 250;
  static int gc_auto_threshold = 6700;
  static int gc_auto_pack_limit = 50;
 -static int gc_write_commit_graph;
  static int detach_auto = 1;
  static timestamp_t gc_log_expire_time;
  static const char *gc_log_expire = "1.day.ago";
@@@ -147,6 -149,7 +148,6 @@@ static void gc_config(void
        git_config_get_int("gc.aggressivedepth", &aggressive_depth);
        git_config_get_int("gc.auto", &gc_auto_threshold);
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
 -      git_config_get_bool("gc.writecommitgraph", &gc_write_commit_graph);
        git_config_get_bool("gc.autodetach", &detach_auto);
        git_config_get_expiry("gc.pruneexpire", &prune_expire);
        git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
@@@ -651,7 -654,7 +652,7 @@@ int cmd_gc(int argc, const char **argv
        gc_before_repack();
  
        if (!repository_format_precious_objects) {
 -              close_all_packs(the_repository->objects);
 +              close_object_store(the_repository->objects);
                if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
                        die(FAILED_RUN, repack.argv[0]);
  
                        argv_array_push(&prune, prune_expire);
                        if (quiet)
                                argv_array_push(&prune, "--no-progress");
-                       if (repository_format_partial_clone)
+                       if (has_promisor_remote())
                                argv_array_push(&prune,
                                                "--exclude-promisor-objects");
                        if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
        report_garbage = report_pack_garbage;
        reprepare_packed_git(the_repository);
        if (pack_garbage.nr > 0) {
 -              close_all_packs(the_repository->objects);
 +              close_object_store(the_repository->objects);
                clean_pack_garbage();
        }
  
 -      if (gc_write_commit_graph)
 -              write_commit_graph_reachable(get_object_directory(), 0,
 -                                           !quiet && !daemonized);
 +      prepare_repo_settings(the_repository);
 +      if (the_repository->settings.gc_write_commit_graph == 1)
 +              write_commit_graph_reachable(get_object_directory(),
 +                                           !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
 +                                           NULL);
  
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
diff --combined builtin/repack.c
index 632c0c0a79422a229d52c83665331501e8c54e29,df9a32c9060e46d9e33d2f1aa7599e63a01763aa..3b3dd1437299a53a9031572820c12f3269922695
@@@ -11,6 -11,7 +11,7 @@@
  #include "midx.h"
  #include "packfile.h"
  #include "object-store.h"
+ #include "promisor-remote.h"
  
  static int delta_base_offset = 1;
  static int pack_kept_objects = -1;
@@@ -129,9 -130,19 +130,9 @@@ static void get_non_kept_pack_filenames
  
  static void remove_redundant_pack(const char *dir_name, const char *base_name)
  {
 -      const char *exts[] = {".pack", ".idx", ".keep", ".bitmap", ".promisor"};
 -      int i;
        struct strbuf buf = STRBUF_INIT;
 -      size_t plen;
 -
 -      strbuf_addf(&buf, "%s/%s", dir_name, base_name);
 -      plen = buf.len;
 -
 -      for (i = 0; i < ARRAY_SIZE(exts); i++) {
 -              strbuf_setlen(&buf, plen);
 -              strbuf_addstr(&buf, exts[i]);
 -              unlink(buf.buf);
 -      }
 +      strbuf_addf(&buf, "%s/%s.pack", dir_name, base_name);
 +      unlink_pack_path(buf.buf, 1);
        strbuf_release(&buf);
  }
  
@@@ -333,13 -344,11 +334,13 @@@ int cmd_repack(int argc, const char **a
            (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
                die(_("--keep-unreachable and -A are incompatible"));
  
 -      if (write_bitmaps < 0)
 -              write_bitmaps = (pack_everything & ALL_INTO_ONE) &&
 -                               is_bare_repository();
 +      if (write_bitmaps < 0) {
 +              if (!(pack_everything & ALL_INTO_ONE) ||
 +                  !is_bare_repository())
 +                      write_bitmaps = 0;
 +      }
        if (pack_kept_objects < 0)
 -              pack_kept_objects = write_bitmaps;
 +              pack_kept_objects = write_bitmaps > 0;
  
        if (write_bitmaps && !(pack_everything & ALL_INTO_ONE))
                die(_(incremental_bitmap_conflict_error));
        argv_array_push(&cmd.args, "--all");
        argv_array_push(&cmd.args, "--reflog");
        argv_array_push(&cmd.args, "--indexed-objects");
-       if (repository_format_partial_clone)
+       if (has_promisor_remote())
                argv_array_push(&cmd.args, "--exclude-promisor-objects");
 -      if (write_bitmaps)
 +      if (write_bitmaps > 0)
                argv_array_push(&cmd.args, "--write-bitmap-index");
 +      else if (write_bitmaps < 0)
 +              argv_array_push(&cmd.args, "--write-bitmap-index-quiet");
        if (use_delta_islands)
                argv_array_push(&cmd.args, "--delta-islands");
  
        if (!names.nr && !po_args.quiet)
                printf_ln(_("Nothing new to pack."));
  
 -      close_all_packs(the_repository->objects);
 +      close_object_store(the_repository->objects);
  
        /*
         * Ok we have prepared all new packfiles.
diff --combined cache-tree.c
index c22161f987152ea18715b00815e987847786c5ff,64c285a746a886bc53bcc3ee0b1713ad2b50b3fc..0e5724fad752e9157dbcf1caba0438b42386ee3d
@@@ -5,9 -5,10 +5,10 @@@
  #include "cache-tree.h"
  #include "object-store.h"
  #include "replace-object.h"
+ #include "promisor-remote.h"
  
 -#ifndef DEBUG
 -#define DEBUG 0
 +#ifndef DEBUG_CACHE_TREE
 +#define DEBUG_CACHE_TREE 0
  #endif
  
  struct cache_tree *cache_tree(void)
@@@ -111,7 -112,7 +112,7 @@@ static int do_invalidate_path(struct ca
        int namelen;
        struct cache_tree_sub *down;
  
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
        fprintf(stderr, "cache-tree invalidate <%s>\n", path);
  #endif
  
@@@ -357,7 -358,7 +358,7 @@@ static int update_one(struct cache_tre
                }
  
                ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
-                       (repository_format_partial_clone &&
+                       (has_promisor_remote() &&
                         ce_skip_worktree(ce));
                if (is_null_oid(oid) ||
                    (!ce_missing_ok && !has_object_file(oid))) {
                strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
                strbuf_add(&buffer, oid->hash, the_hash_algo->rawsz);
  
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
                fprintf(stderr, "cache-tree update-one %o %.*s\n",
                        mode, entlen, path + baselen);
  #endif
  
        strbuf_release(&buffer);
        it->entry_count = to_invalidate ? -1 : i - *skip_count;
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
        fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
                it->entry_count, it->subtree_nr,
                oid_to_hex(&it->oid));
@@@ -462,7 -463,7 +463,7 @@@ static void write_one(struct strbuf *bu
        strbuf_add(buffer, path, pathlen);
        strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr);
  
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
        if (0 <= it->entry_count)
                fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n",
                        pathlen, path, it->entry_count, it->subtree_nr,
@@@ -536,7 -537,7 +537,7 @@@ static struct cache_tree *read_one(cons
                size -= rawsz;
        }
  
 -#if DEBUG
 +#if DEBUG_CACHE_TREE
        if (0 <= it->entry_count)
                fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n",
                        *buffer, it->entry_count, subtree_nr,
@@@ -713,7 -714,7 +714,7 @@@ static struct cache_tree *find_cache_tr
        if (!info->prev)
                return root;
        our_parent = find_cache_tree_from_traversal(root, info->prev);
 -      return cache_tree_find(our_parent, info->name.path);
 +      return cache_tree_find(our_parent, info->name);
  }
  
  int cache_tree_matches_traversal(struct cache_tree *root,
diff --combined cache.h
index b1da1ab08faad3da19657a9a5dcf5f2592c2127c,a4d7f84eebcab27e6f0108fccb2b2de81050fec0..3cbad5b603ec03ce9e4ec26be84de653e049c980
+++ b/cache.h
@@@ -43,6 -43,30 +43,6 @@@ int git_deflate_end_gently(git_zstream 
  int git_deflate(git_zstream *, int flush);
  unsigned long git_deflate_bound(git_zstream *, unsigned long);
  
 -/* The length in bytes and in hex digits of an object name (SHA-1 value). */
 -#define GIT_SHA1_RAWSZ 20
 -#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
 -/* The block size of SHA-1. */
 -#define GIT_SHA1_BLKSZ 64
 -
 -/* The length in bytes and in hex digits of an object name (SHA-256 value). */
 -#define GIT_SHA256_RAWSZ 32
 -#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
 -/* The block size of SHA-256. */
 -#define GIT_SHA256_BLKSZ 64
 -
 -/* The length in byte and in hex digits of the largest possible hash value. */
 -#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
 -#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
 -/* The largest possible block size for any supported hash. */
 -#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
 -
 -struct object_id {
 -      unsigned char hash[GIT_MAX_RAWSZ];
 -};
 -
 -#define the_hash_algo the_repository->hash_algo
 -
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
  #else
@@@ -937,8 -961,6 +937,6 @@@ extern int grafts_replace_parents
  #define GIT_REPO_VERSION 0
  #define GIT_REPO_VERSION_READ 1
  extern int repository_format_precious_objects;
- extern char *repository_format_partial_clone;
- extern const char *core_partial_clone_filter_default;
  extern int repository_format_worktree_config;
  
  /*
@@@ -1476,8 -1498,7 +1474,8 @@@ int df_name_compare(const char *name1, 
  int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
  int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
  
 -void *read_object_with_reference(const struct object_id *oid,
 +void *read_object_with_reference(struct repository *r,
 +                               const struct object_id *oid,
                                 const char *required_type,
                                 unsigned long *size,
                                 struct object_id *oid_ret);
@@@ -1736,7 -1757,6 +1734,7 @@@ void setup_pager(void)
  int pager_in_use(void);
  extern int pager_use_color;
  int term_columns(void);
 +void term_clear_line(void);
  int decimal_width(uintmax_t);
  int check_pager_config(const char *cmd);
  void prepare_pager_args(struct child_process *, const char *pager);
@@@ -1763,8 -1783,8 +1761,8 @@@ int add_files_to_cache(const char *pref
  extern int diff_auto_refresh_index;
  
  /* match-trees.c */
 -void shift_tree(const struct object_id *, const struct object_id *, struct object_id *, int);
 -void shift_tree_by(const struct object_id *, const struct object_id *, struct object_id *, const char *);
 +void shift_tree(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, int);
 +void shift_tree_by(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, const char *);
  
  /*
   * whitespace rules.
diff --combined config.c
index b61c258d6da5266a66caa55ef821de0f8e0d75aa,317b226bc88845780b637cdf82557a8763aaea8d..743e4570ee38cb6fffe7be7ed06ecdb590481016
+++ b/config.c
@@@ -19,7 -19,6 +19,7 @@@
  #include "utf8.h"
  #include "dir.h"
  #include "color.h"
 +#include "refs.h"
  
  struct config_source {
        struct config_source *prev;
@@@ -171,12 -170,6 +171,12 @@@ static int handle_path_include(const ch
        return ret;
  }
  
 +static void add_trailing_starstar_for_dir(struct strbuf *pat)
 +{
 +      if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
 +              strbuf_addstr(pat, "**");
 +}
 +
  static int prepare_include_condition_pattern(struct strbuf *pat)
  {
        struct strbuf path = STRBUF_INIT;
        } else if (!is_absolute_path(pat->buf))
                strbuf_insert(pat, 0, "**/", 3);
  
 -      if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
 -              strbuf_addstr(pat, "**");
 +      add_trailing_starstar_for_dir(pat);
  
        strbuf_release(&path);
        return prefix;
@@@ -270,26 -264,6 +270,26 @@@ done
        return ret;
  }
  
 +static int include_by_branch(const char *cond, size_t cond_len)
 +{
 +      int flags;
 +      int ret;
 +      struct strbuf pattern = STRBUF_INIT;
 +      const char *refname = !the_repository->gitdir ?
 +              NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
 +      const char *shortname;
 +
 +      if (!refname || !(flags & REF_ISSYMREF) ||
 +                      !skip_prefix(refname, "refs/heads/", &shortname))
 +              return 0;
 +
 +      strbuf_add(&pattern, cond, cond_len);
 +      add_trailing_starstar_for_dir(&pattern);
 +      ret = !wildmatch(pattern.buf, shortname, WM_PATHNAME);
 +      strbuf_release(&pattern);
 +      return ret;
 +}
 +
  static int include_condition_is_true(const struct config_options *opts,
                                     const char *cond, size_t cond_len)
  {
                return include_by_gitdir(opts, cond, cond_len, 0);
        else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
                return include_by_gitdir(opts, cond, cond_len, 1);
 +      else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
 +              return include_by_branch(cond, cond_len);
  
        /* unknown conditionals are always false */
        return 0;
@@@ -862,16 -834,22 +862,16 @@@ static int git_parse_source(config_fn_
        return error_return;
  }
  
 -static int parse_unit_factor(const char *end, uintmax_t *val)
 +static uintmax_t get_unit_factor(const char *end)
  {
        if (!*end)
                return 1;
 -      else if (!strcasecmp(end, "k")) {
 -              *val *= 1024;
 -              return 1;
 -      }
 -      else if (!strcasecmp(end, "m")) {
 -              *val *= 1024 * 1024;
 -              return 1;
 -      }
 -      else if (!strcasecmp(end, "g")) {
 -              *val *= 1024 * 1024 * 1024;
 -              return 1;
 -      }
 +      else if (!strcasecmp(end, "k"))
 +              return 1024;
 +      else if (!strcasecmp(end, "m"))
 +              return 1024 * 1024;
 +      else if (!strcasecmp(end, "g"))
 +              return 1024 * 1024 * 1024;
        return 0;
  }
  
@@@ -881,20 -859,19 +881,20 @@@ static int git_parse_signed(const char 
                char *end;
                intmax_t val;
                uintmax_t uval;
 -              uintmax_t factor = 1;
 +              uintmax_t factor;
  
                errno = 0;
                val = strtoimax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
 -              if (!parse_unit_factor(end, &factor)) {
 +              factor = get_unit_factor(end);
 +              if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
 -              uval = labs(val);
 -              uval *= factor;
 -              if (uval > max || labs(val) > uval) {
 +              uval = val < 0 ? -val : val;
 +              if (unsigned_mult_overflows(factor, uval) ||
 +                  factor * uval > max) {
                        errno = ERANGE;
                        return 0;
                }
@@@ -911,23 -888,21 +911,23 @@@ static int git_parse_unsigned(const cha
        if (value && *value) {
                char *end;
                uintmax_t val;
 -              uintmax_t oldval;
 +              uintmax_t factor;
  
                errno = 0;
                val = strtoumax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
 -              oldval = val;
 -              if (!parse_unit_factor(end, &val)) {
 +              factor = get_unit_factor(end);
 +              if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
 -              if (val > max || oldval > val) {
 +              if (unsigned_mult_overflows(factor, val) ||
 +                  factor * val > max) {
                        errno = ERANGE;
                        return 0;
                }
 +              val *= factor;
                *ret = val;
                return 1;
        }
@@@ -974,44 -949,34 +974,44 @@@ int git_parse_ssize_t(const char *value
  NORETURN
  static void die_bad_number(const char *name, const char *value)
  {
 -      const char * error_type = (errno == ERANGE)? _("out of range"):_("invalid unit");
 +      const char *error_type = (errno == ERANGE) ?
 +              N_("out of range") : N_("invalid unit");
 +      const char *bad_numeric = N_("bad numeric config value '%s' for '%s': %s");
  
        if (!value)
                value = "";
  
 +      if (!strcmp(name, "GIT_TEST_GETTEXT_POISON"))
 +              /*
 +               * We explicitly *don't* use _() here since it would
 +               * cause an infinite loop with _() needing to call
 +               * use_gettext_poison(). This is why marked up
 +               * translations with N_() above.
 +               */
 +              die(bad_numeric, value, name, error_type);
 +
        if (!(cf && cf->name))
 -              die(_("bad numeric config value '%s' for '%s': %s"),
 -                  value, name, error_type);
 +              die(_(bad_numeric), value, name, _(error_type));
  
        switch (cf->origin_type) {
        case CONFIG_ORIGIN_BLOB:
                die(_("bad numeric config value '%s' for '%s' in blob %s: %s"),
 -                  value, name, cf->name, error_type);
 +                  value, name, cf->name, _(error_type));
        case CONFIG_ORIGIN_FILE:
                die(_("bad numeric config value '%s' for '%s' in file %s: %s"),
 -                  value, name, cf->name, error_type);
 +                  value, name, cf->name, _(error_type));
        case CONFIG_ORIGIN_STDIN:
                die(_("bad numeric config value '%s' for '%s' in standard input: %s"),
 -                  value, name, error_type);
 +                  value, name, _(error_type));
        case CONFIG_ORIGIN_SUBMODULE_BLOB:
                die(_("bad numeric config value '%s' for '%s' in submodule-blob %s: %s"),
 -                  value, name, cf->name, error_type);
 +                  value, name, cf->name, _(error_type));
        case CONFIG_ORIGIN_CMDLINE:
                die(_("bad numeric config value '%s' for '%s' in command line %s: %s"),
 -                  value, name, cf->name, error_type);
 +                  value, name, cf->name, _(error_type));
        default:
                die(_("bad numeric config value '%s' for '%s' in %s: %s"),
 -                  value, name, cf->name, error_type);
 +                  value, name, cf->name, _(error_type));
        }
  }
  
@@@ -1379,11 -1344,6 +1379,6 @@@ static int git_default_core_config(cons
                return 0;
        }
  
-       if (!strcmp(var, "core.partialclonefilter")) {
-               return git_config_string(&core_partial_clone_filter_default,
-                                        var, value);
-       }
        if (!strcmp(var, "core.usereplacerefs")) {
                read_replace_refs = git_config_bool(var, value);
                return 0;
@@@ -2288,6 -2248,30 +2283,6 @@@ int git_config_get_expiry_in_days(cons
        return -1; /* thing exists but cannot be parsed */
  }
  
 -int git_config_get_untracked_cache(void)
 -{
 -      int val = -1;
 -      const char *v;
 -
 -      /* Hack for test programs like test-dump-untracked-cache */
 -      if (ignore_untracked_cache_config)
 -              return -1;
 -
 -      if (!git_config_get_maybe_bool("core.untrackedcache", &val))
 -              return val;
 -
 -      if (!git_config_get_value("core.untrackedcache", &v)) {
 -              if (!strcasecmp(v, "keep"))
 -                      return -1;
 -
 -              error(_("unknown core.untrackedCache value '%s'; "
 -                      "using 'keep' default value"), v);
 -              return -1;
 -      }
 -
 -      return -1; /* default value */
 -}
 -
  int git_config_get_split_index(void)
  {
        int val;
diff --combined connected.c
index cd9b324afa5a33be7eced6a420061905d52c211f,b0e4968fbdfb68d9043dddf6248bb7fc03e08382..971db009b327e63cf71c82b496d70604cecf6415
@@@ -5,6 -5,7 +5,7 @@@
  #include "connected.h"
  #include "transport.h"
  #include "packfile.h"
+ #include "promisor-remote.h"
  
  /*
   * If we feed all the commits we want to verify to this command
@@@ -73,14 -74,13 +74,14 @@@ int check_connected(oid_iterate_fn fn, 
        argv_array_push(&rev_list.args,"rev-list");
        argv_array_push(&rev_list.args, "--objects");
        argv_array_push(&rev_list.args, "--stdin");
-       if (repository_format_partial_clone)
+       if (has_promisor_remote())
                argv_array_push(&rev_list.args, "--exclude-promisor-objects");
        if (!opt->is_deepening_fetch) {
                argv_array_push(&rev_list.args, "--not");
                argv_array_push(&rev_list.args, "--all");
        }
        argv_array_push(&rev_list.args, "--quiet");
 +      argv_array_push(&rev_list.args, "--alternate-refs");
        if (opt->progress)
                argv_array_pushf(&rev_list.args, "--progress=%s",
                                 _("Checking connectivity"));
diff --combined diff.c
index e28b463f5757e7dd56752702db13d8195a3d39bc,249cc6eaceba6641121cbb24fc6ab61581c18bb0..6db6927369e5c18ae3cb7ea27a0ebce37fd2b357
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -25,7 -25,7 +25,7 @@@
  #include "packfile.h"
  #include "parse-options.h"
  #include "help.h"
- #include "fetch-object.h"
+ #include "promisor-remote.h"
  
  #ifdef NO_FAST_WORKING_DIRECTORY
  #define FAST_WORKING_DIRECTORY 0
@@@ -1673,10 -1673,7 +1673,10 @@@ static void emit_hunk_header(struct emi
        if (ecbdata->opt->flags.dual_color_diffed_diffs)
                strbuf_addstr(&msgbuf, reverse);
        strbuf_addstr(&msgbuf, frag);
 -      strbuf_add(&msgbuf, line, ep - line);
 +      if (ecbdata->opt->flags.suppress_hunk_header_line_count)
 +              strbuf_add(&msgbuf, atat, sizeof(atat));
 +      else
 +              strbuf_add(&msgbuf, line, ep - line);
        strbuf_addstr(&msgbuf, reset);
  
        /*
@@@ -4209,8 -4206,6 +4209,8 @@@ static void run_external_diff(const cha
        argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
        argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
  
 +      diff_free_filespec_data(one);
 +      diff_free_filespec_data(two);
        if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
                die(_("external diff died, stopping at %s"), name);
  
@@@ -6512,7 -6507,6 +6512,7 @@@ static void add_if_missing(struct repos
                           const struct diff_filespec *filespec)
  {
        if (filespec && filespec->oid_valid &&
 +          !S_ISGITLINK(filespec->mode) &&
            oid_object_info_extended(r, &filespec->oid, NULL,
                                     OBJECT_INFO_FOR_PREFETCH))
                oid_array_append(to_fetch, &filespec->oid);
  
  void diffcore_std(struct diff_options *options)
  {
-       if (options->repo == the_repository &&
-           repository_format_partial_clone) {
+       if (options->repo == the_repository && has_promisor_remote()) {
                /*
                 * Prefetch the diff pairs that are about to be flushed.
                 */
                        /*
                         * NEEDSWORK: Consider deduplicating the OIDs sent.
                         */
-                       fetch_objects(repository_format_partial_clone,
-                                     to_fetch.oid, to_fetch.nr);
+                       promisor_remote_get_direct(options->repo,
+                                                  to_fetch.oid, to_fetch.nr);
                oid_array_clear(&to_fetch);
        }
  
diff --combined packfile.c
index d98ac2287683f971a448b931262ef3d459e145d4,50aaf93bc36cc43999de4f7785a6a9c54eafbf37..1a7d69fe32a8808dd34063c48daf61e32886b47a
@@@ -16,7 -16,7 +16,8 @@@
  #include "tree.h"
  #include "object-store.h"
  #include "midx.h"
 +#include "commit-graph.h"
+ #include "promisor-remote.h"
  
  char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@@ -287,6 -287,13 +288,6 @@@ static int unuse_one_window(struct pack
        return 0;
  }
  
 -void release_pack_memory(size_t need)
 -{
 -      size_t cur = pack_mapped;
 -      while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
 -              ; /* nothing */
 -}
 -
  void close_pack_windows(struct packed_git *p)
  {
        while (p->windows) {
@@@ -330,7 -337,7 +331,7 @@@ void close_pack(struct packed_git *p
        close_pack_index(p);
  }
  
 -void close_all_packs(struct raw_object_store *o)
 +void close_object_store(struct raw_object_store *o)
  {
        struct packed_git *p;
  
                close_midx(o->multi_pack_index);
                o->multi_pack_index = NULL;
        }
 +
 +      close_commit_graph(o);
 +}
 +
 +void unlink_pack_path(const char *pack_name, int force_delete)
 +{
 +      static const char *exts[] = {".pack", ".idx", ".keep", ".bitmap", ".promisor"};
 +      int i;
 +      struct strbuf buf = STRBUF_INIT;
 +      size_t plen;
 +
 +      strbuf_addstr(&buf, pack_name);
 +      strip_suffix_mem(buf.buf, &buf.len, ".pack");
 +      plen = buf.len;
 +
 +      if (!force_delete) {
 +              strbuf_addstr(&buf, ".keep");
 +              if (!access(buf.buf, F_OK)) {
 +                      strbuf_release(&buf);
 +                      return;
 +              }
 +      }
 +
 +      for (i = 0; i < ARRAY_SIZE(exts); i++) {
 +              strbuf_setlen(&buf, plen);
 +              strbuf_addstr(&buf, exts[i]);
 +              unlink(buf.buf);
 +      }
 +
 +      strbuf_release(&buf);
  }
  
  /*
@@@ -703,12 -680,23 +704,12 @@@ void unuse_pack(struct pack_window **w_
        }
  }
  
 -static void try_to_free_pack_memory(size_t size)
 -{
 -      release_pack_memory(size);
 -}
 -
  struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
  {
 -      static int have_set_try_to_free_routine;
        struct stat st;
        size_t alloc;
        struct packed_git *p;
  
 -      if (!have_set_try_to_free_routine) {
 -              have_set_try_to_free_routine = 1;
 -              set_try_to_free_routine(try_to_free_pack_memory);
 -      }
 -
        /*
         * Make sure a corresponding .pack file exists and that
         * the index looks sane.
@@@ -1282,7 -1270,7 +1283,7 @@@ static enum object_type packed_to_objec
                if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
                        poi_stack_alloc = alloc_nr(poi_stack_nr);
                        ALLOC_ARRAY(poi_stack, poi_stack_alloc);
 -                      memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
 +                      COPY_ARRAY(poi_stack, small_poi_stack, poi_stack_nr);
                } else {
                        ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
                }
@@@ -1692,8 -1680,8 +1693,8 @@@ void *unpack_entry(struct repository *r
                    && delta_stack == small_delta_stack) {
                        delta_stack_alloc = alloc_nr(delta_stack_nr);
                        ALLOC_ARRAY(delta_stack, delta_stack_alloc);
 -                      memcpy(delta_stack, small_delta_stack,
 -                             sizeof(*delta_stack)*delta_stack_nr);
 +                      COPY_ARRAY(delta_stack, small_delta_stack,
 +                                 delta_stack_nr);
                } else {
                        ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
                }
@@@ -2132,7 -2120,7 +2133,7 @@@ int is_promisor_object(const struct obj
        static int promisor_objects_prepared;
  
        if (!promisor_objects_prepared) {
-               if (repository_format_partial_clone) {
+               if (has_promisor_remote()) {
                        for_each_packed_object(add_promisor_object,
                                               &promisor_objects,
                                               FOR_EACH_OBJECT_PROMISOR_ONLY);
diff --combined sha1-file.c
index 4895408e1ed309241a044c019e11f3addeb722a6,fe250c4b6eb704a6eb22b4242e22be5d665116cd..e85f249a5db027594d25f9882d85d1253ddbf36f
@@@ -30,8 -30,8 +30,8 @@@
  #include "mergesort.h"
  #include "quote.h"
  #include "packfile.h"
- #include "fetch-object.h"
  #include "object-store.h"
+ #include "promisor-remote.h"
  
  /* The maximum size for an object header. */
  #define MAX_HEADER_LEN 32
@@@ -743,104 -743,6 +743,104 @@@ out
        return ref_git;
  }
  
 +static void fill_alternate_refs_command(struct child_process *cmd,
 +                                      const char *repo_path)
 +{
 +      const char *value;
 +
 +      if (!git_config_get_value("core.alternateRefsCommand", &value)) {
 +              cmd->use_shell = 1;
 +
 +              argv_array_push(&cmd->args, value);
 +              argv_array_push(&cmd->args, repo_path);
 +      } else {
 +              cmd->git_cmd = 1;
 +
 +              argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
 +              argv_array_push(&cmd->args, "for-each-ref");
 +              argv_array_push(&cmd->args, "--format=%(objectname)");
 +
 +              if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
 +                      argv_array_push(&cmd->args, "--");
 +                      argv_array_split(&cmd->args, value);
 +              }
 +      }
 +
 +      cmd->env = local_repo_env;
 +      cmd->out = -1;
 +}
 +
 +static void read_alternate_refs(const char *path,
 +                              alternate_ref_fn *cb,
 +                              void *data)
 +{
 +      struct child_process cmd = CHILD_PROCESS_INIT;
 +      struct strbuf line = STRBUF_INIT;
 +      FILE *fh;
 +
 +      fill_alternate_refs_command(&cmd, path);
 +
 +      if (start_command(&cmd))
 +              return;
 +
 +      fh = xfdopen(cmd.out, "r");
 +      while (strbuf_getline_lf(&line, fh) != EOF) {
 +              struct object_id oid;
 +              const char *p;
 +
 +              if (parse_oid_hex(line.buf, &oid, &p) || *p) {
 +                      warning(_("invalid line while parsing alternate refs: %s"),
 +                              line.buf);
 +                      break;
 +              }
 +
 +              cb(&oid, data);
 +      }
 +
 +      fclose(fh);
 +      finish_command(&cmd);
 +      strbuf_release(&line);
 +}
 +
 +struct alternate_refs_data {
 +      alternate_ref_fn *fn;
 +      void *data;
 +};
 +
 +static int refs_from_alternate_cb(struct object_directory *e,
 +                                void *data)
 +{
 +      struct strbuf path = STRBUF_INIT;
 +      size_t base_len;
 +      struct alternate_refs_data *cb = data;
 +
 +      if (!strbuf_realpath(&path, e->path, 0))
 +              goto out;
 +      if (!strbuf_strip_suffix(&path, "/objects"))
 +              goto out;
 +      base_len = path.len;
 +
 +      /* Is this a git repository with refs? */
 +      strbuf_addstr(&path, "/refs");
 +      if (!is_directory(path.buf))
 +              goto out;
 +      strbuf_setlen(&path, base_len);
 +
 +      read_alternate_refs(path.buf, cb->fn, cb->data);
 +
 +out:
 +      strbuf_release(&path);
 +      return 0;
 +}
 +
 +void for_each_alternate_ref(alternate_ref_fn fn, void *data)
 +{
 +      struct alternate_refs_data cb;
 +      cb.fn = fn;
 +      cb.data = data;
 +      foreach_alt_odb(refs_from_alternate_cb, &cb);
 +}
 +
  int foreach_alt_odb(alt_odb_fn fn, void *cb)
  {
        struct object_directory *ent;
@@@ -952,8 -854,12 +952,8 @@@ void *xmmap_gently(void *start, size_t 
  
        mmap_limit_check(length);
        ret = mmap(start, length, prot, flags, fd, offset);
 -      if (ret == MAP_FAILED) {
 -              if (!length)
 -                      return NULL;
 -              release_pack_memory(length);
 -              ret = mmap(start, length, prot, flags, fd, offset);
 -      }
 +      if (ret == MAP_FAILED && !length)
 +              ret = NULL;
        return ret;
  }
  
@@@ -1471,16 -1377,17 +1471,17 @@@ int oid_object_info_extended(struct rep
                }
  
                /* Check if it is a missing object */
-               if (fetch_if_missing && repository_format_partial_clone &&
+               if (fetch_if_missing && has_promisor_remote() &&
                    !already_retried && r == the_repository &&
                    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {
                        /*
-                        * TODO Investigate having fetch_object() return
-                        * TODO error/success and stopping the music here.
-                        * TODO Pass a repository struct through fetch_object,
-                        * such that arbitrary repositories work.
+                        * TODO Investigate checking promisor_remote_get_direct()
+                        * TODO return value and stopping on error here.
+                        * TODO Pass a repository struct through
+                        * promisor_remote_get_direct(), such that arbitrary
+                        * repositories work.
                         */
-                       fetch_objects(repository_format_partial_clone, real, 1);
+                       promisor_remote_get_direct(r, real, 1);
                        already_retried = 1;
                        continue;
                }
@@@ -1599,8 -1506,7 +1600,8 @@@ void *read_object_file_extended(struct 
        return NULL;
  }
  
 -void *read_object_with_reference(const struct object_id *oid,
 +void *read_object_with_reference(struct repository *r,
 +                               const struct object_id *oid,
                                 const char *required_type_name,
                                 unsigned long *size,
                                 struct object_id *actual_oid_return)
                int ref_length = -1;
                const char *ref_type = NULL;
  
 -              buffer = read_object_file(&actual_oid, &type, &isize);
 +              buffer = repo_read_object_file(r, &actual_oid, &type, &isize);
                if (!buffer)
                        return NULL;
                if (type == required_type) {
diff --combined t/t0410-partial-clone.sh
index 33e0aa4a896575f14442e5b1a9029640d4d7ac50,2498e72a3412da9bc22f27e33df7d2380e62ad5e..d4b7e535ea134569ed8bf5643edf8d435a22bc1a
@@@ -26,7 -26,7 +26,7 @@@ promise_and_delete () 
  test_expect_success 'extensions.partialclone without filter' '
        test_create_repo server &&
        git clone --filter="blob:none" "file://$(pwd)/server" client &&
-       git -C client config --unset core.partialclonefilter &&
+       git -C client config --unset remote.origin.partialclonefilter &&
        git -C client fetch origin
  '
  
@@@ -166,8 -166,9 +166,9 @@@ test_expect_success 'fetching of missin
        # associated packfile contains the object
        ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
        test_line_count = 1 promisorlist &&
-       IDX=$(cat promisorlist | sed "s/promisor$/idx/") &&
-       git verify-pack --verbose "$IDX" | grep "$HASH"
+       IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+       git verify-pack --verbose "$IDX" >out &&
+       grep "$HASH" out
  '
  
  test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
        grep "git< fetch=.*ref-in-want" trace
  '
  
+ test_expect_success 'fetching of missing objects from another promisor remote' '
+       git clone "file://$(pwd)/server" server2 &&
+       test_commit -C server2 bar &&
+       git -C server2 repack -a -d --write-bitmap-index &&
+       HASH2=$(git -C server2 rev-parse bar) &&
+       git -C repo remote add server2 "file://$(pwd)/server2" &&
+       git -C repo config remote.server2.promisor true &&
+       git -C repo cat-file -p "$HASH2" &&
+       git -C repo fetch server2 &&
+       rm -rf repo/.git/objects/* &&
+       git -C repo cat-file -p "$HASH2" &&
+       # Ensure that the .promisor file is written, and check that its
+       # associated packfile contains the object
+       ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
+       test_line_count = 1 promisorlist &&
+       IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+       git verify-pack --verbose "$IDX" >out &&
+       grep "$HASH2" out
+ '
+ test_expect_success 'fetching of missing objects configures a promisor remote' '
+       git clone "file://$(pwd)/server" server3 &&
+       test_commit -C server3 baz &&
+       git -C server3 repack -a -d --write-bitmap-index &&
+       HASH3=$(git -C server3 rev-parse baz) &&
+       git -C server3 config uploadpack.allowfilter 1 &&
+       rm repo/.git/objects/pack/pack-*.promisor &&
+       git -C repo remote add server3 "file://$(pwd)/server3" &&
+       git -C repo fetch --filter="blob:none" server3 $HASH3 &&
+       test_cmp_config -C repo true remote.server3.promisor &&
+       # Ensure that the .promisor file is written, and check that its
+       # associated packfile contains the object
+       ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
+       test_line_count = 1 promisorlist &&
+       IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+       git verify-pack --verbose "$IDX" >out &&
+       grep "$HASH3" out
+ '
  test_expect_success 'fetching of missing blobs works' '
-       rm -rf server repo &&
+       rm -rf server server2 repo &&
+       rm -rf server server3 repo &&
        test_create_repo server &&
        test_commit -C server foo &&
        git -C server repack -a -d --write-bitmap-index &&
@@@ -234,7 -282,7 +282,7 @@@ test_expect_success 'rev-list stops tra
  
        git -C repo config core.repositoryformatversion 1 &&
        git -C repo config extensions.partialclone "arbitrary string" &&
 -      GIT_TEST_COMMIT_GRAPH=0 git -C repo rev-list --exclude-promisor-objects --objects bar >out &&
 +      GIT_TEST_COMMIT_GRAPH=0 git -C repo -c core.commitGraph=false rev-list --exclude-promisor-objects --objects bar >out &&
        grep $(git -C repo rev-parse bar) out &&
        ! grep $FOO out
  '
@@@ -514,11 -562,9 +562,12 @@@ test_expect_success 'fetching of missin
        # associated packfile contains the object
        ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
        test_line_count = 1 promisorlist &&
-       IDX=$(cat promisorlist | sed "s/promisor$/idx/") &&
-       git verify-pack --verbose "$IDX" | grep "$HASH"
+       IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+       git verify-pack --verbose "$IDX" >out &&
+       grep "$HASH" out
  '
  
 +# DO NOT add non-httpd-specific tests here, because the last part of this
 +# test script is only executed when httpd is available and enabled.
 +
  test_done
diff --combined t/t5601-clone.sh
index 4a3b901f067c2cb024247f2dc9299a6215939019,078cf48dd610c547e71afb3b2488927afa87903a..2e94354e5b11b3dfcf5104e4911cb37003a1c03e
@@@ -654,7 -654,8 +654,8 @@@ partial_clone () 
        git -C client fsck &&
  
        # Ensure that unneeded blobs are not inadvertently fetched.
-       test_config -C client extensions.partialclone "not a remote" &&
+       test_config -C client remote.origin.promisor "false" &&
+       git -C client config --unset remote.origin.partialclonefilter &&
        test_must_fail git -C client cat-file -e "$HASH1" &&
  
        # But this blob was fetched, because clone performs an initial checkout
@@@ -739,7 -740,4 +740,7 @@@ test_expect_success 'partial clone usin
        partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
  '
  
 +# DO NOT add non-httpd-specific tests here, because the last part of this
 +# test script is only executed when httpd is available and enabled.
 +
  test_done
diff --combined t/t5616-partial-clone.sh
index 565254558f362397270bcf13bd3566993e1b0f50,8ae7ba9c950f5475c920ea11797a3d15cb2ccc19..73cd95812f143bbea40df9242eed64d78e0bf53f
@@@ -42,8 -42,8 +42,8 @@@ test_expect_success 'do partial clone 1
  
        test_cmp expect_1.oids observed.oids &&
        test "$(git -C pc1 config --local core.repositoryformatversion)" = "1" &&
-       test "$(git -C pc1 config --local extensions.partialclone)" = "origin" &&
-       test "$(git -C pc1 config --local core.partialclonefilter)" = "blob:none"
+       test "$(git -C pc1 config --local remote.origin.promisor)" = "true" &&
+       test "$(git -C pc1 config --local remote.origin.partialclonefilter)" = "blob:none"
  '
  
  # checkout master to force dynamic object fetch of blobs at HEAD.
@@@ -417,7 -417,4 +417,7 @@@ test_expect_success 'tolerate server se
        ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
  '
  
 +# DO NOT add non-httpd-specific tests here, because the last part of this
 +# test script is only executed when httpd is available and enabled.
 +
  test_done
diff --combined unpack-trees.c
index 50f257bd5c768ae87f8e526c587fa11afad98ad6,aebd865ef6bd270f9a99b2043a51f476c1247c2d..9c25126aecaabc8f0e3d0841ee1aa959acdf8dbd
@@@ -16,7 -16,7 +16,7 @@@
  #include "submodule-config.h"
  #include "fsmonitor.h"
  #include "object-store.h"
- #include "fetch-object.h"
+ #include "promisor-remote.h"
  
  /*
   * Error messages expected by scripts out of plumbing commands such as
@@@ -315,7 -315,7 +315,7 @@@ static struct progress *get_progress(st
                        total++;
        }
  
 -      return start_delayed_progress(_("Checking out files"), total);
 +      return start_delayed_progress(_("Updating files"), total);
  }
  
  static void setup_collided_checkout_detection(struct checkout *state,
@@@ -400,7 -400,7 +400,7 @@@ static int check_updates(struct unpack_
                load_gitmodules_file(index, &state);
  
        enable_delayed_checkout(&state);
-       if (repository_format_partial_clone && o->update && !o->dry_run) {
+       if (has_promisor_remote() && o->update && !o->dry_run) {
                /*
                 * Prefetch the objects that are to be checked out in the loop
                 * below.
                        oid_array_append(&to_fetch, &ce->oid);
                }
                if (to_fetch.nr)
-                       fetch_objects(repository_format_partial_clone,
-                                     to_fetch.oid, to_fetch.nr);
+                       promisor_remote_get_direct(the_repository,
+                                                  to_fetch.oid, to_fetch.nr);
                oid_array_clear(&to_fetch);
        }
        for (i = 0; i < index->cache_nr; i++) {
@@@ -632,7 -632,7 +632,7 @@@ static int unpack_index_entry(struct ca
        return ret;
  }
  
 -static int find_cache_pos(struct traverse_info *, const struct name_entry *);
 +static int find_cache_pos(struct traverse_info *, const char *p, size_t len);
  
  static void restore_cache_bottom(struct traverse_info *info, int bottom)
  {
@@@ -651,7 -651,7 +651,7 @@@ static int switch_cache_bottom(struct t
        if (o->diff_index_cached)
                return 0;
        ret = o->cache_bottom;
 -      pos = find_cache_pos(info->prev, &info->name);
 +      pos = find_cache_pos(info->prev, info->name, info->namelen);
  
        if (pos < -1)
                o->cache_bottom = -2 - pos;
@@@ -686,19 -686,21 +686,19 @@@ static int index_pos_by_traverse_info(s
                                      struct traverse_info *info)
  {
        struct unpack_trees_options *o = info->data;
 -      int len = traverse_path_len(info, names);
 -      char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */);
 +      struct strbuf name = STRBUF_INIT;
        int pos;
  
 -      make_traverse_path(name, info, names);
 -      name[len++] = '/';
 -      name[len] = '\0';
 -      pos = index_name_pos(o->src_index, name, len);
 +      strbuf_make_traverse_path(&name, info, names->path, names->pathlen);
 +      strbuf_addch(&name, '/');
 +      pos = index_name_pos(o->src_index, name.buf, name.len);
        if (pos >= 0)
                BUG("This is a directory and should not exist in index");
        pos = -pos - 1;
 -      if (!starts_with(o->src_index->cache[pos]->name, name) ||
 -          (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name)))
 +      if (!starts_with(o->src_index->cache[pos]->name, name.buf) ||
 +          (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name.buf)))
                BUG("pos must point at the first entry in this directory");
 -      free(name);
 +      strbuf_release(&name);
        return pos;
  }
  
@@@ -809,10 -811,8 +809,10 @@@ static int traverse_trees_recursive(in
        newinfo = *info;
        newinfo.prev = info;
        newinfo.pathspec = info->pathspec;
 -      newinfo.name = *p;
 -      newinfo.pathlen += tree_entry_len(p) + 1;
 +      newinfo.name = p->path;
 +      newinfo.namelen = p->pathlen;
 +      newinfo.mode = p->mode;
 +      newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);
        newinfo.df_conflicts |= df_conflicts;
  
        /*
                        const struct object_id *oid = NULL;
                        if (dirmask & 1)
                                oid = &names[i].oid;
 -                      buf[nr_buf++] = fill_tree_descriptor(t + i, oid);
 +                      buf[nr_buf++] = fill_tree_descriptor(the_repository, t + i, oid);
                }
        }
  
   * itself - the caller needs to do the final check for the cache
   * entry having more data at the end!
   */
 -static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
 +static int do_compare_entry_piecewise(const struct cache_entry *ce,
 +                                    const struct traverse_info *info,
 +                                    const char *name, size_t namelen,
 +                                    unsigned mode)
  {
 -      int len, pathlen, ce_len;
 +      int pathlen, ce_len;
        const char *ce_name;
  
        if (info->prev) {
                int cmp = do_compare_entry_piecewise(ce, info->prev,
 -                                                   &info->name);
 +                                                   info->name, info->namelen,
 +                                                   info->mode);
                if (cmp)
                        return cmp;
        }
        ce_len -= pathlen;
        ce_name = ce->name + pathlen;
  
 -      len = tree_entry_len(n);
 -      return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
 +      return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
  }
  
  static int do_compare_entry(const struct cache_entry *ce,
                            const struct traverse_info *info,
 -                          const struct name_entry *n)
 +                          const char *name, size_t namelen,
 +                          unsigned mode)
  {
 -      int len, pathlen, ce_len;
 +      int pathlen, ce_len;
        const char *ce_name;
        int cmp;
  
         * it is quicker to use the precomputed version.
         */
        if (!info->traverse_path)
 -              return do_compare_entry_piecewise(ce, info, n);
 +              return do_compare_entry_piecewise(ce, info, name, namelen, mode);
  
        cmp = strncmp(ce->name, info->traverse_path, info->pathlen);
        if (cmp)
        ce_len -= pathlen;
        ce_name = ce->name + pathlen;
  
 -      len = tree_entry_len(n);
 -      return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
 +      return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
  }
  
  static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
  {
 -      int cmp = do_compare_entry(ce, info, n);
 +      int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);
        if (cmp)
                return cmp;
  
         * Even if the beginning compared identically, the ce should
         * compare as bigger than a directory leading up to it!
         */
 -      return ce_namelen(ce) > traverse_path_len(info, n);
 +      return ce_namelen(ce) > traverse_path_len(info, tree_entry_len(n));
  }
  
  static int ce_in_traverse_path(const struct cache_entry *ce,
  {
        if (!info->prev)
                return 1;
 -      if (do_compare_entry(ce, info->prev, &info->name))
 +      if (do_compare_entry(ce, info->prev,
 +                           info->name, info->namelen, info->mode))
                return 0;
        /*
         * If ce (blob) is the same name as the path (which is a tree
@@@ -958,7 -954,7 +958,7 @@@ static struct cache_entry *create_ce_en
        struct index_state *istate,
        int is_transient)
  {
 -      int len = traverse_path_len(info, n);
 +      size_t len = traverse_path_len(info, tree_entry_len(n));
        struct cache_entry *ce =
                is_transient ?
                make_empty_transient_cache_entry(len) :
        ce->ce_flags = create_ce_flags(stage);
        ce->ce_namelen = len;
        oidcpy(&ce->oid, &n->oid);
 -      make_traverse_path(ce->name, info, n);
 +      /* len+1 because the cache_entry allocates space for NUL */
 +      make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen);
  
        return ce;
  }
@@@ -1062,12 -1057,13 +1062,12 @@@ static int unpack_failed(struct unpack_
   * the directory.
   */
  static int find_cache_pos(struct traverse_info *info,
 -                        const struct name_entry *p)
 +                        const char *p, size_t p_len)
  {
        int pos;
        struct unpack_trees_options *o = info->data;
        struct index_state *index = o->src_index;
        int pfxlen = info->pathlen;
 -      int p_len = tree_entry_len(p);
  
        for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
                const struct cache_entry *ce = index->cache[pos];
                        ce_len = ce_slash - ce_name;
                else
                        ce_len = ce_namelen(ce) - pfxlen;
 -              cmp = name_compare(p->path, p_len, ce_name, ce_len);
 +              cmp = name_compare(p, p_len, ce_name, ce_len);
                /*
                 * Exact match; if we have a directory we need to
                 * delay returning it.
                 * E.g.  ce_name == "t-i", and p->path == "t"; we may
                 * have "t/a" in the index.
                 */
 -              if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) &&
 +              if (p_len < ce_len && !memcmp(ce_name, p, p_len) &&
                    ce_name[p_len] < '/')
                        continue; /* keep looking */
                break;
  static struct cache_entry *find_cache_entry(struct traverse_info *info,
                                            const struct name_entry *p)
  {
 -      int pos = find_cache_pos(info, p);
 +      int pos = find_cache_pos(info, p->path, p->pathlen);
        struct unpack_trees_options *o = info->data;
  
        if (0 <= pos)
@@@ -1142,10 -1138,10 +1142,10 @@@ static void debug_path(struct traverse_
  {
        if (info->prev) {
                debug_path(info->prev);
 -              if (*info->prev->name.path)
 +              if (*info->prev->name)
                        putchar('/');
        }
 -      printf("%s", info->name.path);
 +      printf("%s", info->name);
  }
  
  static void debug_name_entry(int i, struct name_entry *n)