Merge branch 'ks/tree-diff-nway'
authorJunio C Hamano <gitster@pobox.com>
Tue, 3 Jun 2014 19:06:40 +0000 (12:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 3 Jun 2014 19:06:40 +0000 (12:06 -0700)
Instead of running N pair-wise diff-trees when inspecting a
N-parent merge, find the set of paths that were touched by walking
N+1 trees in parallel. These set of paths can then be turned into
N pair-wise diff-tree results to be processed through rename
detections and such. And N=2 case nicely degenerates to the usual
2-way diff-tree, which is very nice.

* ks/tree-diff-nway:
mingw: activate alloca
combine-diff: speed it up, by using multiparent diff tree-walker directly
tree-diff: rework diff_tree() to generate diffs for multiparent cases as well
Portable alloca for Git
tree-diff: reuse base str(buf) memory on sub-tree recursion
tree-diff: no need to call "full" diff_tree_sha1 from show_path()
tree-diff: rework diff_tree interface to be sha1 based
tree-diff: diff_tree() should now be static
tree-diff: remove special-case diff-emitting code for empty-tree cases
tree-diff: simplify tree_entry_pathcmp
tree-diff: show_path prototype is not needed anymore
tree-diff: rename compare_tree_entry -> tree_entry_pathcmp
tree-diff: move all action-taking code out of compare_tree_entry()
tree-diff: don't assume compare_tree_entry() returns -1,0,1
tree-diff: consolidate code for emitting diffs and recursion in one place
tree-diff: show_tree() is not needed
tree-diff: no need to pass match to skip_uninteresting()
tree-diff: no need to manually verify that there is no mode change for a path
combine-diff: move changed-paths scanning logic into its own function
combine-diff: move show_log_first logic/action out of paths scanning

1  2 
Makefile
cache.h
config.mak.uname
configure.ac
diff.c
diff.h
git-compat-util.h
diff --combined Makefile
index a53f3a8326c2e62dc79bae7169d64137ac3dab20,03348066bc17a9ecfc908d332a199fa66af041a9..1f5d92440840ebee642738ed53feb737d5af0fab
+++ b/Makefile
@@@ -30,6 -30,8 +30,8 @@@ all:
  # Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
  # /foo/bar/include and /foo/bar/lib directories.
  #
+ # Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header.
+ #
  # Define NO_CURL if you do not have libcurl installed.  git-http-fetch and
  # git-http-push are not built, and you cannot use http:// and https://
  # transports (neither smart nor dumb).
@@@ -59,9 -61,9 +61,9 @@@
  # FreeBSD can use either, but MinGW and some others need to use
  # libcharset.h's locale_charset() instead.
  #
 -# Define CHARSET_LIB to you need to link with library other than -liconv to
 +# Define CHARSET_LIB to the library you need to link with in order to
  # use locale_charset() function.  On some platforms this needs to set to
 -# -lcharset
 +# -lcharset, on others to -liconv .
  #
  # Define LIBC_CONTAINS_LIBINTL if your gettext implementation doesn't
  # need -lintl when linking.
  #
  # Define NO_MKSTEMPS if you don't have mkstemps in the C library.
  #
 -# Define NO_FNMATCH if you don't have fnmatch in the C library.
 -#
 -# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
 -# FNM_CASEFOLD GNU extension.
 -#
 -# Define NO_WILDMATCH if you do not want to use Git's wildmatch
 -# implementation as fnmatch
 -#
  # Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
  # in the C library.
  #
  #
  # Define NEEDS_LIBINTL_BEFORE_LIBICONV if you need libintl before libiconv.
  #
 -# Define NO_INTPTR_T if you don't have intptr_t nor uintptr_t.
 +# Define NO_INTPTR_T if you don't have intptr_t or uintptr_t.
  #
  # Define NO_UINTMAX_T if you don't have uintmax_t.
  #
  # Define DEFAULT_HELP_FORMAT to "man", "info" or "html"
  # (defaults to "man") if you want to have a different default when
  # "git help" is called without a parameter specifying the format.
 +#
 +# Define TEST_GIT_INDEX_VERSION to 2, 3 or 4 to run the test suite
 +# with a different indexfile format version.  If it isn't set the index
 +# file format used is index-v[23].
 +#
 +# Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
 +# return NULL when it receives a bogus time_t.
  
  GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -554,7 -557,6 +556,7 @@@ TEST_PROGRAMS_NEED_X += test-dat
  TEST_PROGRAMS_NEED_X += test-delta
  TEST_PROGRAMS_NEED_X += test-dump-cache-tree
  TEST_PROGRAMS_NEED_X += test-genrandom
 +TEST_PROGRAMS_NEED_X += test-hashmap
  TEST_PROGRAMS_NEED_X += test-index-version
  TEST_PROGRAMS_NEED_X += test-line-buffer
  TEST_PROGRAMS_NEED_X += test-match-trees
@@@ -663,8 -665,6 +665,8 @@@ LIB_H += diff.
  LIB_H += diffcore.h
  LIB_H += dir.h
  LIB_H += exec_cmd.h
 +LIB_H += ewah/ewok.h
 +LIB_H += ewah/ewok_rlw.h
  LIB_H += fetch-pack.h
  LIB_H += fmt-merge-msg.h
  LIB_H += fsck.h
@@@ -673,7 -673,7 +675,7 @@@ LIB_H += git-compat-util.
  LIB_H += gpg-interface.h
  LIB_H += graph.h
  LIB_H += grep.h
 -LIB_H += hash.h
 +LIB_H += hashmap.h
  LIB_H += help.h
  LIB_H += http.h
  LIB_H += kwset.h
@@@ -692,10 -692,8 +694,10 @@@ LIB_H += notes-merge.
  LIB_H += notes-utils.h
  LIB_H += notes.h
  LIB_H += object.h
 +LIB_H += pack-objects.h
  LIB_H += pack-revindex.h
  LIB_H += pack.h
 +LIB_H += pack-bitmap.h
  LIB_H += parse-options.h
  LIB_H += patch-ids.h
  LIB_H += pathspec.h
@@@ -799,10 -797,6 +801,10 @@@ LIB_OBJS += dir.
  LIB_OBJS += editor.o
  LIB_OBJS += entry.o
  LIB_OBJS += environment.o
 +LIB_OBJS += ewah/bitmap.o
 +LIB_OBJS += ewah/ewah_bitmap.o
 +LIB_OBJS += ewah/ewah_io.o
 +LIB_OBJS += ewah/ewah_rlw.o
  LIB_OBJS += exec_cmd.o
  LIB_OBJS += fetch-pack.o
  LIB_OBJS += fsck.o
@@@ -810,7 -804,7 +812,7 @@@ LIB_OBJS += gettext.
  LIB_OBJS += gpg-interface.o
  LIB_OBJS += graph.o
  LIB_OBJS += grep.o
 -LIB_OBJS += hash.o
 +LIB_OBJS += hashmap.o
  LIB_OBJS += help.o
  LIB_OBJS += hex.o
  LIB_OBJS += ident.o
@@@ -834,10 -828,7 +836,10 @@@ LIB_OBJS += notes-cache.
  LIB_OBJS += notes-merge.o
  LIB_OBJS += notes-utils.o
  LIB_OBJS += object.o
 +LIB_OBJS += pack-bitmap.o
 +LIB_OBJS += pack-bitmap-write.o
  LIB_OBJS += pack-check.o
 +LIB_OBJS += pack-objects.o
  LIB_OBJS += pack-revindex.o
  LIB_OBJS += pack-write.o
  LIB_OBJS += pager.o
@@@ -895,7 -886,6 +897,7 @@@ LIB_OBJS += userdiff.
  LIB_OBJS += utf8.o
  LIB_OBJS += varint.o
  LIB_OBJS += version.o
 +LIB_OBJS += versioncmp.o
  LIB_OBJS += walker.o
  LIB_OBJS += wildmatch.o
  LIB_OBJS += wrapper.o
@@@ -1111,6 -1101,10 +1113,10 @@@ ifdef USE_LIBPCR
        EXTLIBS += -lpcre
  endif
  
+ ifdef HAVE_ALLOCA_H
+       BASIC_CFLAGS += -DHAVE_ALLOCA_H
+ endif
  ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
        REMOTE_CURL_PRIMARY =
@@@ -1283,6 -1277,20 +1289,6 @@@ endi
  ifdef NO_STRTOULL
        COMPAT_CFLAGS += -DNO_STRTOULL
  endif
 -ifdef NO_FNMATCH
 -      COMPAT_CFLAGS += -Icompat/fnmatch
 -      COMPAT_CFLAGS += -DNO_FNMATCH
 -      COMPAT_OBJS += compat/fnmatch/fnmatch.o
 -else
 -ifdef NO_FNMATCH_CASEFOLD
 -      COMPAT_CFLAGS += -Icompat/fnmatch
 -      COMPAT_CFLAGS += -DNO_FNMATCH_CASEFOLD
 -      COMPAT_OBJS += compat/fnmatch/fnmatch.o
 -endif
 -endif
 -ifndef NO_WILDMATCH
 -      COMPAT_CFLAGS += -DUSE_WILDMATCH
 -endif
  ifdef NO_SETENV
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
@@@ -1492,11 -1500,6 +1498,11 @@@ ifneq (,$(XDL_FAST_HASH)
        BASIC_CFLAGS += -DXDL_FAST_HASH
  endif
  
 +ifdef GMTIME_UNRELIABLE_ERRORS
 +      COMPAT_OBJS += compat/gmtime.o
 +      BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
 +endif
 +
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK = NoThanks
  endif
@@@ -2102,7 -2105,7 +2108,7 @@@ pdf
  
  XGETTEXT_FLAGS = \
        --force-po \
 -      --add-comments \
 +      --add-comments=TRANSLATORS: \
        --msgid-bugs-address="Git Mailing List <git@vger.kernel.org>" \
        --from-code=UTF-8
  XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
@@@ -2225,9 -2228,6 +2231,9 @@@ endi
  ifdef GIT_PERF_MAKE_OPTS
        @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@
  endif
 +ifdef TEST_GIT_INDEX_VERSION
 +      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@
 +endif
  
  ### Detect Python interpreter path changes
  ifndef NO_PYTHON
@@@ -2485,9 -2485,8 +2491,9 @@@ profile-clean
        $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
  
  clean: profile-clean coverage-clean
 -      $(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
 -              builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
 +      $(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o
 +      $(RM) xdiff/*.o vcs-svn/*.o ewah/*.o builtin/*.o
 +      $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
diff --combined cache.h
index 107ac61b68f15b1e15532c09fda9e9799f830e44,e7f5a0c7c8038336392d33a09df3933c3d72dac9..ef4412d1c68e1f4dc4821eaf24178361b312e27a
+++ b/cache.h
@@@ -3,7 -3,7 +3,7 @@@
  
  #include "git-compat-util.h"
  #include "strbuf.h"
 -#include "hash.h"
 +#include "hashmap.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
@@@ -74,6 -74,21 +74,21 @@@ unsigned long git_deflate_bound(git_zst
  #define S_IFGITLINK   0160000
  #define S_ISGITLINK(m)        (((m) & S_IFMT) == S_IFGITLINK)
  
+ /*
+  * Some mode bits are also used internally for computations.
+  *
+  * They *must* not overlap with any valid modes, and they *must* not be emitted
+  * to outside world - i.e. appear on disk or network. In other words, it's just
+  * temporary fields, which we internally use, but they have to stay in-house.
+  *
+  * ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
+  *   codebase mode is `unsigned int` which is assumed to be at least 32 bits )
+  */
+ /* used internally in tree-diff */
+ #define S_DIFFTREE_IFXMIN_NEQ 0x80000000
  /*
   * Intensive research over the course of many years has shown that
   * port 9418 is totally unused by anything else. Or
@@@ -130,12 -145,12 +145,12 @@@ struct stat_data 
  };
  
  struct cache_entry {
 +      struct hashmap_entry ent;
        struct stat_data ce_stat_data;
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned char sha1[20];
 -      struct cache_entry *next;
        char name[FLEX_ARRAY]; /* more */
  };
  
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 -#define CE_UNHASHED          (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -194,18 -210,17 +209,18 @@@ struct pathspec
   * Copy the sha1 and stat state of a cache entry from one to
   * another. But we never change the name, or the hash state!
   */
 -#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
  static inline void copy_cache_entry(struct cache_entry *dst,
                                    const struct cache_entry *src)
  {
 -      unsigned int state = dst->ce_flags & CE_STATE_MASK;
 +      unsigned int state = dst->ce_flags & CE_HASHED;
  
        /* Don't copy hash chain and name */
 -      memcpy(dst, src, offsetof(struct cache_entry, next));
 +      memcpy(&dst->ce_stat_data, &src->ce_stat_data,
 +                      offsetof(struct cache_entry, name) -
 +                      offsetof(struct cache_entry, ce_stat_data));
  
        /* Restore the hash state */
 -      dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 +      dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
  }
  
  static inline unsigned create_ce_flags(unsigned stage)
@@@ -277,8 -292,8 +292,8 @@@ struct index_state 
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
 -      struct hash_table name_hash;
 -      struct hash_table dir_hash;
 +      struct hashmap name_hash;
 +      struct hashmap dir_hash;
  };
  
  extern struct index_state the_index;
@@@ -316,6 -331,7 +331,6 @@@ extern void free_name_hash(struct index
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
  #define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
 -#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
  #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
  #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
  #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
@@@ -433,7 -449,6 +448,7 @@@ extern int set_git_dir_init(const char 
  extern int init_db(const char *template_dir, unsigned int flags);
  
  extern void sanitize_stdfds(void);
 +extern int daemonize(void);
  
  #define alloc_nr(x) (((x)+16)*3/2)
  
@@@ -467,6 -482,7 +482,6 @@@ extern int unmerged_index(const struct 
  extern int verify_path(const char *path);
  extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 -extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
@@@ -483,11 -499,11 +498,11 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
 -#define ADD_CACHE_IMPLICIT_DOT 32     /* internal to "git add -u/-A" */
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
 -extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
 +extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
 +extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
  extern int index_name_is_other(const struct index_state *, const char *, int);
  extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
  
  #define CE_MATCH_RACY_IS_DIRTY                02
  /* do stat comparison even if CE_SKIP_WORKTREE is true */
  #define CE_MATCH_IGNORE_SKIP_WORKTREE 04
 +/* ignore non-existent files during stat update  */
 +#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);
  
 -extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 -
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
@@@ -581,17 -595,7 +596,17 @@@ extern size_t packed_git_limit
  extern size_t delta_base_cache_limit;
  extern unsigned long big_file_threshold;
  extern unsigned long pack_size_limit_cfg;
 -extern int read_replace_refs;
 +
 +/*
 + * Do replace refs need to be checked this run?  This variable is
 + * initialized to true unless --no-replace-object is used or
 + * $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some
 + * commands that do not want replace references to be active.  As an
 + * optimization it is also set to false if replace references have
 + * been sought but there were none.
 + */
 +extern int check_replace_refs;
 +
  extern int fsync_object_files;
  extern int core_preload_index;
  extern int core_apply_sparse_checkout;
@@@ -670,28 -674,9 +685,28 @@@ extern char *git_path(const char *fmt, 
  extern char *git_path_submodule(const char *path, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
  
 -extern char *sha1_file_name(const unsigned char *sha1);
 +/*
 + * Return the name of the file in the local object database that would
 + * be used to store a loose object with the specified sha1.  The
 + * return value is a pointer to a statically allocated buffer that is
 + * overwritten each time the function is called.
 + */
 +extern const char *sha1_file_name(const unsigned char *sha1);
 +
 +/*
 + * Return the name of the (local) packfile with the specified sha1 in
 + * its name.  The return value is a pointer to memory that is
 + * overwritten each time this function is called.
 + */
  extern char *sha1_pack_name(const unsigned char *sha1);
 +
 +/*
 + * Return the name of the (local) pack index file with the specified
 + * sha1 in its name.  The return value is a pointer to memory that is
 + * overwritten each time this function is called.
 + */
  extern char *sha1_pack_index_name(const unsigned char *sha1);
 +
  extern const char *find_unique_abbrev(const unsigned char *sha1, int);
  extern const unsigned char null_sha1[20];
  
@@@ -818,26 -803,13 +833,26 @@@ static inline void *read_sha1_file(cons
  {
        return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT);
  }
 +
 +/*
 + * This internal function is only declared here for the benefit of
 + * lookup_replace_object().  Please do not call it directly.
 + */
  extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
 +
 +/*
 + * If object sha1 should be replaced, return the replacement object's
 + * name (replaced recursively, if necessary).  The return value is
 + * either sha1 or a pointer to a permanently-allocated value.  When
 + * object replacement is suppressed, always return sha1.
 + */
  static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
  {
 -      if (!read_replace_refs)
 +      if (!check_replace_refs)
                return sha1;
        return do_lookup_replace_object(sha1);
  }
 +
  static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag)
  {
        if (!(flag & LOOKUP_REPLACE_OBJECT))
@@@ -851,7 -823,6 +866,7 @@@ extern int hash_sha1_file(const void *b
  extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 +extern int git_open_noatime(const char *name);
  extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
@@@ -864,19 -835,7 +879,19 @@@ extern int check_sha1_signature(const u
  extern int move_temp_to_file(const char *tmpfile, const char *filename);
  
  extern int has_sha1_pack(const unsigned char *sha1);
 +
 +/*
 + * Return true iff we have an object named sha1, whether local or in
 + * an alternate object database, and whether packed or loose.  This
 + * function does not respect replace references.
 + */
  extern int has_sha1_file(const unsigned char *sha1);
 +
 +/*
 + * Return true iff an alternate object database has a loose object
 + * with the specified name.  This function does not respect replace
 + * references.
 + */
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
  extern int has_pack_index(const unsigned char *sha1);
@@@ -1015,7 -974,6 +1030,7 @@@ void datestamp(char *buf, int bufsize)
  unsigned long approxidate_careful(const char *, int *);
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
  enum date_mode parse_date_format(const char *format);
 +int date_overflows(unsigned long date);
  
  #define IDENT_STRICT         1
  #define IDENT_NO_DATE        2
@@@ -1140,46 -1098,17 +1155,46 @@@ extern struct packed_git *find_sha1_pac
                                         struct packed_git *packs);
  
  extern void pack_report(void);
 +
 +/*
 + * mmap the index file for the specified packfile (if it is not
 + * already mmapped).  Return 0 on success.
 + */
  extern int open_pack_index(struct packed_git *);
 +
 +/*
 + * munmap the index file for the specified packfile (if it is
 + * currently mmapped).
 + */
  extern void close_pack_index(struct packed_git *);
 +
  extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
  extern void close_pack_windows(struct packed_git *);
  extern void unuse_pack(struct pack_window **);
  extern void free_pack_by_name(const char *);
  extern void clear_delta_base_cache(void);
  extern struct packed_git *add_packed_git(const char *, int, int);
 -extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t);
 -extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
 -extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
 +
 +/*
 + * Return the SHA-1 of the nth object within the specified packfile.
 + * Open the index if it is not already open.  The return value points
 + * at the SHA-1 within the mmapped index.  Return NULL if there is an
 + * error.
 + */
 +extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
 +
 +/*
 + * Return the offset of the nth object within the specified packfile.
 + * The index must already be opened.
 + */
 +extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
 +
 +/*
 + * If the object named sha1 is present in the specified packfile,
 + * return its offset within the packfile; otherwise, return 0.
 + */
 +extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
 +
  extern int is_pack_valid(struct packed_git *);
  extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
  extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
@@@ -1232,12 -1161,6 +1247,12 @@@ extern int update_server_info(int)
  #define CONFIG_INVALID_PATTERN 6
  #define CONFIG_GENERIC_ERROR 7
  
 +struct git_config_source {
 +      unsigned int use_stdin:1;
 +      const char *file;
 +      const char *blob;
 +};
 +
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
@@@ -1247,7 -1170,8 +1262,7 @@@ extern void git_config_push_parameter(c
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
  extern int git_config_with_options(config_fn_t fn, void *,
 -                                 const char *filename,
 -                                 const char *blob_ref,
 +                                 struct git_config_source *config_source,
                                   int respect_includes);
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
@@@ -1458,6 -1382,4 +1473,6 @@@ int stat_validity_check(struct stat_val
   */
  void stat_validity_update(struct stat_validity *sv, int fd);
  
 +int versioncmp(const char *s1, const char *s2);
 +
  #endif /* CACHE_H */
diff --combined config.mak.uname
index 23a880365616c34ea84d8a3194b98163864a2cd1,9967de66a2c7f4c52c1f218f434ed8aeb67254b8..97acf8fea630ad47d51567782f5e27661e00006b
@@@ -28,6 -28,7 +28,7 @@@ ifeq ($(uname_S),OSF1
        NO_NSEC = YesPlease
  endif
  ifeq ($(uname_S),Linux)
+       HAVE_ALLOCA_H = YesPlease
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
        HAVE_PATHS_H = YesPlease
@@@ -35,6 -36,7 +36,7 @@@
        HAVE_DEV_TTY = YesPlease
  endif
  ifeq ($(uname_S),GNU/kFreeBSD)
+       HAVE_ALLOCA_H = YesPlease
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
        HAVE_PATHS_H = YesPlease
@@@ -103,11 -105,13 +105,12 @@@ ifeq ($(uname_S),SunOS
        NEEDS_NSL = YesPlease
        SHELL_PATH = /bin/bash
        SANE_TOOL_PATH = /usr/xpg6/bin:/usr/xpg4/bin
+       HAVE_ALLOCA_H = YesPlease
        NO_STRCASESTR = YesPlease
        NO_MEMMEM = YesPlease
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        NO_MSGFMT_EXTENDED_OPTIONS = YesPlease
        HAVE_DEV_TTY = YesPlease
        ifeq ($(uname_R),5.6)
        endif
        INSTALL = /usr/ucb/install
        TAR = gtar
-       BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
+       BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__
  endif
  ifeq ($(uname_O),Cygwin)
        ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
        else
                NO_REGEX = UnfortunatelyYes
        endif
+       HAVE_ALLOCA_H = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
@@@ -187,7 -192,6 +191,7 @@@ ifeq ($(uname_S),FreeBSD
        endif
        PYTHON_PATH = /usr/local/bin/python
        HAVE_PATHS_H = YesPlease
 +      GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes
  endif
  ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@@ -239,6 -243,7 +243,7 @@@ ifeq ($(uname_S),AIX
  endif
  ifeq ($(uname_S),GNU)
        # GNU/Hurd
+       HAVE_ALLOCA_H = YesPlease
        NO_STRLCPY = YesPlease
        NO_MKSTEMPS = YesPlease
        HAVE_PATHS_H = YesPlease
@@@ -259,6 -264,7 +264,6 @@@ ifeq ($(uname_S),IRIX
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
        NO_REGEX = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@@ -278,6 -284,7 +283,6 @@@ ifeq ($(uname_S),IRIX64
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
        NO_REGEX = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@@ -294,6 -301,7 +299,6 @@@ ifeq ($(uname_S),HP-UX
        NO_UNSETENV = YesPlease
        NO_HSTRERROR = YesPlease
        NO_SYS_SELECT_H = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        NO_NSEC = YesPlease
        ifeq ($(uname_R),B.11.00)
@@@ -313,6 -321,7 +318,7 @@@ endi
  ifeq ($(uname_S),Windows)
        GIT_VERSION := $(GIT_VERSION).MSVC
        pathsep = ;
+       HAVE_ALLOCA_H = YesPlease
        NO_PREAD = YesPlease
        NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
 -      NO_FNMATCH = YesPlease
        NO_MEMMEM = YesPlease
        # NEEDS_LIBICONV = YesPlease
        NO_ICONV = YesPlease
        NO_MKSTEMPS = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        NO_SVN_TESTS = YesPlease
 -      NO_PERL_MAKEMAKER = YesPlease
        RUNTIME_PREFIX = YesPlease
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        NO_NSEC = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
 -      NO_CURL = YesPlease
        NO_GETTEXT = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
        COMPAT_OBJS = compat/msvc.o compat/winansi.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/dirent.o
-       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
+       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
        BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
 -      EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib
 +      EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
        PTHREAD_LIBS =
        lib =
  ifndef DEBUG
@@@ -383,11 -395,13 +389,11 @@@ ifeq ($(uname_S),Interix
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
                NO_SOCKADDR_STORAGE = YesPlease
 -              NO_FNMATCH_CASEFOLD = YesPlease
        endif
        ifeq ($(uname_R),5.2)
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
                NO_SOCKADDR_STORAGE = YesPlease
 -              NO_FNMATCH_CASEFOLD = YesPlease
        endif
  endif
  ifeq ($(uname_S),Minix)
@@@ -432,6 -446,7 +438,6 @@@ ifeq ($(uname_S),NONSTOP_KERNEL
        NO_D_TYPE_IN_DIRENT = YesPlease
        NO_HSTRERROR = YesPlease
        NO_STRCASESTR = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        NO_MEMMEM = YesPlease
        NO_STRLCPY = YesPlease
        NO_SETENV = YesPlease
  endif
  ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
+       HAVE_ALLOCA_H = YesPlease
        NO_PREAD = YesPlease
        NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
 -      NO_FNMATCH = YesPlease
        NO_MEMMEM = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_STRTOUMAX = YesPlease
@@@ -528,6 -545,7 +535,6 @@@ ifeq ($(uname_S),QNX
        EXPAT_NEEDS_XMLPARSE_H = YesPlease
        HAVE_STRINGS_H = YesPlease
        NEEDS_SOCKET = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        NO_GETPAGESIZE = YesPlease
        NO_ICONV = YesPlease
        NO_MEMMEM = YesPlease
diff --combined configure.ac
index b7112542b4b62fbbf0235c923f75803edc149e5c,0eae70430cc8370b6d242d10fc17295a7e0b75e8..4b1ae7c3c9f5ff5080dc026a6eb835d579d29949
@@@ -272,6 -272,14 +272,14 @@@ AS_HELP_STRING([],           [ARG can b
        GIT_CONF_SUBST([LIBPCREDIR])
      fi)
  #
+ # Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header.
+ AC_FUNC_ALLOCA
+ case $ac_cv_working_alloca_h in
+     yes)    HAVE_ALLOCA_H=YesPlease;;
+     *)      HAVE_ALLOCA_H='';;
+ esac
+ GIT_CONF_SUBST([HAVE_ALLOCA_H])
+ #
  # Define NO_CURL if you do not have curl installed.  git-http-pull and
  # git-http-push are not built, and you cannot use http:// and https://
  # transports.
@@@ -890,7 -898,7 +898,7 @@@ GIT_CONF_SUBST([HAVE_STRINGS_H]
  # and libcharset does
  CHARSET_LIB=
  AC_CHECK_LIB([iconv], [locale_charset],
 -       [],
 +       [CHARSET_LIB=-liconv],
         [AC_CHECK_LIB([charset], [locale_charset],
                       [CHARSET_LIB=-lcharset])])
  GIT_CONF_SUBST([CHARSET_LIB])
@@@ -901,6 -909,34 +909,6 @@@ GIT_CHECK_FUNC(strcasestr
  [NO_STRCASESTR=YesPlease])
  GIT_CONF_SUBST([NO_STRCASESTR])
  #
 -# Define NO_FNMATCH if you don't have fnmatch
 -GIT_CHECK_FUNC(fnmatch,
 -[NO_FNMATCH=],
 -[NO_FNMATCH=YesPlease])
 -GIT_CONF_SUBST([NO_FNMATCH])
 -#
 -# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
 -# FNM_CASEFOLD GNU extension.
 -AC_CACHE_CHECK([whether the fnmatch function supports the FNMATCH_CASEFOLD GNU extension],
 - [ac_cv_c_excellent_fnmatch], [
 -AC_EGREP_CPP(yippeeyeswehaveit,
 -      AC_LANG_PROGRAM([
 -#include <fnmatch.h>
 -],
 -[#ifdef FNM_CASEFOLD
 -yippeeyeswehaveit
 -#endif
 -]),
 -      [ac_cv_c_excellent_fnmatch=yes],
 -      [ac_cv_c_excellent_fnmatch=no])
 -])
 -if test $ac_cv_c_excellent_fnmatch = yes; then
 -      NO_FNMATCH_CASEFOLD=
 -else
 -      NO_FNMATCH_CASEFOLD=YesPlease
 -fi
 -GIT_CONF_SUBST([NO_FNMATCH_CASEFOLD])
 -#
  # Define NO_MEMMEM if you don't have memmem.
  GIT_CHECK_FUNC(memmem,
  [NO_MEMMEM=],
diff --combined diff.c
index f72769a1c492dfcac501032c54c3fb2ac0a4343e,f2fff4667d64024fe383bd9de238280c767e49d1..36679aaf4eab872386a29ee5502fd7282dcbf0a7
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -16,7 -16,6 +16,7 @@@
  #include "submodule.h"
  #include "ll-merge.h"
  #include "string-list.h"
 +#include "argv-array.h"
  
  #ifdef NO_FAST_WORKING_DIRECTORY
  #define FAST_WORKING_DIRECTORY 0
@@@ -1362,7 -1361,11 +1362,7 @@@ static struct diffstat_file *diffstat_a
  {
        struct diffstat_file *x;
        x = xcalloc(sizeof (*x), 1);
 -      if (diffstat->nr == diffstat->alloc) {
 -              diffstat->alloc = alloc_nr(diffstat->alloc);
 -              diffstat->files = xrealloc(diffstat->files,
 -                              diffstat->alloc * sizeof(x));
 -      }
 +      ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc);
        diffstat->files[diffstat->nr++] = x;
        if (name_b) {
                x->from_name = xstrdup(name_a);
@@@ -1462,12 -1465,20 +1462,12 @@@ int print_stat_summary(FILE *fp, int fi
         * but nothing about added/removed lines? Is this a bug in Git?").
         */
        if (insertions || deletions == 0) {
 -              /*
 -               * TRANSLATORS: "+" in (+) is a line addition marker;
 -               * do not translate it.
 -               */
                strbuf_addf(&sb,
                            (insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)",
                            insertions);
        }
  
        if (deletions || insertions == 0) {
 -              /*
 -               * TRANSLATORS: "-" in (-) is a line removal marker;
 -               * do not translate it.
 -               */
                strbuf_addf(&sb,
                            (deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)",
                            deletions);
@@@ -2834,9 -2845,8 +2834,9 @@@ static struct diff_tempfile *prepare_te
                remove_tempfile_installed = 1;
        }
  
 -      if (!one->sha1_valid ||
 -          reuse_worktree_file(name, one->sha1, 1)) {
 +      if (!S_ISGITLINK(one->mode) &&
 +          (!one->sha1_valid ||
 +           reuse_worktree_file(name, one->sha1, 1))) {
                struct stat st;
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
@@@ -2895,8 -2905,9 +2895,8 @@@ static void run_external_diff(const cha
                              int complete_rewrite,
                              struct diff_options *o)
  {
 -      const char *spawn_arg[10];
 +      struct argv_array argv = ARGV_ARRAY_INIT;
        int retval;
 -      const char **arg = &spawn_arg[0];
        struct diff_queue_struct *q = &diff_queued_diff;
        const char *env[3] = { NULL };
        char env_counter[50];
                const char *othername = (other ? other : name);
                temp_one = prepare_temp_file(name, one);
                temp_two = prepare_temp_file(othername, two);
 -              *arg++ = pgm;
 -              *arg++ = name;
 -              *arg++ = temp_one->name;
 -              *arg++ = temp_one->hex;
 -              *arg++ = temp_one->mode;
 -              *arg++ = temp_two->name;
 -              *arg++ = temp_two->hex;
 -              *arg++ = temp_two->mode;
 +              argv_array_push(&argv, pgm);
 +              argv_array_push(&argv, name);
 +              argv_array_push(&argv, temp_one->name);
 +              argv_array_push(&argv, temp_one->hex);
 +              argv_array_push(&argv, temp_one->mode);
 +              argv_array_push(&argv, temp_two->name);
 +              argv_array_push(&argv, temp_two->hex);
 +              argv_array_push(&argv, temp_two->mode);
                if (other) {
 -                      *arg++ = other;
 -                      *arg++ = xfrm_msg;
 +                      argv_array_push(&argv, other);
 +                      argv_array_push(&argv, xfrm_msg);
                }
        } else {
 -              *arg++ = pgm;
 -              *arg++ = name;
 +              argv_array_push(&argv, pgm);
 +              argv_array_push(&argv, name);
        }
 -      *arg = NULL;
        fflush(NULL);
  
        env[0] = env_counter;
        env[1] = env_total;
        snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr);
  
 -      retval = run_command_v_opt_cd_env(spawn_arg, RUN_USING_SHELL, NULL, env);
 +      retval = run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env);
        remove_tempfile();
 +      argv_array_clear(&argv);
        if (retval) {
                fprintf(stderr, "external diff died, stopping at %s.\n", name);
                exit(1);
@@@ -3205,6 -3216,7 +3205,7 @@@ void diff_setup(struct diff_options *op
        options->context = diff_context_default;
        DIFF_OPT_SET(options, RENAME_EMPTY);
  
+       /* pathchange left =NULL by default */
        options->change = diff_change;
        options->add_remove = diff_addremove;
        options->use_color = diff_use_color_default;
@@@ -3354,11 -3366,14 +3355,11 @@@ static int opt_arg(const char *arg, in
        if (c != '-')
                return 0;
        arg++;
 -      eq = strchr(arg, '=');
 -      if (eq)
 -              len = eq - arg;
 -      else
 -              len = strlen(arg);
 +      eq = strchrnul(arg, '=');
 +      len = eq - arg;
        if (!len || strncmp(arg, arg_long, len))
                return 0;
 -      if (eq) {
 +      if (*eq) {
                int n;
                char *end;
                if (!isdigit(*++eq))
@@@ -3585,6 -3600,14 +3586,6 @@@ static int parse_diff_filter_opt(const 
        return 0;
  }
  
 -/* Used only by "diff-files" and "diff --no-index" */
 -void handle_deprecated_show_diff_q(struct diff_options *opt)
 -{
 -      warning("'diff -q' and 'diff-files -q' are deprecated.");
 -      warning("Use 'diff --diff-filter=d' instead to ignore deleted filepairs.");
 -      parse_diff_filter_opt("d", opt);
 -}
 -
  static void enable_patch_output(int *fmt) {
        *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
        *fmt |= DIFF_FORMAT_PATCH;
@@@ -3919,7 -3942,7 +3920,7 @@@ static int diff_scoreopt_parse(const ch
                }
        }
        if (cmd != 'M' && cmd != 'C' && cmd != 'B')
 -              return -1; /* that is not a -M, -C nor -B option */
 +              return -1; /* that is not a -M, -Cor -B option */
  
        opt1 = parse_rename_score(&opt);
        if (cmd != 'B')
@@@ -3943,7 -3966,11 +3944,7 @@@ struct diff_queue_struct diff_queued_di
  
  void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
  {
 -      if (queue->alloc <= queue->nr) {
 -              queue->alloc = alloc_nr(queue->alloc);
 -              queue->queue = xrealloc(queue->queue,
 -                                      sizeof(dp) * queue->alloc);
 -      }
 +      ALLOC_GROW(queue->queue, queue->nr + 1, queue->alloc);
        queue->queue[queue->nr++] = dp;
  }
  
@@@ -4671,38 -4698,6 +4672,38 @@@ static int diff_filespec_is_identical(s
        return !memcmp(one->data, two->data, one->size);
  }
  
 +static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
 +{
 +      if (p->done_skip_stat_unmatch)
 +              return p->skip_stat_unmatch_result;
 +
 +      p->done_skip_stat_unmatch = 1;
 +      p->skip_stat_unmatch_result = 0;
 +      /*
 +       * 1. Entries that come from stat info dirtiness
 +       *    always have both sides (iow, not create/delete),
 +       *    one side of the object name is unknown, with
 +       *    the same mode and size.  Keep the ones that
 +       *    do not match these criteria.  They have real
 +       *    differences.
 +       *
 +       * 2. At this point, the file is known to be modified,
 +       *    with the same mode and size, and the object
 +       *    name of one side is unknown.  Need to inspect
 +       *    the identical contents.
 +       */
 +      if (!DIFF_FILE_VALID(p->one) || /* (1) */
 +          !DIFF_FILE_VALID(p->two) ||
 +          (p->one->sha1_valid && p->two->sha1_valid) ||
 +          (p->one->mode != p->two->mode) ||
 +          diff_populate_filespec(p->one, 1) ||
 +          diff_populate_filespec(p->two, 1) ||
 +          (p->one->size != p->two->size) ||
 +          !diff_filespec_is_identical(p->one, p->two)) /* (2) */
 +              p->skip_stat_unmatch_result = 1;
 +      return p->skip_stat_unmatch_result;
 +}
 +
  static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
  {
        int i;
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
  
 -              /*
 -               * 1. Entries that come from stat info dirtiness
 -               *    always have both sides (iow, not create/delete),
 -               *    one side of the object name is unknown, with
 -               *    the same mode and size.  Keep the ones that
 -               *    do not match these criteria.  They have real
 -               *    differences.
 -               *
 -               * 2. At this point, the file is known to be modified,
 -               *    with the same mode and size, and the object
 -               *    name of one side is unknown.  Need to inspect
 -               *    the identical contents.
 -               */
 -              if (!DIFF_FILE_VALID(p->one) || /* (1) */
 -                  !DIFF_FILE_VALID(p->two) ||
 -                  (p->one->sha1_valid && p->two->sha1_valid) ||
 -                  (p->one->mode != p->two->mode) ||
 -                  diff_populate_filespec(p->one, 1) ||
 -                  diff_populate_filespec(p->two, 1) ||
 -                  (p->one->size != p->two->size) ||
 -                  !diff_filespec_is_identical(p->one, p->two)) /* (2) */
 +              if (diff_filespec_check_stat_unmatch(p))
                        diff_q(&outq, p);
                else {
                        /*
@@@ -4749,6 -4764,7 +4750,7 @@@ void diffcore_fix_diff_index(struct dif
  
  void diffcore_std(struct diff_options *options)
  {
+       /* NOTE please keep the following in sync with diff_tree_combined() */
        if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
        if (!options->found_follow) {
@@@ -4876,7 -4892,6 +4878,7 @@@ void diff_change(struct diff_options *o
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
        struct diff_filespec *one, *two;
 +      struct diff_filepair *p;
  
        if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
            is_submodule_ignored(concatpath, options))
        fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
        one->dirty_submodule = old_dirty_submodule;
        two->dirty_submodule = new_dirty_submodule;
 +      p = diff_queue(&diff_queued_diff, one, two);
  
 -      diff_queue(&diff_queued_diff, one, two);
 -      if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
 -              DIFF_OPT_SET(options, HAS_CHANGES);
 +      if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
 +              return;
 +
 +      if (DIFF_OPT_TST(options, QUICK) && options->skip_stat_unmatch &&
 +          !diff_filespec_check_stat_unmatch(p))
 +              return;
 +
 +      DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
  struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
diff --combined diff.h
index a24a767db78d8e864175cf342f03fc48e159e06e,0abd7350544b386be728aae56ec95fe8049c5dcd..b4a624d235748bf13be44e7479f086880dcf574f
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -15,6 -15,10 +15,10 @@@ struct diff_filespec
  struct userdiff_driver;
  struct sha1_array;
  struct commit;
+ struct combine_diff_path;
+ typedef int (*pathchange_fn_t)(struct diff_options *options,
+                struct combine_diff_path *path);
  
  typedef void (*change_fn_t)(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
@@@ -157,6 -161,7 +161,7 @@@ struct diff_options 
        int close_file;
  
        struct pathspec pathspec;
+       pathchange_fn_t pathchange;
        change_fn_t change;
        add_remove_fn_t add_remove;
        diff_format_fn_t format_callback;
@@@ -189,8 -194,10 +194,10 @@@ const char *diff_line_prefix(struct dif
  
  extern const char mime_boundary_leader[];
  
- extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
-                    const char *base, struct diff_options *opt);
+ extern struct combine_diff_path *diff_tree_paths(
+       struct combine_diff_path *p, const unsigned char *sha1,
+       const unsigned char **parent_sha1, int nparent,
+       struct strbuf *base, struct diff_options *opt);
  extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
                          const char *base, struct diff_options *opt);
  extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
@@@ -345,6 -352,8 +352,6 @@@ extern int parse_rename_score(const cha
  
  extern long parse_algorithm_value(const char *value);
  
 -extern void handle_deprecated_show_diff_q(struct diff_options *);
 -
  extern int print_stat_summary(FILE *fp, int files,
                              int insertions, int deletions);
  extern void setup_diff_pager(struct diff_options *);
diff --combined git-compat-util.h
index f6d3a46622d1de2497b56274a255d7bca46117dd,63b2b3bb47c79f858871bb44f88251926267c5ef..7fe1ffda08a2bdbe956ef00a4bb9d43ba11e7ac3
  #include <sys/time.h>
  #include <time.h>
  #include <signal.h>
 -#ifndef USE_WILDMATCH
 -#include <fnmatch.h>
 -#endif
  #include <assert.h>
  #include <regex.h>
  #include <utime.h>
@@@ -301,7 -304,16 +301,7 @@@ extern char *gitbasename(char *)
  
  #include "compat/bswap.h"
  
 -#ifdef USE_WILDMATCH
  #include "wildmatch.h"
 -#define FNM_PATHNAME WM_PATHNAME
 -#define FNM_CASEFOLD WM_CASEFOLD
 -#define FNM_NOMATCH  WM_NOMATCH
 -static inline int fnmatch(const char *pattern, const char *string, int flags)
 -{
 -      return wildmatch(pattern, string, flags, NULL);
 -}
 -#endif
  
  /* General helper functions */
  extern void vreportf(const char *prefix, const char *err, va_list params);
@@@ -339,15 -351,14 +339,15 @@@ extern void set_error_routine(void (*ro
  extern void set_die_is_recursing_routine(int (*routine)(void));
  
  extern int starts_with(const char *str, const char *prefix);
 -extern int prefixcmp(const char *str, const char *prefix);
  extern int ends_with(const char *str, const char *suffix);
 -extern int suffixcmp(const char *str, const char *suffix);
  
  static inline const char *skip_prefix(const char *str, const char *prefix)
  {
 -      size_t len = strlen(prefix);
 -      return strncmp(str, prefix, len) ? NULL : str + len;
 +      do {
 +              if (!*prefix)
 +                      return str;
 +      } while (*str++ == *prefix++);
 +      return NULL;
  }
  
  #if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
@@@ -469,15 -480,9 +469,15 @@@ extern FILE *git_fopen(const char*, con
  #endif
  
  #ifdef SNPRINTF_RETURNS_BOGUS
 +#ifdef snprintf
 +#undef snprintf
 +#endif
  #define snprintf git_snprintf
  extern int git_snprintf(char *str, size_t maxsize,
                        const char *format, ...);
 +#ifdef vsnprintf
 +#undef vsnprintf
 +#endif
  #define vsnprintf git_vsnprintf
  extern int git_vsnprintf(char *str, size_t maxsize,
                         const char *format, va_list ap);
@@@ -521,6 -526,14 +521,14 @@@ extern void release_pack_memory(size_t)
  typedef void (*try_to_free_t)(size_t);
  extern try_to_free_t set_try_to_free_routine(try_to_free_t);
  
+ #ifdef HAVE_ALLOCA_H
+ # include <alloca.h>
+ # define xalloca(size)      (alloca(size))
+ # define xalloca_free(p)    do {} while (0)
+ #else
+ # define xalloca(size)      (xmalloc(size))
+ # define xalloca_free(p)    (free(p))
+ #endif
  extern char *xstrdup(const char *str);
  extern void *xmalloc(size_t size);
  extern void *xmallocz(size_t size);
@@@ -536,7 -549,7 +544,7 @@@ extern FILE *xfdopen(int fd, const cha
  extern int xmkstemp(char *template);
  extern int xmkstemp_mode(char *template, int mode);
  extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
 -extern int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1);
 +extern int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1);
  
  static inline size_t xsize_t(off_t len)
  {
@@@ -716,11 -729,4 +724,11 @@@ void warn_on_inaccessible(const char *p
  /* Get the passwd entry for the UID of the current process. */
  struct passwd *xgetpwuid_self(void);
  
 +#ifdef GMTIME_UNRELIABLE_ERRORS
 +struct tm *git_gmtime(const time_t *);
 +struct tm *git_gmtime_r(const time_t *, struct tm *);
 +#define gmtime git_gmtime
 +#define gmtime_r git_gmtime_r
 +#endif
 +
  #endif