Merge branch 'gs/retire-mru' into next
authorJunio C Hamano <gitster@pobox.com>
Wed, 7 Feb 2018 22:55:50 +0000 (14:55 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 7 Feb 2018 22:55:50 +0000 (14:55 -0800)
Retire mru API as it does not give enough abstraction over
underlying list API to be worth it.

* gs/retire-mru:
mru: Replace mru.[ch] with list.h implementation

1  2 
Makefile
builtin/pack-objects.c
cache.h
packfile.c
sha1_file.c
diff --combined Makefile
index eb74e6cf3c4b25ee639c192cefcddf8f235ffc0d,4a79ec5012adf0b7ca3731784ac3bc39442fe0a5..5bcd83ddf32707f8b98f650fd08d8eac5edf14af
+++ 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 NO_MMAP if you want to avoid mmap.
  #
 +# Define MMAP_PREVENTS_DELETE if a file that is currently mmapped cannot be
 +# deleted or cannot be replaced using rename().
 +#
  # Define NO_SYS_POLL_H if you don't have sys/poll.h.
  #
  # Define NO_POLL if you do not have or don't want to use poll().
  #
  # 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 -470,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 -483,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 -643,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 -724,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 -754,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,10 -789,8 +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
@@@ -819,8 -804,6 +819,8 @@@ LIB_OBJS += levenshtein.
  LIB_OBJS += line-log.o
  LIB_OBJS += line-range.o
  LIB_OBJS += list-objects.o
 +LIB_OBJS += list-objects-filter.o
 +LIB_OBJS += list-objects-filter-options.o
  LIB_OBJS += ll-merge.o
  LIB_OBJS += lockfile.o
  LIB_OBJS += log-tree.o
@@@ -831,14 -814,12 +831,13 @@@ LIB_OBJS += merge.
  LIB_OBJS += merge-blobs.o
  LIB_OBJS += merge-recursive.o
  LIB_OBJS += mergesort.o
- LIB_OBJS += mru.o
  LIB_OBJS += name-hash.o
  LIB_OBJS += notes.o
  LIB_OBJS += notes-cache.o
  LIB_OBJS += notes-merge.o
  LIB_OBJS += notes-utils.o
  LIB_OBJS += object.o
 +LIB_OBJS += oidmap.o
  LIB_OBJS += oidset.o
  LIB_OBJS += packfile.o
  LIB_OBJS += pack-bitmap.o
@@@ -860,7 -841,6 +859,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
@@@ -1273,6 -1247,7 +1272,6 @@@ ifndef NO_OPENSS
        endif
  else
        BASIC_CFLAGS += -DNO_OPENSSL
 -      BLK_SHA1 = 1
        OPENSSL_LIBSSL =
  endif
  ifdef NO_OPENSSL
@@@ -1415,9 -1390,6 +1414,9 @@@ els
                COMPAT_OBJS += compat/win32mmap.o
        endif
  endif
 +ifdef MMAP_PREVENTS_DELETE
 +      BASIC_CFLAGS += -DMMAP_PREVENTS_DELETE
 +endif
  ifdef OBJECT_CREATION_USES_RENAMES
        COMPAT_CFLAGS += -DOBJECT_CREATION_MODE=1
  endif
@@@ -1542,6 -1514,9 +1541,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
@@@ -1728,10 -1703,8 +1727,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))
@@@ -1740,7 -1713,6 +1739,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))
@@@ -1841,6 -1813,9 +1840,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)'
  
@@@ -1906,9 -1881,7 +1905,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) $@ && \
@@@ -1923,8 -1896,7 +1922,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)|' \
@@@ -1960,20 -1932,30 +1959,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
 -
 -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) $@+
 +$(SCRIPT_PERL_GEN):
  
 -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{' \
@@@ -2297,21 -2279,6 +2296,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]' \
@@@ -2375,7 -2342,6 +2374,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))'\' >>$@+
@@@ -2572,9 -2538,7 +2571,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
@@@ -2624,17 -2588,12 +2623,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
  
@@@ -2726,7 -2685,7 +2725,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/pack-objects.c
index 0c3d03de487e1645a11d379dc5966cd9b2be81dd,6a0b8e8b9c8092adf6822789b3aa4db59879ead1..83dcbc97731d2b0feeb72289f0ffa4c623952dec
@@@ -15,8 -15,6 +15,8 @@@
  #include "diff.h"
  #include "revision.h"
  #include "list-objects.h"
 +#include "list-objects-filter.h"
 +#include "list-objects-filter-options.h"
  #include "pack-objects.h"
  #include "progress.h"
  #include "refs.h"
@@@ -26,7 -24,7 +26,7 @@@
  #include "reachable.h"
  #include "sha1-array.h"
  #include "argv-array.h"
- #include "mru.h"
+ #include "list.h"
  #include "packfile.h"
  
  static const char *pack_usage[] = {
@@@ -75,24 -73,12 +75,24 @@@ 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;
  
  static unsigned long window_memory_limit = 0;
  
 +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_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
 +};
 +static enum missing_action arg_missing_action;
 +static show_object_fn fn_show_object;
 +
  /*
   * stats
   */
@@@ -165,7 -151,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;
@@@ -353,7 -339,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);
@@@ -571,13 -557,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;
        }
@@@ -806,7 -792,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 */
@@@ -942,13 -928,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;
  
@@@ -1004,7 -990,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)
        int want;
        struct list_head *pos;
  
 -      if (!exclude && local && has_loose_object_nonlocal(sha1))
 +      if (!exclude && local && has_loose_object_nonlocal(oid->hash))
                return 0;
  
        /*
                        return want;
        }
  
-       list_for_each(pos, &packed_git_mru.list) {
-               struct mru *entry = list_entry(pos, struct mru, list);
-               struct packed_git *p = entry->item;
+       list_for_each(pos, &packed_git_mru) {
+               struct packed_git *p = list_entry(pos, struct packed_git, mru);
                off_t offset;
  
                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) {
                        }
                        want = want_found_object(exclude, p);
                        if (!exclude && want > 0)
-                               mru_mark(&packed_git_mru, entry);
+                               list_move(&p->mru, &packed_git_mru);
                        if (want != -1)
                                return want;
                }
        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;
@@@ -1085,17 -1070,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)
  {
@@@ -1159,14 -1144,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;
@@@ -1262,7 -1247,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);
@@@ -1292,7 -1277,7 +1291,7 @@@ static int done_pbase_path_pos(unsigne
        int lo = 0;
        int hi = done_pbase_paths_num;
        while (lo < hi) {
 -              int mi = (hi + lo) / 2;
 +              int mi = lo + (hi - lo) / 2;
                if (done_pbase_paths[mi] == hash)
                        return mi;
                if (done_pbase_paths[mi] < hash)
@@@ -1332,7 -1317,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;
  }
@@@ -2372,7 -2357,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;
@@@ -2386,7 -2371,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;
@@@ -2520,9 -2505,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;
  }
  
 +static void show_object__ma_allow_any(struct object *obj, const char *name, void *data)
 +{
 +      assert(arg_missing_action == MA_ALLOW_ANY);
 +
 +      /*
 +       * Quietly ignore ALL missing objects.  This avoids problems with
 +       * staging them now and getting an odd error later.
 +       */
 +      if (!has_object_file(&obj->oid))
 +              return;
 +
 +      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)
 +{
 +      assert(arg);
 +      assert(!unset);
 +
 +      if (!strcmp(arg, "error")) {
 +              arg_missing_action = MA_ERROR;
 +              fn_show_object = show_object;
 +              return 0;
 +      }
 +
 +      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 {
  };
  
  struct in_pack {
 -      int alloc;
 -      int nr;
 +      unsigned int alloc;
 +      unsigned int nr;
        struct in_pack_object *array;
  };
  
@@@ -2675,7 -2601,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);
@@@ -2716,7 -2642,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;
  }
  
@@@ -2732,7 -2658,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;
                }
@@@ -2792,7 -2718,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");
@@@ -2890,12 -2816,7 +2889,12 @@@ static void get_object_list(int ac, con
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(&revs, show_edge);
 -      traverse_commit_list(&revs, show_commit, show_object, NULL);
 +
 +      if (!fn_show_object)
 +              fn_show_object = show_object;
 +      traverse_commit_list_filtered(&filter_options, &revs,
 +                                    show_commit, fn_show_object, NULL,
 +                                    NULL);
  
        if (unpack_unreachable_expiration) {
                revs.ignore_missing_links = 1;
@@@ -3031,12 -2952,6 +3030,12 @@@ int cmd_pack_objects(int argc, const ch
                         N_("use a bitmap index if available to speed up counting objects")),
                OPT_BOOL(0, "write-bitmap-index", &write_bitmap_index,
                         N_("write a bitmap index together with the pack index")),
 +              OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
 +              { 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)
        if (!rev_list_all || !rev_list_reflog || !rev_list_index)
                unpack_unreachable_expiration = 0;
  
 +      if (filter_options.choice) {
 +              if (!pack_to_stdout)
 +                      die("cannot use --filter without --stdout.");
 +              use_bitmap_index = 0;
 +      }
 +
        /*
         * "soft" reasons not to use bitmaps - for on-disk repack by default we want
         *
diff --combined cache.h
index 666d307043f528ef88d3ee24d66f0d7d12f1c80e,cc09e3b652fc631865e1ec616dfbf0798f922774..7414eb47b7e0489849ba92086d9f8f7311defc7d
+++ b/cache.h
@@@ -4,7 -4,7 +4,7 @@@
  #include "git-compat-util.h"
  #include "strbuf.h"
  #include "hashmap.h"
- #include "mru.h"
+ #include "list.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.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;
@@@ -451,17 -444,6 +451,17 @@@ static inline enum object_type object_t
  #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
  #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
@@@ -619,40 -601,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 -680,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 -715,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 -774,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
   */
  extern int ref_paranoia;
  
 +/*
 + * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
 + */
 +int use_optional_locks(void);
 +
  /*
   * The character that begins a commented line in user-editable file
   * that is subject to stripspace.
@@@ -914,15 -854,11 +914,15 @@@ extern int grafts_replace_parents
  #define GIT_REPO_VERSION 0
  #define GIT_REPO_VERSION_READ 1
  extern int repository_format_precious_objects;
 +extern char *repository_format_partial_clone;
 +extern const char *core_partial_clone_filter_default;
  
  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;
  };
@@@ -1055,22 -991,22 +1055,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) */
@@@ -1308,8 -1244,8 +1308,8 @@@ static inline unsigned int hexval(unsig
   */
  static inline int hex2chr(const char *s)
  {
 -      int val = hexval(s[0]);
 -      return (val < 0) ? val : (val << 4) | hexval(s[1]);
 +      unsigned int val = hexval(s[0]);
 +      return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
  }
  
  /* Convert to/from hex/sha1 representation */
@@@ -1375,13 -1311,6 +1375,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
@@@ -1503,7 -1432,6 +1503,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);
  
@@@ -1638,6 -1566,7 +1638,7 @@@ struct pack_window 
  
  extern struct packed_git {
        struct packed_git *next;
+       struct list_head mru;
        struct pack_window *windows;
        off_t pack_size;
        const void *index_data;
        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" */
  } *packed_git;
  
  /*
-  * A most-recently-used ordered version of the packed_git list, which can
-  * be iterated instead of packed_git (and marked via mru_mark).
+  * A most-recently-used ordered version of the packed_git list.
   */
- extern struct mru packed_git_mru;
+ extern struct list_head packed_git_mru;
  
  struct pack_entry {
        off_t offset;
@@@ -1791,14 -1718,6 +1791,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);
  
@@@ -1994,10 -1913,4 +1994,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 packfile.c
index f4dc4a242bdf8eb4e063adfdd56eb921c11ef5c0,8d3bd5fcb33c3ed8e17bfbe154932a54d0f28b08..2d5774dbc0901895b4dfc0d66cc03d3c253a3e95
@@@ -1,5 -1,5 +1,5 @@@
  #include "cache.h"
- #include "mru.h"
+ #include "list.h"
  #include "pack.h"
  #include "dir.h"
  #include "mergesort.h"
@@@ -8,11 -8,6 +8,11 @@@
  #include "list.h"
  #include "streaming.h"
  #include "sha1-lookup.h"
 +#include "commit.h"
 +#include "object.h"
 +#include "tag.h"
 +#include "tree-walk.h"
 +#include "tree.h"
  
  char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@@ -45,7 -40,7 +45,7 @@@ static unsigned int pack_max_fds
  static size_t peak_pack_mapped;
  static size_t pack_mapped;
  struct packed_git *packed_git;
struct mru packed_git_mru = {{&packed_git_mru.list, &packed_git_mru.list}};
LIST_HEAD(packed_git_mru);
  
  #define SZ_FMT PRIuMAX
  static inline uintmax_t sz_fmt(size_t s) { return s; }
@@@ -447,7 -442,6 +447,7 @@@ static int open_packed_git_1(struct pac
        unsigned char sha1[20];
        unsigned char *idx_sha1;
        long fd_flag;
 +      ssize_t read_result;
  
        if (!p->index_data && open_pack_index(p))
                return error("packfile %s index unavailable", p->pack_name);
                return error("cannot set FD_CLOEXEC");
  
        /* Verify we recognize this pack file format. */
 -      if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
 +      read_result = read_in_full(p->pack_fd, &hdr, sizeof(hdr));
 +      if (read_result < 0)
 +              return error_errno("error reading from %s", p->pack_name);
 +      if (read_result != sizeof(hdr))
                return error("file %s is far too short to be a packfile", p->pack_name);
        if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
                return error("file %s is not a GIT packfile", p->pack_name);
                             p->num_objects);
        if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
                return error("end of packfile %s is unavailable", p->pack_name);
 -      if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
 +      read_result = read_in_full(p->pack_fd, sha1, sizeof(sha1));
 +      if (read_result < 0)
 +              return error_errno("error reading from %s", p->pack_name);
 +      if (read_result != sizeof(sha1))
                return error("packfile %s signature is unavailable", p->pack_name);
        idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
        if (hashcmp(sha1, idx_sha1))
@@@ -648,10 -636,10 +648,10 @@@ struct packed_git *add_packed_git(cons
                return NULL;
  
        /*
 -       * ".pack" is long enough to hold any suffix we're adding (and
 +       * ".promisor" is long enough to hold any suffix we're adding (and
         * the use xsnprintf double-checks that)
         */
 -      alloc = st_add3(path_len, strlen(".pack"), 1);
 +      alloc = st_add3(path_len, strlen(".promisor"), 1);
        p = alloc_packed_git(alloc);
        memcpy(p->pack_name, path, path_len);
  
        if (!access(p->pack_name, F_OK))
                p->pack_keep = 1;
  
 +      xsnprintf(p->pack_name + path_len, alloc - path_len, ".promisor");
 +      if (!access(p->pack_name, F_OK))
 +              p->pack_promisor = 1;
 +
        xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
        if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
                free(p);
@@@ -790,8 -774,7 +790,8 @@@ static void prepare_packed_git_one(cha
                if (ends_with(de->d_name, ".idx") ||
                    ends_with(de->d_name, ".pack") ||
                    ends_with(de->d_name, ".bitmap") ||
 -                  ends_with(de->d_name, ".keep"))
 +                  ends_with(de->d_name, ".keep") ||
 +                  ends_with(de->d_name, ".promisor"))
                        string_list_append(&garbage, path.buf);
                else
                        report_garbage(PACKDIR_FILE_GARBAGE, path.buf);
@@@ -876,9 -859,10 +876,10 @@@ static void prepare_packed_git_mru(void
  {
        struct packed_git *p;
  
-       mru_clear(&packed_git_mru);
+       INIT_LIST_HEAD(&packed_git_mru);
        for (p = packed_git; p; p = p->next)
-               mru_append(&packed_git_mru, p);
+               list_add_tail(&p->mru, &packed_git_mru);
  }
  
  static int prepare_packed_git_run_once = 0;
@@@ -1753,7 -1737,7 +1754,7 @@@ off_t find_pack_entry_one(const unsigne
                       sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
  
        while (lo < hi) {
 -              unsigned mi = (lo + hi) / 2;
 +              unsigned mi = lo + (hi - lo) / 2;
                int cmp = hashcmp(index + mi * stride, sha1);
  
                if (debug_lookup)
@@@ -1847,10 -1831,10 +1848,10 @@@ int find_pack_entry(const unsigned cha
        if (!packed_git)
                return 0;
  
-       list_for_each(pos, &packed_git_mru.list) {
-               struct mru *p = list_entry(pos, struct mru, list);
-               if (fill_pack_entry(sha1, e, p->item)) {
-                       mru_mark(&packed_git_mru, p);
+       list_for_each(pos, &packed_git_mru) {
+               struct packed_git *p = list_entry(pos, struct packed_git, mru);
+               if (fill_pack_entry(sha1, e, p)) {
+                       list_move(&p->mru, &packed_git_mru);
                        return 1;
                }
        }
@@@ -1900,9 -1884,6 +1901,9 @@@ int for_each_packed_object(each_packed_
        for (p = packed_git; p; p = p->next) {
                if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
                        continue;
 +              if ((flags & FOR_EACH_OBJECT_PROMISOR_ONLY) &&
 +                  !p->pack_promisor)
 +                      continue;
                if (open_pack_index(p)) {
                        pack_errors = 1;
                        continue;
        }
        return r ? r : pack_errors;
  }
 +
 +static int add_promisor_object(const struct object_id *oid,
 +                             struct packed_git *pack,
 +                             uint32_t pos,
 +                             void *set_)
 +{
 +      struct oidset *set = set_;
 +      struct object *obj = parse_object(oid);
 +      if (!obj)
 +              return 1;
 +
 +      oidset_insert(set, oid);
 +
 +      /*
 +       * If this is a tree, commit, or tag, the objects it refers
 +       * to are also promisor objects. (Blobs refer to no objects.)
 +       */
 +      if (obj->type == OBJ_TREE) {
 +              struct tree *tree = (struct tree *)obj;
 +              struct tree_desc desc;
 +              struct name_entry entry;
 +              if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
 +                      /*
 +                       * Error messages are given when packs are
 +                       * verified, so do not print any here.
 +                       */
 +                      return 0;
 +              while (tree_entry_gently(&desc, &entry))
 +                      oidset_insert(set, entry.oid);
 +      } else if (obj->type == OBJ_COMMIT) {
 +              struct commit *commit = (struct commit *) obj;
 +              struct commit_list *parents = commit->parents;
 +
 +              oidset_insert(set, &commit->tree->object.oid);
 +              for (; parents; parents = parents->next)
 +                      oidset_insert(set, &parents->item->object.oid);
 +      } else if (obj->type == OBJ_TAG) {
 +              struct tag *tag = (struct tag *) obj;
 +              oidset_insert(set, &tag->tagged->oid);
 +      }
 +      return 0;
 +}
 +
 +int is_promisor_object(const struct object_id *oid)
 +{
 +      static struct oidset promisor_objects;
 +      static int promisor_objects_prepared;
 +
 +      if (!promisor_objects_prepared) {
 +              if (repository_format_partial_clone) {
 +                      for_each_packed_object(add_promisor_object,
 +                                             &promisor_objects,
 +                                             FOR_EACH_OBJECT_PROMISOR_ONLY);
 +              }
 +              promisor_objects_prepared = 1;
 +      }
 +      return oidset_contains(&promisor_objects, oid);
 +}
diff --combined sha1_file.c
index 2e58f5560a63426ab1b9848434ea0e1ac3ba1fba,e664f2d70d1746f1df4bb6cee8eab9efcf042ca1..90998bb596e79dc3dccef9046f87c40bac347af5
  #include "bulk-checkin.h"
  #include "streaming.h"
  #include "dir.h"
- #include "mru.h"
  #include "list.h"
  #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;
@@@ -40,64 -38,6 +39,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
@@@ -133,18 -73,6 +132,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)) {
@@@ -475,9 -403,6 +474,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);
@@@ -530,19 -455,19 +529,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);
@@@ -678,6 -606,7 +677,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);
@@@ -1194,14 -1123,10 +1193,14 @@@ static int sha1_loose_object_info(cons
        } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
                status = error("unable to parse %s header", sha1_to_hex(sha1));
  
 -      if (status >= 0 && oi->contentp)
 +      if (status >= 0 && oi->contentp) {
                *oi->contentp = unpack_sha1_rest(&stream, hdr,
                                                 *oi->sizep, sha1);
 -      else
 +              if (!*oi->contentp) {
 +                      git_inflate_end(&stream);
 +                      status = -1;
 +              }
 +      } else
                git_inflate_end(&stream);
  
        munmap(map, mapsize);
        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);
@@@ -1752,7 -1659,7 +1751,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);
 -              if (size == read_in_full(fd, buf, size))
 -                      ret = index_mem(sha1, buf, size, type, path, flags);
 +              ssize_t read_result = read_in_full(fd, buf, size);
 +              if (read_result < 0)
 +                      ret = error_errno("read error while indexing %s",
 +                                        path ? path : "<unknown>");
 +              else if (read_result != size)
 +                      ret = error("short read while indexing %s",
 +                                  path ? path : "<unknown>");
                else
 -                      ret = error_errno("short read");
 +                      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;
@@@ -1890,12 -1792,12 +1889,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,
@@@ -1929,7 -1831,7 +1928,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);
        }
@@@ -1972,7 -1874,6 +1971,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);