Merge branch 'jh/fsck-promisors' into next
authorJunio C Hamano <gitster@pobox.com>
Tue, 23 Jan 2018 21:49:51 +0000 (13:49 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 23 Jan 2018 21:49:51 +0000 (13:49 -0800)
In preparation for implementing narrow/partial clone, the machinery
for checking object connectivity used by gc and fsck has been
taught that a missing object is OK when it is referenced by a
packfile specially marked as coming from trusted repository that
promises to make them available on-demand and lazily.

* jh/fsck-promisors:
gc: do not repack promisor packfiles
rev-list: support termination at promisor objects
sha1_file: support lazily fetching missing objects
introduce fetch-object: fetch one promisor object
index-pack: refactor writing of .keep files
fsck: support promisor objects as CLI argument
fsck: support referenced promisor objects
fsck: support refs pointing to promisor objects
fsck: introduce partialclone extension
extension.partialclone: introduce partial clone extension

18 files changed:
1  2 
Documentation/rev-list-options.txt
Makefile
builtin/fsck.c
builtin/index-pack.c
builtin/pack-objects.c
builtin/prune.c
builtin/rev-list.c
cache.h
environment.c
fetch-pack.c
list-objects.c
object.c
revision.c
revision.h
setup.c
sha1_file.c
transport.c
transport.h
index 22f5c9b43dd01c02710e44e36ee6a9dd2afc9b60,0ce8ccdd43611e4f5c74fe7c33d2fc6e4ea2a328..7b273635de2b5bf3e3ba6ade8bcca3068a216395
@@@ -686,11 -686,6 +686,11 @@@ ifdef::git-rev-list[
        all object IDs which I need to download if I have the commit
        object _bar_ but not _foo_''.
  
 +--in-commit-order::
 +      Print tree and blob ids in order of the commits. The tree
 +      and blob ids are printed after they are first referenced
 +      by a commit.
 +
  --objects-edge::
        Similar to `--objects`, but also print the IDs of excluded
        commits prefixed with a ``-'' character.  This is used by
@@@ -750,10 -745,21 +750,21 @@@ The form '--missing=allow-any' will all
  if a missing object is encountered.  Missing objects will silently be
  omitted from the results.
  +
+ The form '--missing=allow-promisor' is like 'allow-any', but will only
+ allow object traversal to continue for EXPECTED promisor missing objects.
+ Unexpected missing objects will raise an error.
+ +
  The form '--missing=print' is like 'allow-any', but will also print a
  list of the missing objects.  Object IDs are prefixed with a ``?'' character.
  endif::git-rev-list[]
  
+ --exclude-promisor-objects::
+       (For internal use only.)  Prefilter object traversal at
+       promisor boundary.  This is used with partial clone.  This is
+       stronger than `--missing=allow-promisor` because it limits the
+       traversal, rather than just silencing errors about missing
+       objects.
  --no-walk[=(sorted|unsorted)]::
        Only show the given commits, but do not traverse their ancestors.
        This has no effect if a range is specified. If the argument
diff --combined Makefile
index 37e02cec1b614097b418e73f2d07bf7e830ed3c1,795e0c7cb581a66b24b64ed0beec87e408027b74..eb74e6cf3c4b25ee639c192cefcddf8f235ffc0d
+++ b/Makefile
@@@ -23,6 -23,7 +23,6 @@@ all:
  # it at all).
  #
  # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 -# This also implies BLK_SHA1.
  #
  # Define USE_LIBPCRE if you have and want to use libpcre. Various
  # commands such as log and grep offer runtime options to use
  #
  # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
  #
 -# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
 -# MakeMaker (e.g. using ActiveState under Cygwin).
 -#
  # Define NO_PERL if you do not want Perl scripts or libraries at all.
  #
  # Define PYTHON_PATH to the path of your Python binary (often /usr/bin/python
  #
  # to say "export LESS=FRX (and LV=-c) if the environment variable
  # LESS (and LV) is not set, respectively".
 +#
 +# Define TEST_SHELL_PATH if you want to use a shell besides SHELL_PATH for
 +# running the test scripts (e.g., bash has better support for "set -x"
 +# tracing).
 +#
 +# When cross-compiling, define HOST_CPU as the canonical name of the CPU on
 +# which the built Git will run (for instance "x86_64").
  
  GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -476,7 -473,6 +476,7 @@@ gitexecdir = libexec/git-cor
  mergetoolsdir = $(gitexecdir)/mergetools
  sharedir = $(prefix)/share
  gitwebdir = $(sharedir)/gitweb
 +perllibdir = $(sharedir)/perl5
  localedir = $(sharedir)/locale
  template_dir = share/git-core/templates
  htmldir = $(prefix)/share/doc/git-doc
@@@ -490,7 -486,7 +490,7 @@@ mandir_relative = $(patsubst $(prefix)/
  infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
  htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
  
 -export prefix bindir sharedir sysconfdir gitwebdir localedir
 +export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir
  
  CC = cc
  AR = ar
@@@ -650,12 -646,9 +650,12 @@@ TEST_PROGRAMS_NEED_X += test-ctyp
  TEST_PROGRAMS_NEED_X += test-config
  TEST_PROGRAMS_NEED_X += test-date
  TEST_PROGRAMS_NEED_X += test-delta
 +TEST_PROGRAMS_NEED_X += test-drop-caches
  TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 +TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
  TEST_PROGRAMS_NEED_X += test-dump-split-index
  TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 +TEST_PROGRAMS_NEED_X += test-example-decorate
  TEST_PROGRAMS_NEED_X += test-fake-ssh
  TEST_PROGRAMS_NEED_X += test-genrandom
  TEST_PROGRAMS_NEED_X += test-hashmap
@@@ -734,8 -727,6 +734,8 @@@ endi
  export PERL_PATH
  export PYTHON_PATH
  
 +TEST_SHELL_PATH = $(SHELL_PATH)
 +
  LIB_FILE = libgit.a
  XDIFF_LIB = xdiff/lib.a
  VCSSVN_LIB = vcs-svn/lib.a
@@@ -766,7 -757,6 +766,7 @@@ LIB_OBJS += branch.
  LIB_OBJS += bulk-checkin.o
  LIB_OBJS += bundle.o
  LIB_OBJS += cache-tree.o
 +LIB_OBJS += checkout.o
  LIB_OBJS += color.o
  LIB_OBJS += column.o
  LIB_OBJS += combine-diff.o
@@@ -802,9 -792,9 +802,10 @@@ LIB_OBJS += ewah/ewah_bitmap.
  LIB_OBJS += ewah/ewah_io.o
  LIB_OBJS += ewah/ewah_rlw.o
  LIB_OBJS += exec_cmd.o
+ LIB_OBJS += fetch-object.o
  LIB_OBJS += fetch-pack.o
  LIB_OBJS += fsck.o
 +LIB_OBJS += fsmonitor.o
  LIB_OBJS += gettext.o
  LIB_OBJS += gpg-interface.o
  LIB_OBJS += graph.o
@@@ -859,7 -849,6 +860,7 @@@ LIB_OBJS += pretty.
  LIB_OBJS += prio-queue.o
  LIB_OBJS += progress.o
  LIB_OBJS += prompt.o
 +LIB_OBJS += protocol.o
  LIB_OBJS += quote.o
  LIB_OBJS += reachable.o
  LIB_OBJS += read-cache.o
  BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
  endif
  
 +ifeq (,$(HOST_CPU))
 +      BASIC_CFLAGS += -DGIT_HOST_CPU="\"$(firstword $(subst -, ,$(uname_M)))\""
 +else
 +      BASIC_CFLAGS += -DGIT_HOST_CPU="\"$(HOST_CPU)\""
 +endif
 +
  ifneq (,$(INLINE))
        BASIC_CFLAGS += -Dinline=$(INLINE)
  endif
@@@ -1272,6 -1255,7 +1273,6 @@@ ifndef NO_OPENSS
        endif
  else
        BASIC_CFLAGS += -DNO_OPENSSL
 -      BLK_SHA1 = 1
        OPENSSL_LIBSSL =
  endif
  ifdef NO_OPENSSL
@@@ -1541,6 -1525,9 +1542,6 @@@ ifdef SHA1_MAX_BLOCK_SIZ
        LIB_OBJS += compat/sha1-chunked.o
        BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
  endif
 -ifdef NO_PERL_MAKEMAKER
 -      export NO_PERL_MAKEMAKER
 -endif
  ifdef NO_HSTRERROR
        COMPAT_CFLAGS += -DNO_HSTRERROR
        COMPAT_OBJS += compat/hstrerror.o
@@@ -1727,10 -1714,8 +1728,10 @@@ ETC_GITATTRIBUTES_SQ = $(subst ','\'',$
  DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
  bindir_SQ = $(subst ','\'',$(bindir))
  bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
 +mandir_SQ = $(subst ','\'',$(mandir))
  mandir_relative_SQ = $(subst ','\'',$(mandir_relative))
  infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
 +perllibdir_SQ = $(subst ','\'',$(perllibdir))
  localedir_SQ = $(subst ','\'',$(localedir))
  gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
  template_dir_SQ = $(subst ','\'',$(template_dir))
@@@ -1739,7 -1724,6 +1740,7 @@@ prefix_SQ = $(subst ','\'',$(prefix)
  gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
  
  SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 +TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
  PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
  PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
  TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
@@@ -1840,6 -1824,9 +1841,6 @@@ all:
  ifndef NO_TCLTK
        $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
        $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
 -endif
 -ifndef NO_PERL
 -      $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
  endif
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
  
@@@ -1905,9 -1892,7 +1906,9 @@@ builtin/help.sp builtin/help.s builtin/
  version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
  version.sp version.s version.o: EXTRA_CPPFLAGS = \
        '-DGIT_VERSION="$(GIT_VERSION)"' \
 -      '-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)'
 +      '-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)' \
 +      '-DGIT_BUILT_FROM_COMMIT="$(shell GIT_CEILING_DIRECTORIES=\"$(CURDIR)/..\" \
 +              git rev-parse -q --verify HEAD || :)"'
  
  $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
@@@ -1922,8 -1907,7 +1923,8 @@@ common-cmds.h: $(wildcard Documentation
  
  SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
 -      $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV)
 +      $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV):\
 +      $(perllibdir_SQ)
  define cmd_munge_script
  $(RM) $@ $@+ && \
  sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@@ -1959,20 -1943,30 +1960,20 @@@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEF
  
  git.res: git.rc GIT-VERSION-FILE
        $(QUIET_RC)$(RC) \
 -        $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
 +        $(join -DMAJOR= -DMINOR= -DMICRO= -DPATCHLEVEL=, $(wordlist 1, 4, \
 +          $(shell echo $(GIT_VERSION) 0 0 0 0 | tr '.a-zA-Z-' ' '))) \
          -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@
  
  # This makes sure we depend on the NO_PERL setting itself.
  $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
  
  ifndef NO_PERL
 -$(SCRIPT_PERL_GEN): perl/perl.mak
 +$(SCRIPT_PERL_GEN):
  
 -perl/perl.mak: perl/PM.stamp
 -
 -perl/PM.stamp: FORCE
 -      @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
 -      $(PERL_PATH) -V >>$@+ && \
 -      { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
 -      $(RM) $@+
 -
 -perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
 -      $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
 -
 -PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
 -$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
 +PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ):$(perllibdir_SQ)
 +$(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
 -      INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
 +      INSTLIBDIR='$(perllibdir_SQ)' && \
        INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
        INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
        sed -e '1{' \
@@@ -2296,21 -2290,6 +2297,21 @@@ endi
  po/build/locale/%/LC_MESSAGES/git.mo: po/%.po
        $(QUIET_MSGFMT)mkdir -p $(dir $@) && $(MSGFMT) -o $@ $<
  
 +LIB_PERL := $(wildcard perl/Git.pm perl/Git/*.pm perl/Git/*/*.pm perl/Git/*/*/*.pm)
 +LIB_PERL_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_PERL))
 +
 +ifndef NO_PERL
 +all:: $(LIB_PERL_GEN)
 +endif
 +
 +perl/build/lib/%.pm: perl/%.pm
 +      $(QUIET_GEN)mkdir -p $(dir $@) && \
 +      sed -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' < $< > $@
 +
 +perl/build/man/man3/Git.3pm: perl/Git.pm
 +      $(QUIET_GEN)mkdir -p $(dir $@) && \
 +      pod2man $< $@
 +
  FIND_SOURCE_FILES = ( \
        git ls-files \
                '*.[hcS]' \
@@@ -2374,7 -2353,6 +2375,7 @@@ GIT-LDFLAGS: FORC
  # and the first level quoting from the shell that runs "echo".
  GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@+
 +      @echo TEST_SHELL_PATH=\''$(subst ','\'',$(TEST_SHELL_PATH_SQ))'\' >>$@+
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@+
        @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@+
        @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@+
@@@ -2571,9 -2549,7 +2572,9 @@@ ifndef NO_GETTEX
        (cd '$(DESTDIR_SQ)$(localedir_SQ)' && umask 022 && $(TAR) xof -)
  endif
  ifndef NO_PERL
 -      $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 +      $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perllibdir_SQ)'
 +      (cd perl/build/lib && $(TAR) cf - .) | \
 +      (cd '$(DESTDIR_SQ)$(perllibdir_SQ)' && umask 022 && $(TAR) xof -)
        $(MAKE) -C gitweb install
  endif
  ifndef NO_TCLTK
@@@ -2623,17 -2599,12 +2624,17 @@@ endi
  install-gitweb:
        $(MAKE) -C gitweb install
  
 -install-doc:
 +install-doc: install-man-perl
        $(MAKE) -C Documentation install
  
 -install-man:
 +install-man: install-man-perl
        $(MAKE) -C Documentation install-man
  
 +install-man-perl: perl/build/man/man3/Git.3pm
 +      $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mandir_SQ)/man3'
 +      (cd perl/build/man/man3 && $(TAR) cf - .) | \
 +      (cd '$(DESTDIR_SQ)$(mandir_SQ)/man3' && umask 022 && $(TAR) xof -)
 +
  install-html:
        $(MAKE) -C Documentation install-html
  
@@@ -2725,7 -2696,7 +2726,7 @@@ clean: profile-clean coverage-clea
        $(MAKE) -C Documentation/ clean
  ifndef NO_PERL
        $(MAKE) -C gitweb clean
 -      $(MAKE) -C perl clean
 +      $(RM) -r perl/build/
  endif
        $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
diff --combined builtin/fsck.c
index 04846d46f91d6cde14d379da8e0e3dd458c08dc5,3b76c0ef0f4beddd2af5500aa435ea67ebf573e7..7a8a679d4f3d0cf6d95991927947cbd00359868c
@@@ -149,6 -149,15 +149,15 @@@ static int mark_object(struct object *o
        if (obj->flags & REACHABLE)
                return 0;
        obj->flags |= REACHABLE;
+       if (is_promisor_object(&obj->oid))
+               /*
+                * Further recursion does not need to be performed on this
+                * object since it is a promisor object (so it does not need to
+                * be added to "pending").
+                */
+               return 0;
        if (!(obj->flags & HAS_OBJ)) {
                if (parent && !has_object_file(&obj->oid)) {
                        printf("broken link from %7s %s\n",
@@@ -208,6 -217,8 +217,8 @@@ static void check_reachable_object(stru
         * do a full fsck
         */
        if (!(obj->flags & HAS_OBJ)) {
+               if (is_promisor_object(&obj->oid))
+                       return;
                if (has_sha1_pack(obj->oid.hash))
                        return; /* it is in pack - forget about it */
                printf("missing %s %s\n", printable_type(obj),
@@@ -398,7 -409,7 +409,7 @@@ static void fsck_handle_reflog_oid(cons
                                        xstrfmt("%s@{%"PRItime"}", refname, timestamp));
                        obj->flags |= USED;
                        mark_object_reachable(obj);
-               } else {
+               } else if (!is_promisor_object(oid)) {
                        error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
@@@ -434,6 -445,14 +445,14 @@@ static int fsck_handle_ref(const char *
  
        obj = parse_object(oid);
        if (!obj) {
+               if (is_promisor_object(oid)) {
+                       /*
+                        * Increment default_refs anyway, because this is a
+                        * valid ref.
+                        */
+                        default_refs++;
+                        return 0;
+               }
                error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
                errors_found |= ERROR_REACHABLE;
                /* We'll continue with the rest despite the error.. */
@@@ -555,7 -574,7 +574,7 @@@ static int fsck_head_link(void
        if (verbose)
                fprintf(stderr, "Checking HEAD link\n");
  
 -      head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, NULL);
 +      head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL);
        if (!head_points_at) {
                errors_found |= ERROR_REFS;
                return error("Invalid HEAD");
@@@ -659,6 -678,9 +678,9 @@@ int cmd_fsck(int argc, const char **arg
        int i;
        struct alternate_object_database *alt;
  
+       /* fsck knows how to handle missing promisor objects */
+       fetch_if_missing = 0;
        errors_found = 0;
        check_replace_refs = 0;
  
                        struct object *obj = lookup_object(oid.hash);
  
                        if (!obj || !(obj->flags & HAS_OBJ)) {
+                               if (is_promisor_object(&oid))
+                                       continue;
                                error("%s: object missing", oid_to_hex(&oid));
                                errors_found |= ERROR_OBJECT;
                                continue;
  
        if (keep_cache_objects) {
                verify_index_checksum = 1;
 +              verify_ce_order = 1;
                read_cache();
                for (i = 0; i < active_nr; i++) {
                        unsigned int mode;
diff --combined builtin/index-pack.c
index 4c51aec81f374d52e7c6a1afa73ab9dea3cf40b8,a0a35e69c0a044fa40a2481c22ef56a766b23e41..5ebd370c56d2611494868ad1cf5957b5e9ea636e
@@@ -1389,15 -1389,60 +1389,60 @@@ static void fix_unresolved_deltas(struc
        free(sorted_by_pos);
  }
  
+ static const char *derive_filename(const char *pack_name, const char *suffix,
+                                  struct strbuf *buf)
+ {
+       size_t len;
+       if (!strip_suffix(pack_name, ".pack", &len))
+               die(_("packfile name '%s' does not end with '.pack'"),
+                   pack_name);
+       strbuf_add(buf, pack_name, len);
+       strbuf_addch(buf, '.');
+       strbuf_addstr(buf, suffix);
+       return buf->buf;
+ }
+ static void write_special_file(const char *suffix, const char *msg,
+                              const char *pack_name, const unsigned char *sha1,
+                              const char **report)
+ {
+       struct strbuf name_buf = STRBUF_INIT;
+       const char *filename;
+       int fd;
+       int msg_len = strlen(msg);
+       if (pack_name)
+               filename = derive_filename(pack_name, suffix, &name_buf);
+       else
+               filename = odb_pack_name(&name_buf, sha1, suffix);
+       fd = odb_pack_keep(filename);
+       if (fd < 0) {
+               if (errno != EEXIST)
+                       die_errno(_("cannot write %s file '%s'"),
+                                 suffix, filename);
+       } else {
+               if (msg_len > 0) {
+                       write_or_die(fd, msg, msg_len);
+                       write_or_die(fd, "\n", 1);
+               }
+               if (close(fd) != 0)
+                       die_errno(_("cannot close written %s file '%s'"),
+                                 suffix, filename);
+               if (report)
+                       *report = suffix;
+       }
+       strbuf_release(&name_buf);
+ }
  static void final(const char *final_pack_name, const char *curr_pack_name,
                  const char *final_index_name, const char *curr_index_name,
-                 const char *keep_name, const char *keep_msg,
+                 const char *keep_msg, const char *promisor_msg,
                  unsigned char *sha1)
  {
        const char *report = "pack";
        struct strbuf pack_name = STRBUF_INIT;
        struct strbuf index_name = STRBUF_INIT;
-       struct strbuf keep_name_buf = STRBUF_INIT;
        int err;
  
        if (!from_stdin) {
                        die_errno(_("error while closing pack file"));
        }
  
-       if (keep_msg) {
-               int keep_fd, keep_msg_len = strlen(keep_msg);
-               if (!keep_name)
-                       keep_name = odb_pack_name(&keep_name_buf, sha1, "keep");
-               keep_fd = odb_pack_keep(keep_name);
-               if (keep_fd < 0) {
-                       if (errno != EEXIST)
-                               die_errno(_("cannot write keep file '%s'"),
-                                         keep_name);
-               } else {
-                       if (keep_msg_len > 0) {
-                               write_or_die(keep_fd, keep_msg, keep_msg_len);
-                               write_or_die(keep_fd, "\n", 1);
-                       }
-                       if (close(keep_fd) != 0)
-                               die_errno(_("cannot close written keep file '%s'"),
-                                         keep_name);
-                       report = "keep";
-               }
-       }
+       if (keep_msg)
+               write_special_file("keep", keep_msg, final_pack_name, sha1,
+                                  &report);
+       if (promisor_msg)
+               write_special_file("promisor", promisor_msg, final_pack_name,
+                                  sha1, NULL);
  
        if (final_pack_name != curr_pack_name) {
                if (!final_pack_name)
  
        strbuf_release(&index_name);
        strbuf_release(&pack_name);
-       strbuf_release(&keep_name_buf);
  }
  
  static int git_index_pack_config(const char *k, const char *v, void *cb)
@@@ -1615,32 -1643,26 +1643,26 @@@ static void show_pack_info(int stat_onl
        }
  }
  
- static const char *derive_filename(const char *pack_name, const char *suffix,
-                                  struct strbuf *buf)
- {
-       size_t len;
-       if (!strip_suffix(pack_name, ".pack", &len))
-               die(_("packfile name '%s' does not end with '.pack'"),
-                   pack_name);
-       strbuf_add(buf, pack_name, len);
-       strbuf_addstr(buf, suffix);
-       return buf->buf;
- }
  int cmd_index_pack(int argc, const char **argv, const char *prefix)
  {
        int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
        const char *curr_index;
        const char *index_name = NULL, *pack_name = NULL;
-       const char *keep_name = NULL, *keep_msg = NULL;
-       struct strbuf index_name_buf = STRBUF_INIT,
-                     keep_name_buf = STRBUF_INIT;
+       const char *keep_msg = NULL;
+       const char *promisor_msg = NULL;
+       struct strbuf index_name_buf = STRBUF_INIT;
        struct pack_idx_entry **idx_objects;
        struct pack_idx_option opts;
        unsigned char pack_sha1[20];
        unsigned foreign_nr = 1;        /* zero is a "good" value, assume bad */
        int report_end_of_input = 0;
  
+       /*
+        * index-pack never needs to fetch missing objects, since it only
+        * accesses the repo to do hash collision checks
+        */
+       fetch_if_missing = 0;
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(index_pack_usage);
  
                                from_stdin = 1;
                        } else if (!strcmp(arg, "--fix-thin")) {
                                fix_thin_pack = 1;
 -                      } else if (!strcmp(arg, "--strict")) {
 -                              strict = 1;
 -                              do_fsck_object = 1;
 -                      } else if (skip_prefix(arg, "--strict=", &arg)) {
 +                      } else if (skip_to_optional_arg(arg, "--strict", &arg)) {
                                strict = 1;
                                do_fsck_object = 1;
                                fsck_set_msg_types(&fsck_options, arg);
                                verify = 1;
                                show_stat = 1;
                                stat_only = 1;
 -                      } else if (!strcmp(arg, "--keep")) {
 -                              keep_msg = "";
 -                      } else if (starts_with(arg, "--keep=")) {
 -                              keep_msg = arg + 7;
 -                      } else if (!strcmp(arg, "--promisor")) {
 -                              promisor_msg = "";
 -                      } else if (starts_with(arg, "--promisor=")) {
 -                              promisor_msg = arg + strlen("--promisor=");
 +                      } else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) {
 +                              ; /* nothing to do */
++                      } else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) {
++                              ; /* already parsed */
                        } else if (starts_with(arg, "--threads=")) {
                                char *end;
                                nr_threads = strtoul(arg+10, &end, 0);
        if (from_stdin && !startup_info->have_repository)
                die(_("--stdin requires a git repository"));
        if (!index_name && pack_name)
-               index_name = derive_filename(pack_name, ".idx", &index_name_buf);
-       if (keep_msg && !keep_name && pack_name)
-               keep_name = derive_filename(pack_name, ".keep", &keep_name_buf);
+               index_name = derive_filename(pack_name, "idx", &index_name_buf);
  
        if (verify) {
                if (!index_name)
        if (!verify)
                final(pack_name, curr_pack,
                      index_name, curr_index,
-                     keep_name, keep_msg,
+                     keep_msg, promisor_msg,
                      pack_sha1);
        else
                close(input_fd);
        free(objects);
        strbuf_release(&index_name_buf);
-       strbuf_release(&keep_name_buf);
        if (pack_name == NULL)
                free((void *) curr_pack);
        if (index_name == NULL)
diff --combined builtin/pack-objects.c
index 6b9cfc289d87b543b747c4024504703a2e0d6641,f5fc40177cc052eb1bb5cb1ec5b94eae6b481d11..6c71552cdf994c4e65cb5495d133494371529f80
@@@ -75,6 -75,8 +75,8 @@@ static int use_bitmap_index = -1
  static int write_bitmap_index;
  static uint16_t write_bitmap_options;
  
+ static int exclude_promisor_objects;
  static unsigned long delta_cache_size = 0;
  static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
  static unsigned long cache_max_small_delta_size = 1000;
@@@ -84,8 -86,9 +86,9 @@@ static unsigned long window_memory_limi
  static struct list_objects_filter_options filter_options;
  
  enum missing_action {
-       MA_ERROR = 0,    /* fail if any missing objects are encountered */
-       MA_ALLOW_ANY,    /* silently allow ALL missing objects */
+       MA_ERROR = 0,      /* fail if any missing objects are encountered */
+       MA_ALLOW_ANY,      /* silently allow ALL missing objects */
+       MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
  };
  static enum missing_action arg_missing_action;
  static show_object_fn fn_show_object;
@@@ -162,7 -165,7 +165,7 @@@ static unsigned long do_compress(void *
  }
  
  static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f,
 -                                         const unsigned char *sha1)
 +                                         const struct object_id *oid)
  {
        git_zstream stream;
        unsigned char ibuf[1024 * 16];
                int zret = Z_OK;
                readlen = read_istream(st, ibuf, sizeof(ibuf));
                if (readlen == -1)
 -                      die(_("unable to read %s"), sha1_to_hex(sha1));
 +                      die(_("unable to read %s"), oid_to_hex(oid));
  
                stream.next_in = ibuf;
                stream.avail_in = readlen;
@@@ -350,7 -353,7 +353,7 @@@ static unsigned long write_no_reuse_obj
                sha1write(f, header, hdrlen);
        }
        if (st) {
 -              datalen = write_large_blob_data(st, f, entry->idx.oid.hash);
 +              datalen = write_large_blob_data(st, f, &entry->idx.oid);
                close_istream(st);
        } else {
                sha1write(f, buf, datalen);
@@@ -568,13 -571,13 +571,13 @@@ static enum write_one_status write_one(
  static int mark_tagged(const char *path, const struct object_id *oid, int flag,
                       void *cb_data)
  {
 -      unsigned char peeled[20];
 +      struct object_id peeled;
        struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
  
        if (entry)
                entry->tagged = 1;
 -      if (!peel_ref(path, peeled)) {
 -              entry = packlist_find(&to_pack, peeled, NULL);
 +      if (!peel_ref(path, &peeled)) {
 +              entry = packlist_find(&to_pack, peeled.hash, NULL);
                if (entry)
                        entry->tagged = 1;
        }
@@@ -803,7 -806,7 +806,7 @@@ static void write_pack_file(void
        write_order = compute_write_order();
  
        do {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                char *pack_tmp_name = NULL;
  
                if (pack_to_stdout)
                 * If so, rewrite it like in fast-import
                 */
                if (pack_to_stdout) {
 -                      sha1close(f, sha1, CSUM_CLOSE);
 +                      sha1close(f, oid.hash, CSUM_CLOSE);
                } else if (nr_written == nr_remaining) {
 -                      sha1close(f, sha1, CSUM_FSYNC);
 +                      sha1close(f, oid.hash, CSUM_FSYNC);
                } else {
 -                      int fd = sha1close(f, sha1, 0);
 -                      fixup_pack_header_footer(fd, sha1, pack_tmp_name,
 -                                               nr_written, sha1, offset);
 +                      int fd = sha1close(f, oid.hash, 0);
 +                      fixup_pack_header_footer(fd, oid.hash, pack_tmp_name,
 +                                               nr_written, oid.hash, offset);
                        close(fd);
                        if (write_bitmap_index) {
                                warning(_(no_split_warning));
                        strbuf_addf(&tmpname, "%s-", base_name);
  
                        if (write_bitmap_index) {
 -                              bitmap_writer_set_checksum(sha1);
 +                              bitmap_writer_set_checksum(oid.hash);
                                bitmap_writer_build_type_index(written_list, nr_written);
                        }
  
                        finish_tmp_packfile(&tmpname, pack_tmp_name,
                                            written_list, nr_written,
 -                                          &pack_idx_opts, sha1);
 +                                          &pack_idx_opts, oid.hash);
  
                        if (write_bitmap_index) {
 -                              strbuf_addf(&tmpname, "%s.bitmap", sha1_to_hex(sha1));
 +                              strbuf_addf(&tmpname, "%s.bitmap", oid_to_hex(&oid));
  
                                stop_progress(&progress_state);
  
  
                        strbuf_release(&tmpname);
                        free(pack_tmp_name);
 -                      puts(sha1_to_hex(sha1));
 +                      puts(oid_to_hex(&oid));
                }
  
                /* mark written objects as written to previous pack */
@@@ -939,13 -942,13 +942,13 @@@ static int no_try_delta(const char *pat
   * found the item, since that saves us from having to look it up again a
   * few lines later when we want to add the new entry.
   */
 -static int have_duplicate_entry(const unsigned char *sha1,
 +static int have_duplicate_entry(const struct object_id *oid,
                                int exclude,
                                uint32_t *index_pos)
  {
        struct object_entry *entry;
  
 -      entry = packlist_find(&to_pack, sha1, index_pos);
 +      entry = packlist_find(&to_pack, oid->hash, index_pos);
        if (!entry)
                return 0;
  
@@@ -1001,7 -1004,7 +1004,7 @@@ static int want_found_object(int exclud
   * function finds if there is any pack that has the object and returns the pack
   * and its offset in these variables.
   */
 -static int want_object_in_pack(const unsigned char *sha1,
 +static int want_object_in_pack(const struct object_id *oid,
                               int exclude,
                               struct packed_git **found_pack,
                               off_t *found_offset)
        struct mru_entry *entry;
        int want;
  
 -      if (!exclude && local && has_loose_object_nonlocal(sha1))
 +      if (!exclude && local && has_loose_object_nonlocal(oid->hash))
                return 0;
  
        /*
                if (p == *found_pack)
                        offset = *found_offset;
                else
 -                      offset = find_pack_entry_one(sha1, p);
 +                      offset = find_pack_entry_one(oid->hash, p);
  
                if (offset) {
                        if (!*found_pack) {
        return 1;
  }
  
 -static void create_object_entry(const unsigned char *sha1,
 +static void create_object_entry(const struct object_id *oid,
                                enum object_type type,
                                uint32_t hash,
                                int exclude,
  {
        struct object_entry *entry;
  
 -      entry = packlist_alloc(&to_pack, sha1, index_pos);
 +      entry = packlist_alloc(&to_pack, oid->hash, index_pos);
        entry->hash = hash;
        if (type)
                entry->type = type;
@@@ -1081,17 -1084,17 +1084,17 @@@ static const char no_closure_warning[] 
  "disabling bitmap writing, as some objects are not being packed"
  );
  
 -static int add_object_entry(const unsigned char *sha1, enum object_type type,
 +static int add_object_entry(const struct object_id *oid, enum object_type type,
                            const char *name, int exclude)
  {
        struct packed_git *found_pack = NULL;
        off_t found_offset = 0;
        uint32_t index_pos;
  
 -      if (have_duplicate_entry(sha1, exclude, &index_pos))
 +      if (have_duplicate_entry(oid, exclude, &index_pos))
                return 0;
  
 -      if (!want_object_in_pack(sha1, exclude, &found_pack, &found_offset)) {
 +      if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) {
                /* The pack is missing an object, so it will not have closure */
                if (write_bitmap_index) {
                        warning(_(no_closure_warning));
                return 0;
        }
  
 -      create_object_entry(sha1, type, pack_name_hash(name),
 +      create_object_entry(oid, type, pack_name_hash(name),
                            exclude, name && no_try_delta(name),
                            index_pos, found_pack, found_offset);
  
        return 1;
  }
  
 -static int add_object_entry_from_bitmap(const unsigned char *sha1,
 +static int add_object_entry_from_bitmap(const struct object_id *oid,
                                        enum object_type type,
                                        int flags, uint32_t name_hash,
                                        struct packed_git *pack, off_t offset)
  {
        uint32_t index_pos;
  
 -      if (have_duplicate_entry(sha1, 0, &index_pos))
 +      if (have_duplicate_entry(oid, 0, &index_pos))
                return 0;
  
 -      if (!want_object_in_pack(sha1, 0, &pack, &offset))
 +      if (!want_object_in_pack(oid, 0, &pack, &offset))
                return 0;
  
 -      create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
 +      create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset);
  
        display_progress(progress_state, nr_result);
        return 1;
  }
  
  struct pbase_tree_cache {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int ref;
        int temporary;
        void *tree_data;
  };
  
  static struct pbase_tree_cache *(pbase_tree_cache[256]);
 -static int pbase_tree_cache_ix(const unsigned char *sha1)
 +static int pbase_tree_cache_ix(const struct object_id *oid)
  {
 -      return sha1[0] % ARRAY_SIZE(pbase_tree_cache);
 +      return oid->hash[0] % ARRAY_SIZE(pbase_tree_cache);
  }
  static int pbase_tree_cache_ix_incr(int ix)
  {
@@@ -1155,14 -1158,14 +1158,14 @@@ static struct pbase_tree 
        struct pbase_tree_cache pcache;
  } *pbase_tree;
  
 -static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
 +static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid)
  {
        struct pbase_tree_cache *ent, *nent;
        void *data;
        unsigned long size;
        enum object_type type;
        int neigh;
 -      int my_ix = pbase_tree_cache_ix(sha1);
 +      int my_ix = pbase_tree_cache_ix(oid);
        int available_ix = -1;
  
        /* pbase-tree-cache acts as a limited hashtable.
         */
        for (neigh = 0; neigh < 8; neigh++) {
                ent = pbase_tree_cache[my_ix];
 -              if (ent && !hashcmp(ent->sha1, sha1)) {
 +              if (ent && !oidcmp(&ent->oid, oid)) {
                        ent->ref++;
                        return ent;
                }
        /* Did not find one.  Either we got a bogus request or
         * we need to read and perhaps cache.
         */
 -      data = read_sha1_file(sha1, &type, &size);
 +      data = read_sha1_file(oid->hash, &type, &size);
        if (!data)
                return NULL;
        if (type != OBJ_TREE) {
                free(ent->tree_data);
                nent = ent;
        }
 -      hashcpy(nent->sha1, sha1);
 +      oidcpy(&nent->oid, oid);
        nent->tree_data = data;
        nent->tree_size = size;
        nent->ref = 1;
@@@ -1258,7 -1261,7 +1261,7 @@@ static void add_pbase_object(struct tre
                if (cmp < 0)
                        return;
                if (name[cmplen] != '/') {
 -                      add_object_entry(entry.oid->hash,
 +                      add_object_entry(entry.oid,
                                         object_type(entry.mode),
                                         fullname, 1);
                        return;
                        const char *down = name+cmplen+1;
                        int downlen = name_cmp_len(down);
  
 -                      tree = pbase_tree_get(entry.oid->hash);
 +                      tree = pbase_tree_get(entry.oid);
                        if (!tree)
                                return;
                        init_tree_desc(&sub, tree->tree_data, tree->tree_size);
@@@ -1328,7 -1331,7 +1331,7 @@@ static void add_preferred_base_object(c
        cmplen = name_cmp_len(name);
        for (it = pbase_tree; it; it = it->next) {
                if (cmplen == 0) {
 -                      add_object_entry(it->pcache.sha1, OBJ_TREE, NULL, 1);
 +                      add_object_entry(&it->pcache.oid, OBJ_TREE, NULL, 1);
                }
                else {
                        struct tree_desc tree;
        }
  }
  
 -static void add_preferred_base(unsigned char *sha1)
 +static void add_preferred_base(struct object_id *oid)
  {
        struct pbase_tree *it;
        void *data;
        unsigned long size;
 -      unsigned char tree_sha1[20];
 +      struct object_id tree_oid;
  
        if (window <= num_preferred_base++)
                return;
  
 -      data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
 +      data = read_object_with_reference(oid->hash, tree_type, &size, tree_oid.hash);
        if (!data)
                return;
  
        for (it = pbase_tree; it; it = it->next) {
 -              if (!hashcmp(it->pcache.sha1, tree_sha1)) {
 +              if (!oidcmp(&it->pcache.oid, &tree_oid)) {
                        free(data);
                        return;
                }
        it->next = pbase_tree;
        pbase_tree = it;
  
 -      hashcpy(it->pcache.sha1, tree_sha1);
 +      oidcpy(&it->pcache.oid, &tree_oid);
        it->pcache.tree_data = data;
        it->pcache.tree_size = size;
  }
@@@ -2368,7 -2371,7 +2371,7 @@@ static void add_tag_chain(const struct 
                        die("unable to pack objects reachable from tag %s",
                            oid_to_hex(oid));
  
 -              add_object_entry(tag->object.oid.hash, OBJ_TAG, NULL, 0);
 +              add_object_entry(&tag->object.oid, OBJ_TAG, NULL, 0);
  
                if (tag->tagged->type != OBJ_TAG)
                        return;
@@@ -2382,7 -2385,7 +2385,7 @@@ static int add_ref_tag(const char *path
        struct object_id peeled;
  
        if (starts_with(path, "refs/tags/") && /* is a tag? */
 -          !peel_ref(path, peeled.hash)    && /* peelable? */
 +          !peel_ref(path, &peeled)    && /* peelable? */
            packlist_find(&to_pack, peeled.hash, NULL))      /* object packed? */
                add_tag_chain(oid);
        return 0;
@@@ -2516,9 -2519,8 +2519,9 @@@ static int git_pack_config(const char *
  
  static void read_object_list_from_stdin(void)
  {
 -      char line[40 + 1 + PATH_MAX + 2];
 -      unsigned char sha1[20];
 +      char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2];
 +      struct object_id oid;
 +      const char *p;
  
        for (;;) {
                if (!fgets(line, sizeof(line), stdin)) {
                        continue;
                }
                if (line[0] == '-') {
 -                      if (get_sha1_hex(line+1, sha1))
 -                              die("expected edge sha1, got garbage:\n %s",
 +                      if (get_oid_hex(line+1, &oid))
 +                              die("expected edge object ID, got garbage:\n %s",
                                    line);
 -                      add_preferred_base(sha1);
 +                      add_preferred_base(&oid);
                        continue;
                }
 -              if (get_sha1_hex(line, sha1))
 -                      die("expected sha1, got garbage:\n %s", line);
 +              if (parse_oid_hex(line, &oid, &p))
 +                      die("expected object ID, got garbage:\n %s", line);
  
 -              add_preferred_base_object(line+41);
 -              add_object_entry(sha1, 0, line+41, 0);
 +              add_preferred_base_object(p + 1);
 +              add_object_entry(&oid, 0, p + 1, 0);
        }
  }
  
  
  static void show_commit(struct commit *commit, void *data)
  {
 -      add_object_entry(commit->object.oid.hash, OBJ_COMMIT, NULL, 0);
 +      add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0);
        commit->object.flags |= OBJECT_ADDED;
  
        if (write_bitmap_index)
  static void show_object(struct object *obj, const char *name, void *data)
  {
        add_preferred_base_object(name);
 -      add_object_entry(obj->oid.hash, obj->type, name, 0);
 +      add_object_entry(&obj->oid, obj->type, name, 0);
        obj->flags |= OBJECT_ADDED;
  }
  
@@@ -2578,6 -2580,20 +2581,20 @@@ static void show_object__ma_allow_any(s
        show_object(obj, name, data);
  }
  
+ static void show_object__ma_allow_promisor(struct object *obj, const char *name, void *data)
+ {
+       assert(arg_missing_action == MA_ALLOW_PROMISOR);
+       /*
+        * Quietly ignore EXPECTED missing objects.  This avoids problems with
+        * staging them now and getting an odd error later.
+        */
+       if (!has_object_file(&obj->oid) && is_promisor_object(&obj->oid))
+               return;
+       show_object(obj, name, data);
+ }
  static int option_parse_missing_action(const struct option *opt,
                                       const char *arg, int unset)
  {
  
        if (!strcmp(arg, "allow-any")) {
                arg_missing_action = MA_ALLOW_ANY;
+               fetch_if_missing = 0;
                fn_show_object = show_object__ma_allow_any;
                return 0;
        }
  
+       if (!strcmp(arg, "allow-promisor")) {
+               arg_missing_action = MA_ALLOW_PROMISOR;
+               fetch_if_missing = 0;
+               fn_show_object = show_object__ma_allow_promisor;
+               return 0;
+       }
        die(_("invalid value for --missing"));
        return 0;
  }
  
  static void show_edge(struct commit *commit)
  {
 -      add_preferred_base(commit->object.oid.hash);
 +      add_preferred_base(&commit->object.oid);
  }
  
  struct in_pack_object {
@@@ -2649,7 -2673,7 +2674,7 @@@ static void add_objects_in_unpacked_pac
        memset(&in_pack, 0, sizeof(in_pack));
  
        for (p = packed_git; p; p = p->next) {
 -              const unsigned char *sha1;
 +              struct object_id oid;
                struct object *o;
  
                if (!p->pack_local || p->pack_keep)
                           in_pack.alloc);
  
                for (i = 0; i < p->num_objects; i++) {
 -                      sha1 = nth_packed_object_sha1(p, i);
 -                      o = lookup_unknown_object(sha1);
 +                      nth_packed_object_oid(&oid, p, i);
 +                      o = lookup_unknown_object(oid.hash);
                        if (!(o->flags & OBJECT_ADDED))
                                mark_in_pack_object(o, p, &in_pack);
                        o->flags |= OBJECT_ADDED;
                QSORT(in_pack.array, in_pack.nr, ofscmp);
                for (i = 0; i < in_pack.nr; i++) {
                        struct object *o = in_pack.array[i].object;
 -                      add_object_entry(o->oid.hash, o->type, "", 0);
 +                      add_object_entry(&o->oid, o->type, "", 0);
                }
        }
        free(in_pack.array);
@@@ -2690,7 -2714,7 +2715,7 @@@ static int add_loose_object(const struc
                return 0;
        }
  
 -      add_object_entry(oid->hash, type, "", 0);
 +      add_object_entry(oid, type, "", 0);
        return 0;
  }
  
@@@ -2706,7 -2730,7 +2731,7 @@@ static void add_unreachable_loose_objec
                                      NULL, NULL, NULL);
  }
  
 -static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 +static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
  {
        static struct packed_git *last_found = (void *)1;
        struct packed_git *p;
  
        while (p) {
                if ((!p->pack_local || p->pack_keep) &&
 -                      find_pack_entry_one(sha1, p)) {
 +                      find_pack_entry_one(oid->hash, p)) {
                        last_found = p;
                        return 1;
                }
@@@ -2766,7 -2790,7 +2791,7 @@@ static void loosen_unused_packed_object
                for (i = 0; i < p->num_objects; i++) {
                        nth_packed_object_oid(&oid, p, i);
                        if (!packlist_find(&to_pack, oid.hash, NULL) &&
 -                          !has_sha1_pack_kept_or_nonlocal(oid.hash) &&
 +                          !has_sha1_pack_kept_or_nonlocal(&oid) &&
                            !loosened_object_can_be_discarded(&oid, p->mtime))
                                if (force_object_loose(oid.hash, p->mtime))
                                        die("unable to force loose object");
@@@ -3009,6 -3033,8 +3034,8 @@@ int cmd_pack_objects(int argc, const ch
                { OPTION_CALLBACK, 0, "missing", NULL, N_("action"),
                  N_("handling for missing objects"), PARSE_OPT_NONEG,
                  option_parse_missing_action },
+               OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
+                        N_("do not pack objects in promisor packfiles")),
                OPT_END(),
        };
  
                argv_array_push(&rp, "--unpacked");
        }
  
+       if (exclude_promisor_objects) {
+               use_internal_rev_list = 1;
+               fetch_if_missing = 0;
+               argv_array_push(&rp, "--exclude-promisor-objects");
+       }
        if (!reuse_object)
                reuse_delta = 0;
        if (pack_compression_level == -1)
diff --combined builtin/prune.c
index d2fdae680a1ebe75d55d4fb66a3bdd0b54829666,be34645dcfa6c13781125d46d7d371c67c33264d..4cfec82f4076e357048bcb5816ceeb0f4b020192
@@@ -8,7 -8,7 +8,7 @@@
  #include "progress.h"
  
  static const char * const prune_usage[] = {
 -      N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"),
 +      N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"),
        NULL
  };
  static int show_only;
@@@ -101,12 -101,15 +101,15 @@@ int cmd_prune(int argc, const char **ar
  {
        struct rev_info revs;
        struct progress *progress = NULL;
+       int exclude_promisor_objects = 0;
        const struct option options[] = {
                OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
                OPT__VERBOSE(&verbose, N_("report pruned objects")),
                OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
                OPT_EXPIRY_DATE(0, "expire", &expire,
                                N_("expire objects older than <time>")),
+               OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
+                        N_("limit traversal to objects outside promisor packfiles")),
                OPT_END()
        };
        char *s;
                show_progress = isatty(2);
        if (show_progress)
                progress = start_delayed_progress(_("Checking connectivity"), 0);
+       if (exclude_promisor_objects) {
+               fetch_if_missing = 0;
+               revs.exclude_promisor_objects = 1;
+       }
  
        mark_reachable_objects(&revs, 1, expire, progress);
        stop_progress(&progress);
diff --combined builtin/rev-list.c
index d5345b6a2e237b550e5cee69e6d46ff8271ade22,48f922d7094f4e8e4a3215a2a16c318a82145616..e27aa1fc0745afc18e71809535d20481f8cb3a62
@@@ -15,6 -15,7 +15,7 @@@
  #include "progress.h"
  #include "reflog-walk.h"
  #include "oidset.h"
+ #include "packfile.h"
  
  static const char rev_list_usage[] =
  "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@@ -67,6 -68,7 +68,7 @@@ enum missing_action 
        MA_ERROR = 0,    /* fail if any missing objects are encountered */
        MA_ALLOW_ANY,    /* silently allow ALL missing objects */
        MA_PRINT,        /* print ALL missing objects in special section */
+       MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
  };
  static enum missing_action arg_missing_action;
  
@@@ -197,6 -199,12 +199,12 @@@ static void finish_commit(struct commi
  
  static inline void finish_object__ma(struct object *obj)
  {
+       /*
+        * Whether or not we try to dynamically fetch missing objects
+        * from the server, we currently DO NOT have the object.  We
+        * can either print, allow (ignore), or conditionally allow
+        * (ignore) them.
+        */
        switch (arg_missing_action) {
        case MA_ERROR:
                die("missing blob object '%s'", oid_to_hex(&obj->oid));
                oidset_insert(&missing_objects, &obj->oid);
                return;
  
+       case MA_ALLOW_PROMISOR:
+               if (is_promisor_object(&obj->oid))
+                       return;
+               die("unexpected missing blob object '%s'",
+                   oid_to_hex(&obj->oid));
+               return;
        default:
                BUG("unhandled missing_action");
                return;
        }
  }
  
- static void finish_object(struct object *obj, const char *name, void *cb_data)
+ static int finish_object(struct object *obj, const char *name, void *cb_data)
  {
        struct rev_list_info *info = cb_data;
-       if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid))
+       if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) {
                finish_object__ma(obj);
+               return 1;
+       }
        if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
                parse_object(&obj->oid);
+       return 0;
  }
  
  static void show_object(struct object *obj, const char *name, void *cb_data)
  {
        struct rev_list_info *info = cb_data;
-       finish_object(obj, name, cb_data);
+       if (finish_object(obj, name, cb_data))
+               return;
        display_progress(progress, ++progress_counter);
        if (info->flags & REV_LIST_QUIET)
                return;
@@@ -295,14 -314,14 +314,14 @@@ static int show_bisect_vars(struct rev_
  }
  
  static int show_object_fast(
 -      const unsigned char *sha1,
 +      const struct object_id *oid,
        enum object_type type,
        int exclude,
        uint32_t name_hash,
        struct packed_git *found_pack,
        off_t found_offset)
  {
 -      fprintf(stdout, "%s\n", sha1_to_hex(sha1));
 +      fprintf(stdout, "%s\n", oid_to_hex(oid));
        return 1;
  }
  
@@@ -315,11 -334,19 +334,19 @@@ static inline int parse_missing_action_
  
        if (!strcmp(value, "allow-any")) {
                arg_missing_action = MA_ALLOW_ANY;
+               fetch_if_missing = 0;
                return 1;
        }
  
        if (!strcmp(value, "print")) {
                arg_missing_action = MA_PRINT;
+               fetch_if_missing = 0;
+               return 1;
+       }
+       if (!strcmp(value, "allow-promisor")) {
+               arg_missing_action = MA_ALLOW_PROMISOR;
+               fetch_if_missing = 0;
                return 1;
        }
  
@@@ -344,6 -371,35 +371,35 @@@ int cmd_rev_list(int argc, const char *
        init_revisions(&revs, prefix);
        revs.abbrev = DEFAULT_ABBREV;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
+       /*
+        * Scan the argument list before invoking setup_revisions(), so that we
+        * know if fetch_if_missing needs to be set to 0.
+        *
+        * "--exclude-promisor-objects" acts as a pre-filter on missing objects
+        * by not crossing the boundary from realized objects to promisor
+        * objects.
+        *
+        * Let "--missing" to conditionally set fetch_if_missing.
+        */
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strcmp(arg, "--exclude-promisor-objects")) {
+                       fetch_if_missing = 0;
+                       revs.exclude_promisor_objects = 1;
+                       break;
+               }
+       }
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (skip_prefix(arg, "--missing=", &arg)) {
+                       if (revs.exclude_promisor_objects)
+                               die(_("cannot combine --exclude-promisor-objects and --missing"));
+                       if (parse_missing_action_value(arg))
+                               break;
+               }
+       }
        argc = setup_revisions(argc, argv, &revs, NULL);
  
        memset(&info, 0, sizeof(info));
        if (revs.bisect)
                bisect_list = 1;
  
 -      if (DIFF_OPT_TST(&revs.diffopt, QUICK))
 +      if (revs.diffopt.flags.quick)
                info.flags |= REV_LIST_QUIET;
        for (i = 1 ; i < argc; i++) {
                const char *arg = argv[i];
                        continue;
                }
  
-               if (skip_prefix(arg, "--missing=", &arg) &&
-                   parse_missing_action_value(arg))
-                       continue;
+               if (!strcmp(arg, "--exclude-promisor-objects"))
+                       continue; /* already handled above */
+               if (skip_prefix(arg, "--missing=", &arg))
+                       continue; /* already handled above */
  
                usage(rev_list_usage);
  
        if (bisect_list) {
                int reaches = reaches, all = all;
  
 -              revs.commits = find_bisection(revs.commits, &reaches, &all,
 -                                            bisect_find_all);
 +              find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
  
                if (bisect_show_vars)
                        return show_bisect_vars(&info, reaches, all);
diff --combined cache.h
index d8b975a5715e8a5ec7c32e176958725cba61d67a,69800721324d7e437e8be78e4ffb54a6271c5755..23413fdcb71f484b6dc59bec2c912d9dd495cf4f
+++ b/cache.h
@@@ -14,7 -14,6 +14,7 @@@
  #include "hash.h"
  #include "path.h"
  #include "sha1-array.h"
 +#include "repository.h"
  
  #ifndef platform_SHA_CTX
  /*
@@@ -78,8 -77,6 +78,8 @@@ 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
@@@ -207,7 -204,6 +207,7 @@@ struct cache_entry 
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 +#define CE_FSMONITOR_VALID   (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -331,7 -327,6 +331,7 @@@ static inline unsigned int canon_mode(u
  #define CACHE_TREE_CHANGED    (1 << 5)
  #define SPLIT_INDEX_ORDERED   (1 << 6)
  #define UNTRACKED_CHANGED     (1 << 7)
 +#define FSMONITOR_CHANGED     (1 << 8)
  
  struct split_index;
  struct untracked_cache;
@@@ -350,8 -345,6 +350,8 @@@ struct index_state 
        struct hashmap dir_hash;
        unsigned char sha1[20];
        struct untracked_cache *untracked;
 +      uint64_t fsmonitor_last_update;
 +      struct ewah_bitmap *fsmonitor_dirty;
  };
  
  extern struct index_state the_index;
@@@ -453,16 -446,6 +453,16 @@@ static inline enum object_type object_t
  #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
  #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
  
 +/*
 + * Environment variable used in handshaking the wire protocol.
 + * Contains a colon ':' separated list of keys with optional values
 + * 'key[=value]'.  Presence of unknown keys and values must be
 + * ignored.
 + */
 +#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
 +/* HTTP header used to handshake the wire protocol */
 +#define GIT_PROTOCOL_HEADER "Git-Protocol"
 +
  /*
   * This environment variable is expected to contain a boolean indicating
   * whether we should or should not treat:
@@@ -619,40 -602,12 +619,40 @@@ extern int do_read_index(struct index_s
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
 +
 +/* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
 -#define CLOSE_LOCK            (1 << 1)
 +
 +/*
 + * Write the index while holding an already-taken lock. Close the lock,
 + * and if `COMMIT_LOCK` is given, commit it.
 + *
 + * Unless a split index is in use, write the index into the lockfile.
 + *
 + * With a split index, write the shared index to a temporary file,
 + * adjust its permissions and rename it into place, then write the
 + * split index to the lockfile. If the temporary file for the shared
 + * index cannot be created, fall back to the behavior described in
 + * the previous paragraph.
 + *
 + * With `COMMIT_LOCK`, the lock is always committed or rolled back.
 + * Without it, the lock is closed, but neither committed nor rolled
 + * back.
 + */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 +
  extern int discard_index(struct index_state *);
  extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
 +
 +/**
 + * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
 + * branch, returns 1 if there are entries in the index, 0 otherwise. If an
 + * strbuf is provided, the space-separated list of files that differ will be
 + * appended to it.
 + */
 +extern int index_has_changes(struct strbuf *sb);
 +
  extern int verify_path(const char *path);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
  extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
@@@ -726,14 -681,11 +726,14 @@@ extern void *read_blob_data_from_index(
  #define CE_MATCH_IGNORE_MISSING               0x08
  /* enable stat refresh */
  #define CE_MATCH_REFRESH              0x10
 -extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 -extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 +#define CE_MATCH_IGNORE_FSMONITOR 0X20
 +extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 +extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
 +#define HASH_RENORMALIZE  4
  extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
  extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
  
@@@ -764,17 -716,12 +764,17 @@@ extern void fill_stat_cache_info(struc
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
  
 +/*
 + * Opportunistically update the index but do not complain if we can't.
 + * The lockfile is always committed or rolled back.
 + */
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
  extern int verify_index_checksum;
 +extern int verify_ce_order;
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -828,7 -775,6 +828,7 @@@ extern int core_apply_sparse_checkout
  extern int precomposed_unicode;
  extern int protect_hfs;
  extern int protect_ntfs;
 +extern const char *core_fsmonitor;
  
  /*
   * Include broken refs in all ref iterations, which will
@@@ -914,12 -860,13 +914,14 @@@ 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;
  
  struct repository_format {
        int version;
        int precious_objects;
+       char *partial_clone; /* value of extensions.partialclone */
        int is_bare;
 +      int hash_algo;
        char *work_tree;
        struct string_list unknown_extensions;
  };
@@@ -1052,22 -999,22 +1054,22 @@@ extern const struct object_id empty_blo
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
 +      return !hashcmp(sha1, the_hash_algo->empty_blob->hash);
  }
  
  static inline int is_empty_blob_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
 +      return !oidcmp(oid, the_hash_algo->empty_blob);
  }
  
  static inline int is_empty_tree_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
 +      return !hashcmp(sha1, the_hash_algo->empty_tree->hash);
  }
  
  static inline int is_empty_tree_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
 +      return !oidcmp(oid, the_hash_algo->empty_tree);
  }
  
  /* set default permissions by passing mode arguments to open(2) */
@@@ -1372,13 -1319,6 +1374,13 @@@ extern int set_disambiguate_hint_config
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
 +/*
 + * Read `len` pairs of hexadecimal digits from `hex` and write the
 + * values to `binary` as `len` bytes. Return 0 on success, or -1 if
 + * the input does not consist of hex digits).
 + */
 +extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 +
  /*
   * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
@@@ -1500,7 -1440,6 +1502,7 @@@ extern const char *ident_default_name(v
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
 +extern int is_terminal_dumb(void);
  extern int git_ident_config(const char *, const char *, void *);
  extern void reset_ident_date(void);
  
@@@ -1648,7 -1587,8 +1650,8 @@@ extern struct packed_git 
        unsigned pack_local:1,
                 pack_keep:1,
                 freshened:1,
-                do_not_close:1;
+                do_not_close:1,
+                pack_promisor:1;
        unsigned char sha1[20];
        struct revindex_entry *revindex;
        /* something like ".git/objects/pack/xxxxx.pack" */
@@@ -1787,6 -1727,14 +1790,14 @@@ struct object_info 
  #define OBJECT_INFO_QUICK 8
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
  
+ /*
+  * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
+  * blobs. This has a difference only if extensions.partialClone is set.
+  *
+  * Its default value is 1.
+  */
+ extern int fetch_if_missing;
  /* Dumb servers support */
  extern int update_server_info(int);
  
@@@ -1982,10 -1930,4 +1993,10 @@@ void sleep_millisec(int millisec)
   */
  void safe_create_dir(const char *dir, int share);
  
 +/*
 + * Should we print an ellipsis after an abbreviated SHA-1 value
 + * when doing diff-raw output or indicating a detached HEAD?
 + */
 +extern int print_sha1_ellipsis(void);
 +
  #endif /* CACHE_H */
diff --combined environment.c
index 63ac38a46f8f01ee3d09ed8e09bd93acba63b21a,e52aab3ea4c4a90b42e560ba040ab5204b7067e6..835bb75c4ef2380d7fd1208f3ca042eee60a9ae3
@@@ -27,6 -27,7 +27,7 @@@ int warn_ambiguous_refs = 1
  int warn_on_object_refname_ambiguity = 1;
  int ref_paranoia = -1;
  int repository_format_precious_objects;
+ char *repository_format_partial_clone;
  const char *git_commit_encoding;
  const char *git_log_output_encoding;
  const char *apply_default_whitespace;
@@@ -76,7 -77,6 +77,7 @@@ int protect_hfs = PROTECT_HFS_DEFAULT
  #define PROTECT_NTFS_DEFAULT 0
  #endif
  int protect_ntfs = PROTECT_NTFS_DEFAULT;
 +const char *core_fsmonitor;
  
  /*
   * The character that begins a commented line in user-editable file
@@@ -344,18 -344,3 +345,18 @@@ int use_optional_locks(void
  {
        return git_env_bool(GIT_OPTIONAL_LOCKS_ENVIRONMENT, 1);
  }
 +
 +int print_sha1_ellipsis(void)
 +{
 +      /*
 +       * Determine if the calling environment contains the variable
 +       * GIT_PRINT_SHA1_ELLIPSIS set to "yes".
 +       */
 +      static int cached_result = -1; /* unknown */
 +
 +      if (cached_result < 0) {
 +              const char *v = getenv("GIT_PRINT_SHA1_ELLIPSIS");
 +              cached_result = (v && !strcasecmp(v, "yes"));
 +      }
 +      return cached_result;
 +}
diff --combined fetch-pack.c
index 9f6b07ad91f8c2c17e85004a0e71a8b69752d120,0798e0b8b28538963c7dcd1d3f0bc50350ed531c..7aa1f58995a2d987db0fc69a35de4af783779185
@@@ -450,6 -450,8 +450,8 @@@ static int find_common(struct fetch_pac
  
        flushes = 0;
        retval = -1;
+       if (args->no_dependents)
+               goto done;
        while ((oid = get_rev())) {
                packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid));
                print_verbose(args, "have %s", oid_to_hex(oid));
@@@ -716,8 -718,7 +718,8 @@@ static int everything_local(struct fetc
        for (ref = *refs; ref; ref = ref->next) {
                struct object *o;
  
 -              if (!has_object_file(&ref->old_oid))
 +              if (!has_object_file_with_flags(&ref->old_oid,
 +                                              OBJECT_INFO_QUICK))
                        continue;
  
                o = parse_object(&ref->old_oid);
                }
        }
  
-       if (!args->deepen) {
-               for_each_ref(mark_complete_oid, NULL);
-               for_each_cached_alternate(mark_alternate_complete);
-               commit_list_sort_by_date(&complete);
-               if (cutoff)
-                       mark_recent_complete_commits(args, cutoff);
-       }
+       if (!args->no_dependents) {
+               if (!args->deepen) {
+                       for_each_ref(mark_complete_oid, NULL);
+                       for_each_cached_alternate(mark_alternate_complete);
+                       commit_list_sort_by_date(&complete);
+                       if (cutoff)
+                               mark_recent_complete_commits(args, cutoff);
+               }
  
-       /*
-        * Mark all complete remote refs as common refs.
-        * Don't mark them common yet; the server has to be told so first.
-        */
-       for (ref = *refs; ref; ref = ref->next) {
-               struct object *o = deref_tag(lookup_object(ref->old_oid.hash),
-                                            NULL, 0);
+               /*
+                * Mark all complete remote refs as common refs.
+                * Don't mark them common yet; the server has to be told so first.
+                */
+               for (ref = *refs; ref; ref = ref->next) {
+                       struct object *o = deref_tag(lookup_object(ref->old_oid.hash),
+                                                    NULL, 0);
  
-               if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
-                       continue;
+                       if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
+                               continue;
  
-               if (!(o->flags & SEEN)) {
-                       rev_list_push((struct commit *)o, COMMON_REF | SEEN);
+                       if (!(o->flags & SEEN)) {
+                               rev_list_push((struct commit *)o, COMMON_REF | SEEN);
  
-                       mark_common((struct commit *)o, 1, 1);
+                               mark_common((struct commit *)o, 1, 1);
+                       }
                }
        }
  
@@@ -833,7 -836,7 +837,7 @@@ static int get_pack(struct fetch_pack_a
                argv_array_push(&cmd.args, alternate_shallow_file);
        }
  
-       if (do_keep) {
+       if (do_keep || args->from_promisor) {
                if (pack_lockfile)
                        cmd.out = -1;
                cmd_name = "index-pack";
                        argv_array_push(&cmd.args, "-v");
                if (args->use_thin_pack)
                        argv_array_push(&cmd.args, "--fix-thin");
-               if (args->lock_pack || unpack_limit) {
+               if (do_keep && (args->lock_pack || unpack_limit)) {
                        char hostname[HOST_NAME_MAX + 1];
                        if (xgethostname(hostname, sizeof(hostname)))
                                xsnprintf(hostname, sizeof(hostname), "localhost");
                }
                if (args->check_self_contained_and_connected)
                        argv_array_push(&cmd.args, "--check-self-contained-and-connected");
+               if (args->from_promisor)
+                       argv_array_push(&cmd.args, "--promisor");
        }
        else {
                cmd_name = "unpack-objects";
diff --combined list-objects.c
index 0966cdc9fa8dd448aa9955a2fb5324ae8ebbf97d,58621fc6ee47c09515766cdee9e796c2a9b5a25b..168bef688a89489a9d88d3e1f773483dbc1c8860
@@@ -9,6 -9,7 +9,7 @@@
  #include "list-objects.h"
  #include "list-objects-filter.h"
  #include "list-objects-filter-options.h"
+ #include "packfile.h"
  
  static void process_blob(struct rev_info *revs,
                         struct blob *blob,
        if (obj->flags & (UNINTERESTING | SEEN))
                return;
  
+       /*
+        * Pre-filter known-missing objects when explicitly requested.
+        * Otherwise, a missing object error message may be reported
+        * later (depending on other filtering criteria).
+        *
+        * Note that this "--exclude-promisor-objects" pre-filtering
+        * may cause the actual filter to report an incomplete list
+        * of missing objects.
+        */
+       if (revs->exclude_promisor_objects &&
+           !has_object_file(&obj->oid) &&
+           is_promisor_object(&obj->oid))
+               return;
        pathlen = path->len;
        strbuf_addstr(path, name);
        if (filter_fn)
@@@ -91,6 -106,8 +106,8 @@@ static void process_tree(struct rev_inf
                all_entries_interesting: entry_not_interesting;
        int baselen = base->len;
        enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
+       int gently = revs->ignore_missing_links ||
+                    revs->exclude_promisor_objects;
  
        if (!revs->tree_objects)
                return;
                die("bad tree object");
        if (obj->flags & (UNINTERESTING | SEEN))
                return;
-       if (parse_tree_gently(tree, revs->ignore_missing_links) < 0) {
+       if (parse_tree_gently(tree, gently) < 0) {
                if (revs->ignore_missing_links)
                        return;
+               /*
+                * Pre-filter known-missing tree objects when explicitly
+                * requested.  This may cause the actual filter to report
+                * an incomplete list of missing objects.
+                */
+               if (revs->exclude_promisor_objects &&
+                   is_promisor_object(&obj->oid))
+                       return;
                die("bad tree object %s", oid_to_hex(&obj->oid));
        }
  
@@@ -214,17 -241,27 +241,17 @@@ static void add_pending_tree(struct rev
        add_pending_object(revs, &tree->object, "");
  }
  
 -static void do_traverse(struct rev_info *revs,
 -                      show_commit_fn show_commit,
 -                      show_object_fn show_object,
 -                      void *show_data,
 -                      filter_object_fn filter_fn,
 -                      void *filter_data)
 +static void traverse_trees_and_blobs(struct rev_info *revs,
 +                                   struct strbuf *base,
 +                                   show_object_fn show_object,
 +                                   void *show_data,
 +                                   filter_object_fn filter_fn,
 +                                   void *filter_data)
  {
        int i;
 -      struct commit *commit;
 -      struct strbuf base;
  
 -      strbuf_init(&base, PATH_MAX);
 -      while ((commit = get_revision(revs)) != NULL) {
 -              /*
 -               * an uninteresting boundary commit may not have its tree
 -               * parsed yet, but we are not going to show them anyway
 -               */
 -              if (commit->tree)
 -                      add_pending_tree(revs, commit->tree);
 -              show_commit(commit, show_data);
 -      }
 +      assert(base->len == 0);
 +
        for (i = 0; i < revs->pending.nr; i++) {
                struct object_array_entry *pending = revs->pending.objects + i;
                struct object *obj = pending->item;
                        path = "";
                if (obj->type == OBJ_TREE) {
                        process_tree(revs, (struct tree *)obj, show_object,
 -                                   &base, path, show_data,
 +                                   base, path, show_data,
                                     filter_fn, filter_data);
                        continue;
                }
                if (obj->type == OBJ_BLOB) {
                        process_blob(revs, (struct blob *)obj, show_object,
 -                                   &base, path, show_data,
 +                                   base, path, show_data,
                                     filter_fn, filter_data);
                        continue;
                }
                    oid_to_hex(&obj->oid), name);
        }
        object_array_clear(&revs->pending);
 -      strbuf_release(&base);
 +}
 +
 +static void do_traverse(struct rev_info *revs,
 +                      show_commit_fn show_commit,
 +                      show_object_fn show_object,
 +                      void *show_data,
 +                      filter_object_fn filter_fn,
 +                      void *filter_data)
 +{
 +      struct commit *commit;
 +      struct strbuf csp; /* callee's scratch pad */
 +      strbuf_init(&csp, PATH_MAX);
 +
 +      while ((commit = get_revision(revs)) != NULL) {
 +              /*
 +               * an uninteresting boundary commit may not have its tree
 +               * parsed yet, but we are not going to show them anyway
 +               */
 +              if (commit->tree)
 +                      add_pending_tree(revs, commit->tree);
 +              show_commit(commit, show_data);
 +
 +              if (revs->tree_blobs_in_commit_order)
 +                      /*
 +                       * NEEDSWORK: Adding the tree and then flushing it here
 +                       * needs a reallocation for each commit. Can we pass the
 +                       * tree directory without allocation churn?
 +                       */
 +                      traverse_trees_and_blobs(revs, &csp,
 +                                               show_object, show_data,
 +                                               filter_fn, filter_data);
 +      }
 +      traverse_trees_and_blobs(revs, &csp,
 +                               show_object, show_data,
 +                               filter_fn, filter_data);
 +      strbuf_release(&csp);
  }
  
  void traverse_commit_list(struct rev_info *revs,
diff --combined object.c
index 0afdfd19b784a541ad6d7237add2fbb1c9ec91ab,4c222d6260ab8b3b1a44730a10649bb1c77ef043..9e6f9ff20b03f800edae8ec7adc26e30313a5616
+++ b/object.c
@@@ -252,7 -252,7 +252,7 @@@ struct object *parse_object(const struc
        if (obj && obj->parsed)
                return obj;
  
-       if ((obj && obj->type == OBJ_BLOB) ||
+       if ((obj && obj->type == OBJ_BLOB && has_object_file(oid)) ||
            (!obj && has_object_file(oid) &&
             sha1_object_info(oid->hash, NULL) == OBJ_BLOB)) {
                if (check_sha1_signature(repl, NULL, 0, NULL) < 0) {
@@@ -434,14 -434,3 +434,14 @@@ void clear_object_flags(unsigned flags
                        obj->flags &= ~flags;
        }
  }
 +
 +void clear_commit_marks_all(unsigned int flags)
 +{
 +      int i;
 +
 +      for (i = 0; i < obj_hash_size; i++) {
 +              struct object *obj = obj_hash[i];
 +              if (obj && obj->type == OBJ_COMMIT)
 +                      obj->flags &= ~flags;
 +      }
 +}
diff --combined revision.c
index a60628fbff96c413f9df5641d0bece45d89b721f,05a7aac063bd4053a1478a0a4b51210af73ded58..236d0d579f0b6920127e586fe09391d172445318
@@@ -198,6 -198,8 +198,8 @@@ static struct object *get_reference(str
        if (!object) {
                if (revs->ignore_missing)
                        return object;
+               if (revs->exclude_promisor_objects && is_promisor_object(oid))
+                       return NULL;
                die("bad object %s", name);
        }
        object->flags |= flags;
@@@ -395,16 -397,8 +397,16 @@@ static struct commit *one_relevant_pare
   * if the whole diff is removal of old data, and otherwise
   * REV_TREE_DIFFERENT (of course if the trees are the same we
   * want REV_TREE_SAME).
 - * That means that once we get to REV_TREE_DIFFERENT, we do not
 - * have to look any further.
 + *
 + * The only time we care about the distinction is when
 + * remove_empty_trees is in effect, in which case we care only about
 + * whether the whole change is REV_TREE_NEW, or if there's another type
 + * of change. Which means we can stop the diff early in either of these
 + * cases:
 + *
 + *   1. We're not using remove_empty_trees at all.
 + *
 + *   2. We saw anything except REV_TREE_NEW.
   */
  static int tree_difference = REV_TREE_SAME;
  
@@@ -415,11 -409,10 +417,11 @@@ static void file_add_remove(struct diff
                    const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
 +      struct rev_info *revs = options->change_fn_data;
  
        tree_difference |= diff;
 -      if (tree_difference == REV_TREE_DIFFERENT)
 -              DIFF_OPT_SET(options, HAS_CHANGES);
 +      if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW)
 +              options->flags.has_changes = 1;
  }
  
  static void file_change(struct diff_options *options,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
        tree_difference = REV_TREE_DIFFERENT;
 -      DIFF_OPT_SET(options, HAS_CHANGES);
 +      options->flags.has_changes = 1;
  }
  
  static int rev_compare_tree(struct rev_info *revs,
        }
  
        tree_difference = REV_TREE_SAME;
 -      DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
 +      revs->pruning.flags.has_changes = 0;
        if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@@ -480,7 -473,7 +482,7 @@@ static int rev_same_tree_as_empty(struc
                return 0;
  
        tree_difference = REV_TREE_SAME;
 -      DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
 +      revs->pruning.flags.has_changes = 0;
        retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
  
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
@@@ -799,9 -792,17 +801,17 @@@ static int add_parents_to_list(struct r
  
        for (parent = commit->parents; parent; parent = parent->next) {
                struct commit *p = parent->item;
-               if (parse_commit_gently(p, revs->ignore_missing_links) < 0)
+               int gently = revs->ignore_missing_links ||
+                            revs->exclude_promisor_objects;
+               if (parse_commit_gently(p, gently) < 0) {
+                       if (revs->exclude_promisor_objects &&
+                           is_promisor_object(&p->object.oid)) {
+                               if (revs->first_parent_only)
+                                       break;
+                               continue;
+                       }
                        return -1;
+               }
                if (revs->show_source && !p->util)
                        p->util = commit->util;
                p->object.flags |= left_flag;
@@@ -1412,11 -1413,10 +1422,11 @@@ void init_revisions(struct rev_info *re
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
 -      DIFF_OPT_SET(&revs->pruning, RECURSIVE);
 -      DIFF_OPT_SET(&revs->pruning, QUICK);
 +      revs->pruning.flags.recursive = 1;
 +      revs->pruning.flags.quick = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
 +      revs->pruning.change_fn_data = revs;
        revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
        revs->dense = 1;
        revs->prefix = prefix;
@@@ -1832,7 -1832,7 +1842,7 @@@ static int handle_revision_opt(struct r
                revs->simplify_by_decoration = 1;
                revs->limited = 1;
                revs->prune = 1;
 -              load_ref_decorations(DECORATE_SHORT_REFS);
 +              load_ref_decorations(NULL, DECORATE_SHORT_REFS);
        } else if (!strcmp(arg, "--date-order")) {
                revs->sort_order = REV_SORT_BY_COMMIT_DATE;
                revs->topo_order = 1;
                revs->dense = 0;
        } else if (!strcmp(arg, "--show-all")) {
                revs->show_all = 1;
 +      } else if (!strcmp(arg, "--in-commit-order")) {
 +              revs->tree_blobs_in_commit_order = 1;
        } else if (!strcmp(arg, "--remove-empty")) {
                revs->remove_empty_trees = 1;
        } else if (!strcmp(arg, "--merges")) {
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
 -              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 +              revs->diffopt.flags.recursive = 1;
        } else if (!strcmp(arg, "-t")) {
                revs->diff = 1;
 -              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 -              DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
 +              revs->diffopt.flags.recursive = 1;
 +              revs->diffopt.flags.tree_in_recursive = 1;
        } else if (!strcmp(arg, "-m")) {
                revs->ignore_merges = 0;
        } else if (!strcmp(arg, "-c")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.ignore_case = 1;
 -              DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
 +              revs->diffopt.pickaxe_opts |= DIFF_PICKAXE_IGNORE_CASE;
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
        } else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) {
                revs->limited = 1;
        } else if (!strcmp(arg, "--ignore-missing")) {
                revs->ignore_missing = 1;
+       } else if (!strcmp(arg, "--exclude-promisor-objects")) {
+               if (fetch_if_missing)
+                       die("BUG: exclude_promisor_objects can only be used when fetch_if_missing is 0");
+               revs->exclude_promisor_objects = 1;
        } else {
                int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix);
                if (!opts)
@@@ -2409,21 -2411,18 +2423,21 @@@ int setup_revisions(int argc, const cha
                revs->diff = 1;
  
        /* Pickaxe, diff-filter and rename following need diffs */
 -      if (revs->diffopt.pickaxe ||
 +      if ((revs->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||
            revs->diffopt.filter ||
 -          DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 +          revs->diffopt.flags.follow_renames)
                revs->diff = 1;
  
 +      if (revs->diffopt.objfind)
 +              revs->simplify_history = 0;
 +
        if (revs->topo_order)
                revs->limited = 1;
  
        if (revs->prune_data.nr) {
                copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
                /* Can't prune commits with rename following: the paths change.. */
 -              if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 +              if (!revs->diffopt.flags.follow_renames)
                        revs->prune = 1;
                if (!revs->full_diff)
                        copy_pathspec(&revs->diffopt.pathspec,
@@@ -2845,6 -2844,16 +2859,16 @@@ void reset_revision_walk(void
        clear_object_flags(SEEN | ADDED | SHOWN);
  }
  
+ static int mark_uninteresting(const struct object_id *oid,
+                             struct packed_git *pack,
+                             uint32_t pos,
+                             void *unused)
+ {
+       struct object *o = parse_object(oid);
+       o->flags |= UNINTERESTING | SEEN;
+       return 0;
+ }
  int prepare_revision_walk(struct rev_info *revs)
  {
        int i;
                        }
                }
        }
 -      if (!revs->leak_pending)
 -              object_array_clear(&old_pending);
 +      object_array_clear(&old_pending);
  
        /* Signal whether we need per-parent treesame decoration */
        if (revs->simplify_merges ||
            (revs->limited && limiting_can_increase_treesame(revs)))
                revs->treesame.name = "treesame";
  
+       if (revs->exclude_promisor_objects) {
+               for_each_packed_object(mark_uninteresting, NULL,
+                                      FOR_EACH_OBJECT_PROMISOR_ONLY);
+       }
        if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
                commit_list_sort_by_date(&revs->commits);
        if (revs->no_walk)
diff --combined revision.h
index d7a35c8c9e752cea0ea05ee7e13d696814470a83,5f9a49ca66b5b0e4dfaf0acb862785cf7a2abb5e..3dee97bfb97a4abd636ad7055eb84cf5dea79558
@@@ -4,7 -4,7 +4,7 @@@
  #include "parse-options.h"
  #include "grep.h"
  #include "notes.h"
 -#include "commit.h"
 +#include "pretty.h"
  #include "diff.h"
  
  /* Remember to update object flag allocation in object.h */
@@@ -122,7 -122,9 +122,10 @@@ struct rev_info 
                        ancestry_path:1,
                        first_parent_only:1,
                        line_level_traverse:1,
-                       tree_blobs_in_commit_order:1;
++                      tree_blobs_in_commit_order:1,
+                       /* for internal use only */
+                       exclude_promisor_objects:1;
  
        /* Diff flags */
        unsigned int    diff:1,
                        date_mode_explicit:1,
                        preserve_subject:1;
        unsigned int    disable_stdin:1;
 -      /*
 -       * Set `leak_pending` to prevent `prepare_revision_walk()` from clearing
 -       * the array of pending objects (`pending`). It will still forget about
 -       * the array and its entries, so they really are leaked. This can be
 -       * useful if the `struct object_array` `pending` is copied before
 -       * calling `prepare_revision_walk()`. By setting `leak_pending`, you
 -       * effectively claim ownership of the old array, so you should most
 -       * likely call `object_array_clear(&pending_copy)` once you are done.
 -       * Observe that this is about ownership of the array and its entries,
 -       * not the commits referenced by those entries.
 -       */
 -      unsigned int    leak_pending:1;
        /* --show-linear-break */
        unsigned int    track_linear:1,
                        track_first_time:1,
diff --combined setup.c
index 8cc34186ce1f918ce5a9c8fc22ea81b7c645ff17,58536bd6eeb496d455953634b6b1943f69ed80ef..c5d55dcee45ca811def540ca43c1e1eb57532a81
+++ b/setup.c
@@@ -312,9 -312,7 +312,9 @@@ int is_git_directory(const char *suspec
        size_t len;
  
        /* Check worktree-related signatures */
 -      strbuf_addf(&path, "%s/HEAD", suspect);
 +      strbuf_addstr(&path, suspect);
 +      strbuf_complete(&path, '/');
 +      strbuf_addstr(&path, "HEAD");
        if (validate_headref(path.buf))
                goto done;
  
@@@ -422,7 -420,11 +422,11 @@@ static int check_repo_format(const cha
                        ;
                else if (!strcmp(ext, "preciousobjects"))
                        data->precious_objects = git_config_bool(var, value);
-               else
+               else if (!strcmp(ext, "partialclone")) {
+                       if (!value)
+                               return config_error_nonbool(var);
+                       data->partial_clone = xstrdup(value);
+               } else
                        string_list_append(&data->unknown_extensions, ext);
        } else if (strcmp(var, "core.bare") == 0) {
                data->is_bare = git_config_bool(var, value);
        return 0;
  }
  
 -static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 +static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
  {
        struct strbuf sb = STRBUF_INIT;
        struct strbuf err = STRBUF_INIT;
 -      struct repository_format candidate;
        int has_common;
  
        has_common = get_common_dir(&sb, gitdir);
        strbuf_addstr(&sb, "/config");
 -      read_repository_format(&candidate, sb.buf);
 +      read_repository_format(candidate, sb.buf);
        strbuf_release(&sb);
  
        /*
         * we treat a missing config as a silent "ok", even when nongit_ok
         * is unset.
         */
 -      if (candidate.version < 0)
 +      if (candidate->version < 0)
                return 0;
  
 -      if (verify_repository_format(&candidate, &err) < 0) {
 +      if (verify_repository_format(candidate, &err) < 0) {
                if (nongit_ok) {
                        warning("%s", err.buf);
                        strbuf_release(&err);
                die("%s", err.buf);
        }
  
 -      repository_format_precious_objects = candidate.precious_objects;
 -      repository_format_partial_clone = candidate.partial_clone;
 -      string_list_clear(&candidate.unknown_extensions, 0);
 +      repository_format_precious_objects = candidate->precious_objects;
++      repository_format_partial_clone = candidate->partial_clone;
 +      string_list_clear(&candidate->unknown_extensions, 0);
        if (!has_common) {
 -              if (candidate.is_bare != -1) {
 -                      is_bare_repository_cfg = candidate.is_bare;
 +              if (candidate->is_bare != -1) {
 +                      is_bare_repository_cfg = candidate->is_bare;
                        if (is_bare_repository_cfg == 1)
                                inside_work_tree = -1;
                }
 -              if (candidate.work_tree) {
 +              if (candidate->work_tree) {
                        free(git_work_tree_cfg);
 -                      git_work_tree_cfg = candidate.work_tree;
 +                      git_work_tree_cfg = candidate->work_tree;
                        inside_work_tree = -1;
                }
        } else {
 -              free(candidate.work_tree);
 +              free(candidate->work_tree);
        }
  
        return 0;
@@@ -488,7 -492,6 +493,7 @@@ int read_repository_format(struct repos
        memset(format, 0, sizeof(*format));
        format->version = -1;
        format->is_bare = -1;
 +      format->hash_algo = GIT_HASH_SHA1;
        string_list_init(&format->unknown_extensions, 1);
        git_config_from_file(check_repo_format, path, format);
        return format->version;
@@@ -625,7 -628,6 +630,7 @@@ cleanup_return
  
  static const char *setup_explicit_git_dir(const char *gitdirenv,
                                          struct strbuf *cwd,
 +                                        struct repository_format *repo_fmt,
                                          int *nongit_ok)
  {
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
                die("Not a git repository: '%s'", gitdirenv);
        }
  
 -      if (check_repository_format_gently(gitdirenv, nongit_ok)) {
 +      if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
                free(gitfile);
                return NULL;
        }
  
  static const char *setup_discovered_git_dir(const char *gitdir,
                                            struct strbuf *cwd, int offset,
 +                                          struct repository_format *repo_fmt,
                                            int *nongit_ok)
  {
 -      if (check_repository_format_gently(gitdir, nongit_ok))
 +      if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))
                return NULL;
  
        /* --work-tree is set without --git-dir; use discovered one */
                        gitdir = to_free = real_pathdup(gitdir, 1);
                if (chdir(cwd->buf))
                        die_errno("Could not come back to cwd");
 -              ret = setup_explicit_git_dir(gitdir, cwd, nongit_ok);
 +              ret = setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
                free(to_free);
                return ret;
        }
  
  /* #16.1, #17.1, #20.1, #21.1, #22.1 (see t1510) */
  static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
 +                                    struct repository_format *repo_fmt,
                                      int *nongit_ok)
  {
        int root_len;
  
 -      if (check_repository_format_gently(".", nongit_ok))
 +      if (check_repository_format_gently(".", repo_fmt, nongit_ok))
                return NULL;
  
        setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
                gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
                if (chdir(cwd->buf))
                        die_errno("Could not come back to cwd");
 -              return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
 +              return setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
        }
  
        inside_git_dir = 1;
@@@ -926,7 -926,7 +931,7 @@@ static enum discovery_result setup_git_
         * - ../.git
         * - ../.git/
         * - ../ (bare)
 -       * - ../../.git/
 +       * - ../../.git
         *   etc.
         */
        one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
@@@ -1029,7 -1029,6 +1034,7 @@@ const char *setup_git_directory_gently(
        static struct strbuf cwd = STRBUF_INIT;
        struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
        const char *prefix;
 +      struct repository_format repo_fmt;
  
        /*
         * We may have read an incomplete configuration before
                prefix = NULL;
                break;
        case GIT_DIR_EXPLICIT:
 -              prefix = setup_explicit_git_dir(gitdir.buf, &cwd, nongit_ok);
 +              prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_DISCOVERED:
                if (dir.len < cwd.len && chdir(dir.buf))
                        die(_("Cannot change to '%s'"), dir.buf);
                prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
 -                                                nongit_ok);
 +                                                &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_BARE:
                if (dir.len < cwd.len && chdir(dir.buf))
                        die(_("Cannot change to '%s'"), dir.buf);
 -              prefix = setup_bare_git_dir(&cwd, dir.len, nongit_ok);
 +              prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_HIT_CEILING:
                prefix = setup_nongit(cwd.buf, nongit_ok);
                        repo_set_gitdir(the_repository, gitdir);
                        setup_git_env();
                }
 +              if (startup_info->have_repository)
 +                      repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
        }
  
        strbuf_release(&dir);
@@@ -1177,8 -1174,7 +1182,8 @@@ int git_config_perm(const char *var, co
  
  void check_repository_format(void)
  {
 -      check_repository_format_gently(get_git_dir(), NULL);
 +      struct repository_format repo_fmt;
 +      check_repository_format_gently(get_git_dir(), &repo_fmt, NULL);
        startup_info->have_repository = 1;
  }
  
diff --combined sha1_file.c
index 3da70ac650a8cdeca6ef8a6a424a7740d38267d5,dd956e2bb6a23909554e4ec01d8b683b43a83f1f..2e58f5560a63426ab1b9848434ea0e1ac3ba1fba
@@@ -29,6 -29,7 +29,7 @@@
  #include "mergesort.h"
  #include "quote.h"
  #include "packfile.h"
+ #include "fetch-object.h"
  
  const unsigned char null_sha1[GIT_MAX_RAWSZ];
  const struct object_id null_oid;
@@@ -39,64 -40,6 +40,64 @@@ const struct object_id empty_blob_oid 
        EMPTY_BLOB_SHA1_BIN_LITERAL
  };
  
 +static void git_hash_sha1_init(void *ctx)
 +{
 +      git_SHA1_Init((git_SHA_CTX *)ctx);
 +}
 +
 +static void git_hash_sha1_update(void *ctx, const void *data, size_t len)
 +{
 +      git_SHA1_Update((git_SHA_CTX *)ctx, data, len);
 +}
 +
 +static void git_hash_sha1_final(unsigned char *hash, void *ctx)
 +{
 +      git_SHA1_Final(hash, (git_SHA_CTX *)ctx);
 +}
 +
 +static void git_hash_unknown_init(void *ctx)
 +{
 +      die("trying to init unknown hash");
 +}
 +
 +static void git_hash_unknown_update(void *ctx, const void *data, size_t len)
 +{
 +      die("trying to update unknown hash");
 +}
 +
 +static void git_hash_unknown_final(unsigned char *hash, void *ctx)
 +{
 +      die("trying to finalize unknown hash");
 +}
 +
 +const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
 +      {
 +              NULL,
 +              0x00000000,
 +              0,
 +              0,
 +              0,
 +              git_hash_unknown_init,
 +              git_hash_unknown_update,
 +              git_hash_unknown_final,
 +              NULL,
 +              NULL,
 +      },
 +      {
 +              "sha-1",
 +              /* "sha1", big-endian */
 +              0x73686131,
 +              sizeof(git_SHA_CTX),
 +              GIT_SHA1_RAWSZ,
 +              GIT_SHA1_HEXSZ,
 +              git_hash_sha1_init,
 +              git_hash_sha1_update,
 +              git_hash_sha1_final,
 +              &empty_tree_oid,
 +              &empty_blob_oid,
 +      },
 +};
 +
  /*
   * This is meant to hold a *small* number of objects that you would
   * want read_sha1_file() to be able to return, but yet you do not want
@@@ -132,18 -75,6 +133,18 @@@ static struct cached_object *find_cache
        return NULL;
  }
  
 +
 +static enum safe_crlf get_safe_crlf(unsigned flags)
 +{
 +      if (flags & HASH_RENORMALIZE)
 +              return SAFE_CRLF_RENORMALIZE;
 +      else if (flags & HASH_WRITE_OBJECT)
 +              return safe_crlf;
 +      else
 +              return SAFE_CRLF_FALSE;
 +}
 +
 +
  int mkdir_in_gitdir(const char *path)
  {
        if (mkdir(path, 0777)) {
@@@ -474,9 -405,6 +475,9 @@@ static void link_alt_odb_entries(const 
        struct strbuf objdirbuf = STRBUF_INIT;
        struct strbuf entry = STRBUF_INIT;
  
 +      if (!alt || !*alt)
 +              return;
 +
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
                                relative_base);
@@@ -529,19 -457,19 +530,19 @@@ struct alternate_object_database *alloc
  
  void add_to_alternates_file(const char *reference)
  {
 -      struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock = LOCK_INIT;
        char *alts = git_pathdup("objects/info/alternates");
        FILE *in, *out;
 +      int found = 0;
  
 -      hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
 -      out = fdopen_lock_file(lock, "w");
 +      hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
 +      out = fdopen_lock_file(&lock, "w");
        if (!out)
                die_errno("unable to fdopen alternates lockfile");
  
        in = fopen(alts, "r");
        if (in) {
                struct strbuf line = STRBUF_INIT;
 -              int found = 0;
  
                while (strbuf_getline(&line, in) != EOF) {
                        if (!strcmp(reference, line.buf)) {
  
                strbuf_release(&line);
                fclose(in);
 -
 -              if (found) {
 -                      rollback_lock_file(lock);
 -                      lock = NULL;
 -              }
        }
        else if (errno != ENOENT)
                die_errno("unable to read alternates file");
  
 -      if (lock) {
 +      if (found) {
 +              rollback_lock_file(&lock);
 +      } else {
                fprintf_or_die(out, "%s\n", reference);
 -              if (commit_lock_file(lock))
 +              if (commit_lock_file(&lock))
                        die_errno("unable to move new alternates file into place");
                if (alt_odb_tail)
                        link_alt_odb_entries(reference, '\n', NULL, 0);
@@@ -677,6 -608,7 +678,6 @@@ void prepare_alt_odb(void
                return;
  
        alt = getenv(ALTERNATE_DB_ENVIRONMENT);
 -      if (!alt) alt = "";
  
        alt_odb_tail = &alt_odb_list;
        link_alt_odb_entries(alt, PATH_SEP, NULL, 0);
@@@ -1213,6 -1145,8 +1214,8 @@@ static int sha1_loose_object_info(cons
        return (status < 0) ? status : 0;
  }
  
+ int fetch_if_missing = 1;
  int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
  {
        static struct object_info blank_oi = OBJECT_INFO_INIT;
        const unsigned char *real = (flags & OBJECT_INFO_LOOKUP_REPLACE) ?
                                    lookup_replace_object(sha1) :
                                    sha1;
+       int already_retried = 0;
  
 +      if (is_null_sha1(real))
 +              return -1;
 +
        if (!oi)
                oi = &blank_oi;
  
                }
        }
  
-       if (!find_pack_entry(real, &e)) {
+       while (1) {
+               if (find_pack_entry(real, &e))
+                       break;
                /* Most likely it's a loose object. */
                if (!sha1_loose_object_info(real, oi, flags))
                        return 0;
  
                /* Not a loose object; someone else may have just packed it. */
-               if (flags & OBJECT_INFO_QUICK) {
-                       return -1;
-               } else {
-                       reprepare_packed_git();
-                       if (!find_pack_entry(real, &e))
-                               return -1;
+               reprepare_packed_git();
+               if (find_pack_entry(real, &e))
+                       break;
+               /* Check if it is a missing object */
+               if (fetch_if_missing && repository_format_partial_clone &&
+                   !already_retried) {
+                       /*
+                        * TODO Investigate haveing fetch_object() return
+                        * TODO error/success and stopping the music here.
+                        */
+                       fetch_object(repository_format_partial_clone, real);
+                       already_retried = 1;
+                       continue;
                }
+               return -1;
        }
  
        if (oi == &blank_oi)
                 * information below, so return early.
                 */
                return 0;
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
                mark_bad_packed_object(e.p, real);
@@@ -1736,7 -1680,7 +1752,7 @@@ static void check_tag(const void *buf, 
                die("corrupt tag");
  }
  
 -static int index_mem(unsigned char *sha1, void *buf, size_t size,
 +static int index_mem(struct object_id *oid, void *buf, size_t size,
                     enum object_type type,
                     const char *path, unsigned flags)
  {
        if ((type == OBJ_BLOB) && path) {
                struct strbuf nbuf = STRBUF_INIT;
                if (convert_to_git(&the_index, path, buf, size, &nbuf,
 -                                 write_object ? safe_crlf : SAFE_CRLF_FALSE)) {
 +                                 get_safe_crlf(flags))) {
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
                }
        }
  
        if (write_object)
 -              ret = write_sha1_file(buf, size, typename(type), sha1);
 +              ret = write_sha1_file(buf, size, typename(type), oid->hash);
        else
 -              ret = hash_sha1_file(buf, size, typename(type), sha1);
 +              ret = hash_sha1_file(buf, size, typename(type), oid->hash);
        if (re_allocated)
                free(buf);
        return ret;
  }
  
 -static int index_stream_convert_blob(unsigned char *sha1, int fd,
 +static int index_stream_convert_blob(struct object_id *oid, int fd,
                                     const char *path, unsigned flags)
  {
        int ret;
        assert(would_convert_to_git_filter_fd(path));
  
        convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
 -                               write_object ? safe_crlf : SAFE_CRLF_FALSE);
 +                               get_safe_crlf(flags));
  
        if (write_object)
                ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
 -                                    sha1);
 +                                    oid->hash);
        else
                ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
 -                                   sha1);
 +                                   oid->hash);
        strbuf_release(&sbuf);
        return ret;
  }
  
 -static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
 +static int index_pipe(struct object_id *oid, int fd, enum object_type type,
                      const char *path, unsigned flags)
  {
        struct strbuf sbuf = STRBUF_INIT;
        int ret;
  
        if (strbuf_read(&sbuf, fd, 4096) >= 0)
 -              ret = index_mem(sha1, sbuf.buf, sbuf.len, type, path, flags);
 +              ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags);
        else
                ret = -1;
        strbuf_release(&sbuf);
  
  #define SMALL_FILE_SIZE (32*1024)
  
 -static int index_core(unsigned char *sha1, int fd, size_t size,
 +static int index_core(struct object_id *oid, int fd, size_t size,
                      enum object_type type, const char *path,
                      unsigned flags)
  {
        int ret;
  
        if (!size) {
 -              ret = index_mem(sha1, "", size, type, path, flags);
 +              ret = index_mem(oid, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                ssize_t read_result = read_in_full(fd, buf, size);
                        ret = error("short read while indexing %s",
                                    path ? path : "<unknown>");
                else
 -                      ret = index_mem(sha1, buf, size, type, path, flags);
 +                      ret = index_mem(oid, buf, size, type, path, flags);
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 -              ret = index_mem(sha1, buf, size, type, path, flags);
 +              ret = index_mem(oid, buf, size, type, path, flags);
                munmap(buf, size);
        }
        return ret;
@@@ -1874,12 -1818,12 +1890,12 @@@ int index_fd(struct object_id *oid, in
         * die() for large files.
         */
        if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
 -              ret = index_stream_convert_blob(oid->hash, fd, path, flags);
 +              ret = index_stream_convert_blob(oid, fd, path, flags);
        else if (!S_ISREG(st->st_mode))
 -              ret = index_pipe(oid->hash, fd, type, path, flags);
 +              ret = index_pipe(oid, fd, type, path, flags);
        else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
                 (path && would_convert_to_git(&the_index, path)))
 -              ret = index_core(oid->hash, fd, xsize_t(st->st_size), type, path,
 +              ret = index_core(oid, fd, xsize_t(st->st_size), type, path,
                                 flags);
        else
                ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
@@@ -1913,7 -1857,7 +1929,7 @@@ int index_path(struct object_id *oid, c
                strbuf_release(&sb);
                break;
        case S_IFDIR:
 -              return resolve_gitlink_ref(path, "HEAD", oid->hash);
 +              return resolve_gitlink_ref(path, "HEAD", oid);
        default:
                return error("%s: unsupported file type", path);
        }
@@@ -1956,7 -1900,6 +1972,7 @@@ int for_each_file_in_obj_subdir(unsigne
        DIR *dir;
        struct dirent *de;
        int r = 0;
 +      struct object_id oid;
  
        if (subdir_nr > 0xff)
                BUG("invalid loose object subdirectory: %x", subdir_nr);
        origlen = path->len;
        strbuf_complete(path, '/');
        strbuf_addf(path, "%02x", subdir_nr);
 -      baselen = path->len;
  
        dir = opendir(path->buf);
        if (!dir) {
                return r;
        }
  
 +      oid.hash[0] = subdir_nr;
 +      strbuf_addch(path, '/');
 +      baselen = path->len;
 +
        while ((de = readdir(dir))) {
 +              size_t namelen;
                if (is_dot_or_dotdot(de->d_name))
                        continue;
  
 +              namelen = strlen(de->d_name);
                strbuf_setlen(path, baselen);
 -              strbuf_addf(path, "/%s", de->d_name);
 -
 -              if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
 -                      char hex[GIT_MAX_HEXSZ+1];
 -                      struct object_id oid;
 -
 -                      xsnprintf(hex, sizeof(hex), "%02x%s",
 -                                subdir_nr, de->d_name);
 -                      if (!get_oid_hex(hex, &oid)) {
 -                              if (obj_cb) {
 -                                      r = obj_cb(&oid, path->buf, data);
 -                                      if (r)
 -                                              break;
 -                              }
 -                              continue;
 +              strbuf_add(path, de->d_name, namelen);
 +              if (namelen == GIT_SHA1_HEXSZ - 2 &&
 +                  !hex_to_bytes(oid.hash + 1, de->d_name,
 +                                GIT_SHA1_RAWSZ - 1)) {
 +                      if (obj_cb) {
 +                              r = obj_cb(&oid, path->buf, data);
 +                              if (r)
 +                                      break;
                        }
 +                      continue;
                }
  
                if (cruft_cb) {
        }
        closedir(dir);
  
 -      strbuf_setlen(path, baselen);
 +      strbuf_setlen(path, baselen - 1);
        if (!r && subdir_cb)
                r = subdir_cb(subdir_nr, path->buf, data);
  
diff --combined transport.c
index fc802260f61c7496f4250b15ef6f5ab8dd316898,f2fbc6f968c6ccca40204db951bcff358e6ef301..e82db773fd2383a998dff45b2b06eed668b5bebc
@@@ -17,7 -17,6 +17,7 @@@
  #include "string-list.h"
  #include "sha1-array.h"
  #include "sigchain.h"
 +#include "transport-internal.h"
  
  static void set_upstreams(struct transport *transport, struct ref *refs,
        int pretend)
@@@ -161,6 -160,12 +161,12 @@@ static int set_git_option(struct git_tr
        } else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {
                opts->deepen_relative = !!value;
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_FROM_PROMISOR)) {
+               opts->from_promisor = !!value;
+               return 0;
+       } else if (!strcmp(name, TRANS_OPT_NO_DEPENDENTS)) {
+               opts->no_dependents = !!value;
+               return 0;
        }
        return 1;
  }
@@@ -229,6 -234,8 +235,8 @@@ static int fetch_refs_via_pack(struct t
                data->options.check_self_contained_and_connected;
        args.cloning = transport->cloning;
        args.update_shallow = data->options.update_shallow;
+       args.from_promisor = data->options.from_promisor;
+       args.no_dependents = data->options.no_dependents;
  
        if (!data->got_remote_heads) {
                connect_setup(transport, 0);
@@@ -306,8 -313,8 +314,8 @@@ void transport_update_tracking_ref(stru
                if (ref->deletion) {
                        delete_ref(NULL, rs.dst, NULL, 0);
                } else
 -                      update_ref("update by push", rs.dst,
 -                                      ref->new_oid.hash, NULL, 0, 0);
 +                      update_ref("update by push", rs.dst, &ref->new_oid,
 +                                 NULL, 0, 0);
                free(rs.dst);
        }
  }
@@@ -608,15 -615,6 +616,15 @@@ static int disconnect_git(struct transp
        return 0;
  }
  
 +static struct transport_vtable taken_over_vtable = {
 +      NULL,
 +      get_refs_via_connect,
 +      fetch_refs_via_pack,
 +      git_transport_push,
 +      NULL,
 +      disconnect_git
 +};
 +
  void transport_take_over(struct transport *transport,
                         struct child_process *child)
  {
        data->got_remote_heads = 0;
        transport->data = data;
  
 -      transport->set_option = NULL;
 -      transport->get_refs_list = get_refs_via_connect;
 -      transport->fetch = fetch_refs_via_pack;
 -      transport->push = NULL;
 -      transport->push_refs = git_transport_push;
 -      transport->disconnect = disconnect_git;
 +      transport->vtable = &taken_over_vtable;
        transport->smart_options = &(data->options);
  
        transport->cannot_reuse = 1;
@@@ -757,24 -760,6 +765,24 @@@ void transport_check_allowed(const cha
                die("transport '%s' not allowed", type);
  }
  
 +static struct transport_vtable bundle_vtable = {
 +      NULL,
 +      get_refs_from_bundle,
 +      fetch_refs_from_bundle,
 +      NULL,
 +      NULL,
 +      close_bundle
 +};
 +
 +static struct transport_vtable builtin_smart_vtable = {
 +      NULL,
 +      get_refs_via_connect,
 +      fetch_refs_via_pack,
 +      git_transport_push,
 +      connect_git,
 +      disconnect_git
 +};
 +
  struct transport *transport_get(struct remote *remote, const char *url)
  {
        const char *helper;
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
                transport_check_allowed("file");
                ret->data = data;
 -              ret->get_refs_list = get_refs_from_bundle;
 -              ret->fetch = fetch_refs_from_bundle;
 -              ret->disconnect = close_bundle;
 +              ret->vtable = &bundle_vtable;
                ret->smart_options = NULL;
        } else if (!is_url(url)
                || starts_with(url, "file://")
                 */
                struct git_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
 -              ret->set_option = NULL;
 -              ret->get_refs_list = get_refs_via_connect;
 -              ret->fetch = fetch_refs_via_pack;
 -              ret->push_refs = git_transport_push;
 -              ret->connect = connect_git;
 -              ret->disconnect = disconnect_git;
 +              ret->vtable = &builtin_smart_vtable;
                ret->smart_options = &(data->options);
  
                data->conn = NULL;
@@@ -860,9 -852,9 +868,9 @@@ int transport_set_option(struct transpo
                git_reports = set_git_option(transport->smart_options,
                                             name, value);
  
 -      if (transport->set_option)
 -              protocol_reports = transport->set_option(transport, name,
 -                                                      value);
 +      if (transport->vtable->set_option)
 +              protocol_reports = transport->vtable->set_option(transport,
 +                                                               name, value);
  
        /* If either report is 0, report 0 (success). */
        if (!git_reports || !protocol_reports)
@@@ -985,7 -977,13 +993,7 @@@ int transport_push(struct transport *tr
        *reject_reasons = 0;
        transport_verify_remote_names(refspec_nr, refspec);
  
 -      if (transport->push) {
 -              /* Maybe FIXME. But no important transport uses this case. */
 -              if (flags & TRANSPORT_PUSH_SET_UPSTREAM)
 -                      die("This transport does not support using --set-upstream");
 -
 -              return transport->push(transport, refspec_nr, refspec, flags);
 -      } else if (transport->push_refs) {
 +      if (transport->vtable->push_refs) {
                struct ref *remote_refs;
                struct ref *local_refs = get_local_heads();
                int match_flags = MATCH_REFS_NONE;
                if (check_push_refs(local_refs, refspec_nr, refspec) < 0)
                        return -1;
  
 -              remote_refs = transport->get_refs_list(transport, 1);
 +              remote_refs = transport->vtable->get_refs_list(transport, 1);
  
                if (flags & TRANSPORT_PUSH_ALL)
                        match_flags |= MATCH_REFS_ALL;
                }
  
                if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY))
 -                      push_ret = transport->push_refs(transport, remote_refs, flags);
 +                      push_ret = transport->vtable->push_refs(transport, remote_refs, flags);
                else
                        push_ret = 0;
                err = push_had_errors(remote_refs);
  const struct ref *transport_get_remote_refs(struct transport *transport)
  {
        if (!transport->got_remote_refs) {
 -              transport->remote_refs = transport->get_refs_list(transport, 0);
 +              transport->remote_refs = transport->vtable->get_refs_list(transport, 0);
                transport->got_remote_refs = 1;
        }
  
@@@ -1144,7 -1142,7 +1152,7 @@@ int transport_fetch_refs(struct transpo
                        heads[nr_heads++] = rm;
        }
  
 -      rc = transport->fetch(transport, nr_heads, heads);
 +      rc = transport->vtable->fetch(transport, nr_heads, heads);
  
        free(heads);
        return rc;
@@@ -1161,8 -1159,8 +1169,8 @@@ void transport_unlock_pack(struct trans
  int transport_connect(struct transport *transport, const char *name,
                      const char *exec, int fd[2])
  {
 -      if (transport->connect)
 -              return transport->connect(transport, name, exec, fd);
 +      if (transport->vtable->connect)
 +              return transport->vtable->connect(transport, name, exec, fd);
        else
                die("Operation not supported by protocol");
  }
  int transport_disconnect(struct transport *transport)
  {
        int ret = 0;
 -      if (transport->disconnect)
 -              ret = transport->disconnect(transport);
 +      if (transport->vtable->disconnect)
 +              ret = transport->vtable->disconnect(transport);
        free(transport);
        return ret;
  }
diff --combined transport.h
index 731c78b6795740f8e9bfb80741b0170671b37dd3,c49a8edc537cf9d829aa4b8108737529acf61807..8c3430a5b98dae733892d6aeb718944cd5dea070
@@@ -15,6 -15,8 +15,8 @@@ struct git_transport_options 
        unsigned self_contained_and_connected : 1;
        unsigned update_shallow : 1;
        unsigned deepen_relative : 1;
+       unsigned from_promisor : 1;
+       unsigned no_dependents : 1;
        int depth;
        const char *deepen_since;
        const struct string_list *deepen_not;
@@@ -30,8 -32,6 +32,8 @@@ enum transport_family 
  };
  
  struct transport {
 +      const struct transport_vtable *vtable;
 +
        struct remote *remote;
        const char *url;
        void *data;
         */
        const struct string_list *push_options;
  
 -      /**
 -       * Returns 0 if successful, positive if the option is not
 -       * recognized or is inapplicable, and negative if the option
 -       * is applicable but the value is invalid.
 -       **/
 -      int (*set_option)(struct transport *connection, const char *name,
 -                        const char *value);
 -
 -      /**
 -       * Returns a list of the remote side's refs. In order to allow
 -       * the transport to try to share connections, for_push is a
 -       * hint as to whether the ultimate operation is a push or a fetch.
 -       *
 -       * If the transport is able to determine the remote hash for
 -       * the ref without a huge amount of effort, it should store it
 -       * in the ref's old_sha1 field; otherwise it should be all 0.
 -       **/
 -      struct ref *(*get_refs_list)(struct transport *transport, int for_push);
 -
 -      /**
 -       * Fetch the objects for the given refs. Note that this gets
 -       * an array, and should ignore the list structure.
 -       *
 -       * If the transport did not get hashes for refs in
 -       * get_refs_list(), it should set the old_sha1 fields in the
 -       * provided refs now.
 -       **/
 -      int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
 -
 -      /**
 -       * Push the objects and refs. Send the necessary objects, and
 -       * then, for any refs where peer_ref is set and
 -       * peer_ref->new_oid is different from old_oid, tell the
 -       * remote side to update each ref in the list from old_oid to
 -       * peer_ref->new_oid.
 -       *
 -       * Where possible, set the status for each ref appropriately.
 -       *
 -       * The transport must modify new_sha1 in the ref to the new
 -       * value if the remote accepted the change. Note that this
 -       * could be a different value from peer_ref->new_oid if the
 -       * process involved generating new commits.
 -       **/
 -      int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
 -      int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
 -      int (*connect)(struct transport *connection, const char *name,
 -                     const char *executable, int fd[2]);
 -
 -      /** get_refs_list(), fetch(), and push_refs() can keep
 -       * resources (such as a connection) reserved for further
 -       * use. disconnect() releases these resources.
 -       **/
 -      int (*disconnect)(struct transport *connection);
        char *pack_lockfile;
        signed verbose : 3;
        /**
@@@ -159,6 -212,15 +161,15 @@@ void transport_check_allowed(const cha
  /* Send push certificates */
  #define TRANS_OPT_PUSH_CERT "pushcert"
  
+ /* Indicate that these objects are being fetched by a promisor */
+ #define TRANS_OPT_FROM_PROMISOR "from-promisor"
+ /*
+  * Indicate that only the objects wanted need to be fetched, not their
+  * dependents
+  */
+ #define TRANS_OPT_NO_DEPENDENTS "no-dependents"
  /**
   * Returns 0 if the option was used, non-zero otherwise. Prints a
   * message to stderr if the option is not used.