Merge branch 'jh/memihash-opt'
authorJunio C Hamano <gitster@pobox.com>
Tue, 28 Mar 2017 21:06:00 +0000 (14:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 28 Mar 2017 21:06:00 +0000 (14:06 -0700)
The name-hash used for detecting paths that are different only in
cases (which matter on case insensitive filesystems) has been
optimized to take advantage of multi-threading when it makes sense.

* jh/memihash-opt:
name-hash: add test-lazy-init-name-hash to .gitignore
name-hash: add perf test for lazy_init_name_hash
name-hash: add test-lazy-init-name-hash
name-hash: perf improvement for lazy_init_name_hash
hashmap: document memihash_cont, hashmap_disallow_rehash api
hashmap: add disallow_rehash setting
hashmap: allow memihash computation to be continued
name-hash: specify initial size for istate.dir_hash table

1  2 
Documentation/technical/api-hashmap.txt
Makefile
cache.h
index a3f020cd9ef6a5110a1ca439403e525377975b72,4e4ae6f249c3eea5a1e7913b7a08312298cfeabf..ccc634bbd754f6e0bab80fb3ccf1cc42afc5b829
@@@ -21,6 -21,9 +21,9 @@@ that the hashmap is initialized. It ma
  `cmpfn` stores the comparison function specified in `hashmap_init()`. In
  advanced scenarios, it may be useful to change this, e.g. to switch between
  case-sensitive and case-insensitive lookup.
+ +
+ When `disallow_rehash` is set, automatic rehashes are prevented during inserts
+ and deletes.
  
  `struct hashmap_entry`::
  
@@@ -57,6 -60,7 +60,7 @@@ Function
  `unsigned int strihash(const char *buf)`::
  `unsigned int memhash(const void *buf, size_t len)`::
  `unsigned int memihash(const void *buf, size_t len)`::
+ `unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len)`::
  
        Ready-to-use hash functions for strings, using the FNV-1 algorithm (see
        http://www.isthe.com/chongo/tech/comp/fnv).
@@@ -65,6 -69,9 +69,9 @@@
  `memihash` operate on arbitrary-length memory.
  +
  `strihash` and `memihash` are case insensitive versions.
+ +
+ `memihash_cont` is a variant of `memihash` that allows a computation to be
+ continued with another chunk of data.
  
  `unsigned int sha1hash(const unsigned char *sha1)`::
  
@@@ -184,13 -191,26 +191,28 @@@ passed to `hashmap_cmp_fn` to decide wh
  +
  Returns the removed entry, or NULL if not found.
  
+ `void hashmap_disallow_rehash(struct hashmap *map, unsigned value)`::
+       Disallow/allow automatic rehashing of the hashmap during inserts
+       and deletes.
+ +
+ This is useful if the caller knows that the hashmap will be accessed
+ by multiple threads.
+ +
+ The caller is still responsible for any necessary locking; this simply
+ prevents unexpected rehashing.  The caller is also responsible for properly
+ sizing the initial hashmap to ensure good performance.
+ +
+ A call to allow rehashing does not force a rehash; that might happen
+ with the next insert or delete.
  `void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)`::
  `void *hashmap_iter_next(struct hashmap_iter *iter)`::
  `void *hashmap_iter_first(struct hashmap *map, struct hashmap_iter *iter)`::
  
 -      Used to iterate over all entries of a hashmap.
 +      Used to iterate over all entries of a hashmap. Note that it is
 +      not safe to add or remove entries to the hashmap while
 +      iterating.
  +
  `hashmap_iter_init` initializes a `hashmap_iter` structure.
  +
diff --combined Makefile
index c80fec2920028f2ef971e7ebd6fe89d61de13d3c,061d9ea88489fc8575e116d61704b0473c58269c..9f8b35ad41fc37f3b0e3dcd2e294c039f698cbe4
+++ b/Makefile
@@@ -102,6 -102,8 +102,6 @@@ all:
  #
  # Define MKDIR_WO_TRAILING_SLASH if your mkdir() can't deal with trailing slash.
  #
 -# Define NO_MKSTEMPS if you don't have mkstemps in the C library.
 -#
  # Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
  # in the C library.
  #
  # Define PPC_SHA1 environment variable when running make to make use of
  # a bundled SHA1 routine optimized for PowerPC.
  #
 +# Define DC_SHA1 to unconditionally enable the collision-detecting sha1
 +# algorithm. This is slower, but may detect attempted collision attacks.
 +# Takes priority over other *_SHA1 knobs.
 +#
 +# Define OPENSSL_SHA1 environment variable when running make to link
 +# with the SHA1 routine from openssl library.
 +#
  # Define SHA1_MAX_BLOCK_SIZE to limit the amount of data that will be hashed
  # in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO
  # wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined.
  # apostrophes to be ASCII so that cut&pasting examples to the shell
  # will work.
  #
 +# Define USE_ASCIIDOCTOR to use Asciidoctor instead of AsciiDoc to build the
 +# documentation.
 +#
 +# Define ASCIIDOCTOR_EXTENSIONS_LAB to point to the location of the Asciidoctor
 +# Extensions Lab if you have it available.
 +#
  # 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
  # is a simplified version of the merge sort used in glibc. This is
  # recommended if Git triggers O(n^2) behavior in your platform's qsort().
  #
 +# Define HAVE_ISO_QSORT_S if your platform provides a qsort_s() that's
 +# compatible with the one described in C11 Annex K.
 +#
  # Define UNRELIABLE_FSTAT if your system's fstat does not return the same
  # information on a not yet closed file that lstat would return for the same
  # file after it was closed.
  #
  # Define NATIVE_CRLF if your platform uses CRLF for line endings.
  #
 -# Define XDL_FAST_HASH to use an alternative line-hashing method in
 -# the diff algorithm.  It gives a nice speedup if your processor has
 -# fast unaligned word loads.  Does NOT work on big-endian systems!
 -# Enabled by default on x86_64.
 -#
  # Define GIT_USER_AGENT if you want to change how git identifies itself during
  # network interactions.  The default is "git/$(GIT_VERSION)".
  #
@@@ -536,10 -527,12 +536,10 @@@ SCRIPT_LIB += git-sh-setu
  SCRIPT_LIB += git-sh-i18n
  
  SCRIPT_PERL += git-add--interactive.perl
 -SCRIPT_PERL += git-difftool.perl
  SCRIPT_PERL += git-archimport.perl
  SCRIPT_PERL += git-cvsexportcommit.perl
  SCRIPT_PERL += git-cvsimport.perl
  SCRIPT_PERL += git-cvsserver.perl
 -SCRIPT_PERL += git-relink.perl
  SCRIPT_PERL += git-send-email.perl
  SCRIPT_PERL += git-svn.perl
  
@@@ -621,6 -614,7 +621,7 @@@ TEST_PROGRAMS_NEED_X += test-fake-ss
  TEST_PROGRAMS_NEED_X += test-genrandom
  TEST_PROGRAMS_NEED_X += test-hashmap
  TEST_PROGRAMS_NEED_X += test-index-version
+ TEST_PROGRAMS_NEED_X += test-lazy-init-name-hash
  TEST_PROGRAMS_NEED_X += test-line-buffer
  TEST_PROGRAMS_NEED_X += test-match-trees
  TEST_PROGRAMS_NEED_X += test-mergesort
@@@ -786,7 -780,6 +787,7 @@@ LIB_OBJS += notes-cache.
  LIB_OBJS += notes-merge.o
  LIB_OBJS += notes-utils.o
  LIB_OBJS += object.o
 +LIB_OBJS += oidset.o
  LIB_OBJS += pack-bitmap.o
  LIB_OBJS += pack-bitmap-write.o
  LIB_OBJS += pack-check.o
@@@ -896,7 -889,6 +897,7 @@@ BUILTIN_OBJS += builtin/diff-files.
  BUILTIN_OBJS += builtin/diff-index.o
  BUILTIN_OBJS += builtin/diff-tree.o
  BUILTIN_OBJS += builtin/diff.o
 +BUILTIN_OBJS += builtin/difftool.o
  BUILTIN_OBJS += builtin/fast-export.o
  BUILTIN_OBJS += builtin/fetch-pack.o
  BUILTIN_OBJS += builtin/fetch.o
@@@ -938,7 -930,6 +939,7 @@@ BUILTIN_OBJS += builtin/prune.
  BUILTIN_OBJS += builtin/pull.o
  BUILTIN_OBJS += builtin/push.o
  BUILTIN_OBJS += builtin/read-tree.o
 +BUILTIN_OBJS += builtin/rebase--helper.o
  BUILTIN_OBJS += builtin/receive-pack.o
  BUILTIN_OBJS += builtin/reflog.o
  BUILTIN_OBJS += builtin/remote.o
@@@ -1287,6 -1278,9 +1288,6 @@@ ifdef MKDIR_WO_TRAILING_SLAS
        COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH
        COMPAT_OBJS += compat/mkdir.o
  endif
 -ifdef NO_MKSTEMPS
 -      COMPAT_CFLAGS += -DNO_MKSTEMPS
 -endif
  ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
        COMPAT_OBJS += compat/unsetenv.o
@@@ -1390,27 -1384,20 +1391,27 @@@ ifdef APPLE_COMMON_CRYPT
        SHA1_MAX_BLOCK_SIZE = 1024L*1024L*1024L
  endif
  
 +ifdef OPENSSL_SHA1
 +      EXTLIBS += $(LIB_4_CRYPTO)
 +      BASIC_CFLAGS += -DSHA1_OPENSSL
 +else
  ifdef BLK_SHA1
 -      SHA1_HEADER = "block-sha1/sha1.h"
        LIB_OBJS += block-sha1/sha1.o
 +      BASIC_CFLAGS += -DSHA1_BLK
  else
  ifdef PPC_SHA1
 -      SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
 +      BASIC_CFLAGS += -DSHA1_PPC
  else
  ifdef APPLE_COMMON_CRYPTO
        COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
 -      SHA1_HEADER = <CommonCrypto/CommonDigest.h>
 +      BASIC_CFLAGS += -DSHA1_APPLE
  else
 -      SHA1_HEADER = <openssl/sha.h>
 -      EXTLIBS += $(LIB_4_CRYPTO)
 +      DC_SHA1 := YesPlease
 +      LIB_OBJS += sha1dc/sha1.o
 +      LIB_OBJS += sha1dc/ubc_check.o
 +      BASIC_CFLAGS += -DSHA1_DC
 +endif
  endif
  endif
  endif
@@@ -1437,11 -1424,6 +1438,11 @@@ ifdef INTERNAL_QSOR
        COMPAT_CFLAGS += -DINTERNAL_QSORT
        COMPAT_OBJS += compat/qsort.o
  endif
 +ifdef HAVE_ISO_QSORT_S
 +      COMPAT_CFLAGS += -DHAVE_ISO_QSORT_S
 +else
 +      COMPAT_OBJS += compat/qsort_s.o
 +endif
  ifdef RUNTIME_PREFIX
        COMPAT_CFLAGS += -DRUNTIME_PREFIX
  endif
@@@ -1504,6 -1486,10 +1505,6 @@@ ifndef NO_MSGFMT_EXTENDED_OPTION
        MSGFMT += --check --statistics
  endif
  
 -ifneq (,$(XDL_FAST_HASH))
 -      BASIC_CFLAGS += -DXDL_FAST_HASH
 -endif
 -
  ifdef GMTIME_UNRELIABLE_ERRORS
        COMPAT_OBJS += compat/gmtime.o
        BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
@@@ -1602,6 -1588,7 +1603,6 @@@ endi
  
  # Shell quote (do not use $(call) to accommodate ancient setups);
  
 -SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
  ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
  ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
  
@@@ -1634,7 -1621,8 +1635,7 @@@ PERLLIB_EXTRA_SQ = $(subst ','\'',$(PER
  # from the dependency list, that would make each entry appear twice.
  LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS)
  
 -BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
 -      $(COMPAT_CFLAGS)
 +BASIC_CFLAGS += $(COMPAT_CFLAGS)
  LIB_OBJS += $(COMPAT_OBJS)
  
  # Quote for C
@@@ -1838,7 -1826,7 +1839,7 @@@ $(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))))) \
 -        -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
 +        -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@
  
  # This makes sure we depend on the NO_PERL setting itself.
  $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
@@@ -2068,7 -2056,7 +2069,7 @@@ git-%$X: %.o GIT-LDFLAGS $(GITLIBS
  
  git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 -              $(LIBS) $(IMAP_SEND_LDFLAGS)
 +              $(IMAP_SEND_LDFLAGS) $(LIBS)
  
  git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
@@@ -2127,8 -2115,7 +2128,8 @@@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --
        --keyword=_ --keyword=N_ --keyword="Q_:1,2"
  XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
        --keyword=gettextln --keyword=eval_gettextln
 -XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
 +XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
 +      --keyword=__ --keyword=N__ --keyword="__n:1,2"
  LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
  LOCALIZED_SH = $(SCRIPT_SH)
  LOCALIZED_SH += git-parse-remote.sh
@@@ -2237,7 -2224,6 +2238,7 @@@ GIT-BUILD-OPTIONS: FORC
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
        @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
 +      @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+
  ifdef TEST_OUTPUT_DIRECTORY
        @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
  endif
@@@ -2264,9 -2250,6 +2265,9 @@@ endi
  ifdef GIT_PERF_MAKE_OPTS
        @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
  endif
 +ifdef GIT_INTEROP_MAKE_OPTS
 +      @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
 +endif
  ifdef TEST_GIT_INDEX_VERSION
        @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
  endif
diff --combined cache.h
index db4120c233535e363cb9c368056ee83bf0b113db,6556fbb6fbc710be1a76f7809de386954935856d..fbdf7a815a1f6b56df82877d0a5b2febe5a7d095
+++ b/cache.h
@@@ -10,8 -10,8 +10,8 @@@
  #include "trace.h"
  #include "string-list.h"
  #include "pack-revindex.h"
 +#include "hash.h"
  
 -#include SHA1_HEADER
  #ifndef platform_SHA_CTX
  /*
   * platform's underlying implementation of SHA-1; could be OpenSSL,
@@@ -343,6 -343,7 +343,7 @@@ struct index_state 
  extern struct index_state the_index;
  
  /* Name hashing */
+ extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
  extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
  extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
  extern void free_name_hash(struct index_state *istate);
@@@ -507,41 -508,20 +508,41 @@@ extern int is_nonbare_repository_dir(st
  #define READ_GITFILE_ERR_NO_PATH 6
  #define READ_GITFILE_ERR_NOT_A_REPO 7
  #define READ_GITFILE_ERR_TOO_LARGE 8
 +extern void read_gitfile_error_die(int error_code, const char *path, const char *dir);
  extern const char *read_gitfile_gently(const char *path, int *return_error_code);
  #define read_gitfile(path) read_gitfile_gently((path), NULL)
 -extern const char *resolve_gitdir(const char *suspect);
 +extern const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 +#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
 +
  extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
 -extern const char **get_pathspec(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
 +/*
 + * Find GIT_DIR of the repository that contains the current working directory,
 + * without changing the working directory or other global state. The result is
 + * appended to gitdir. The return value is either NULL if no repository was
 + * found, or pointing to the path inside gitdir's buffer.
 + */
 +extern const char *discover_git_directory(struct strbuf *gitdir);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
  extern char *prefix_path(const char *prefix, int len, const char *path);
  extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
 -extern const char *prefix_filename(const char *prefix, int len, const char *path);
 +
 +/*
 + * Concatenate "prefix" (if len is non-zero) and "path", with no
 + * connecting characters (so "prefix" should end with a "/").
 + * Unlike prefix_path, this should be used if the named file does
 + * not have to interact with index entry; i.e. name of a random file
 + * on the filesystem.
 + *
 + * The return value is always a newly allocated string (even if the
 + * prefix was empty).
 + */
 +extern char *prefix_filename(const char *prefix, const char *path);
 +
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix,
                            const char *name,
@@@ -652,7 -632,7 +653,7 @@@ extern int chmod_index_entry(struct ind
  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 *);
 +extern void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
  
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
@@@ -714,6 -694,7 +715,6 @@@ extern int minimum_abbrev, default_abbr
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
 -extern int log_all_ref_updates;
  extern int warn_ambiguous_refs;
  extern int warn_on_object_refname_ambiguity;
  extern const char *apply_default_whitespace;
@@@ -781,14 -762,6 +782,14 @@@ enum hide_dotfiles_type 
  };
  extern enum hide_dotfiles_type hide_dotfiles;
  
 +enum log_refs_config {
 +      LOG_REFS_UNSET = -1,
 +      LOG_REFS_NONE = 0,
 +      LOG_REFS_NORMAL,
 +      LOG_REFS_ALWAYS
 +};
 +extern enum log_refs_config log_all_ref_updates;
 +
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -1064,6 -1037,9 +1065,6 @@@ static inline int is_empty_tree_oid(con
        return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
  }
  
 -
 -int git_mkstemp(char *path, size_t n, const char *template);
 -
  /* set default permissions by passing mode arguments to open(2) */
  int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
  int git_mkstemp_mode(char *pattern, int mode);
@@@ -1088,9 -1064,8 +1089,9 @@@ int adjust_shared_perm(const char *path
  
  /*
   * Create the directory containing the named path, using care to be
 - * somewhat safe against races.  Return one of the scld_error values
 - * to indicate success/failure.
 + * somewhat safe against races. Return one of the scld_error values to
 + * indicate success/failure. On error, set errno to describe the
 + * problem.
   *
   * SCLD_VANISHED indicates that one of the ancestor directories of the
   * path existed at one point during the function call and then
@@@ -1114,49 -1089,6 +1115,49 @@@ enum scld_error 
  enum scld_error safe_create_leading_directories(char *path);
  enum scld_error safe_create_leading_directories_const(const char *path);
  
 +/*
 + * Callback function for raceproof_create_file(). This function is
 + * expected to do something that makes dirname(path) permanent despite
 + * the fact that other processes might be cleaning up empty
 + * directories at the same time. Usually it will create a file named
 + * path, but alternatively it could create another file in that
 + * directory, or even chdir() into that directory. The function should
 + * return 0 if the action was completed successfully. On error, it
 + * should return a nonzero result and set errno.
 + * raceproof_create_file() treats two errno values specially:
 + *
 + * - ENOENT -- dirname(path) does not exist. In this case,
 + *             raceproof_create_file() tries creating dirname(path)
 + *             (and any parent directories, if necessary) and calls
 + *             the function again.
 + *
 + * - EISDIR -- the file already exists and is a directory. In this
 + *             case, raceproof_create_file() removes the directory if
 + *             it is empty (and recursively any empty directories that
 + *             it contains) and calls the function again.
 + *
 + * Any other errno causes raceproof_create_file() to fail with the
 + * callback's return value and errno.
 + *
 + * Obviously, this function should be OK with being called again if it
 + * fails with ENOENT or EISDIR. In other scenarios it will not be
 + * called again.
 + */
 +typedef int create_file_fn(const char *path, void *cb);
 +
 +/*
 + * Create a file in dirname(path) by calling fn, creating leading
 + * directories if necessary. Retry a few times in case we are racing
 + * with another process that is trying to clean up the directory that
 + * contains path. See the documentation for create_file_fn for more
 + * details.
 + *
 + * Return the value and set the errno that resulted from the most
 + * recent call of fn. fn is always called at least once, and will be
 + * called more than once if it returns ENOENT or EISDIR.
 + */
 +int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 +
  int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  const char *enter_repo(const char *path, int strict);
@@@ -1165,13 -1097,9 +1166,13 @@@ static inline int is_absolute_path(cons
        return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
 +char *strbuf_realpath(struct strbuf *resolved, const char *path,
 +                    int die_on_error);
  const char *real_path(const char *path);
  const char *real_path_if_valid(const char *path);
 +char *real_pathdup(const char *path, int die_on_error);
  const char *absolute_path(const char *path);
 +char *absolute_pathdup(const char *path);
  const char *remove_leading_path(const char *in, const char *prefix);
  const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
  int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
@@@ -1188,13 -1116,6 +1189,13 @@@ extern int is_ntfs_dotgit(const char *n
   */
  extern char *xdg_config_home(const char *filename);
  
 +/**
 + * Return a newly allocated string with the evaluation of
 + * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
 + * "$HOME/.cache/git/$filename". Return NULL upon error.
 + */
 +extern char *xdg_cache_home(const char *filename);
 +
  /* object replacement */
  #define LOOKUP_REPLACE_OBJECT 1
  #define LOOKUP_UNKNOWN_OBJECT 2
@@@ -1237,8 -1158,7 +1238,8 @@@ extern int write_sha1_file(const void *
  extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
  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(const char *name);
 +extern int git_open_cloexec(const char *name, int flags);
 +#define git_open(name) git_open_cloexec(name, O_RDONLY)
  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);
@@@ -1252,19 -1172,6 +1253,19 @@@ extern int finalize_object_file(const c
  
  extern int has_sha1_pack(const unsigned char *sha1);
  
 +/*
 + * Open the loose object at path, check its sha1, and return the contents,
 + * type, and size. If the object is a blob, then "contents" may return NULL,
 + * to allow streaming of large blobs.
 + *
 + * Returns 0 on success, negative on error (details may be written to stderr).
 + */
 +int read_loose_object(const char *path,
 +                    const unsigned char *expected_sha1,
 +                    enum object_type *type,
 +                    unsigned long *size,
 +                    void **contents);
 +
  /*
   * Return true iff we have an object named sha1, whether local or in
   * an alternate object database, and whether packed or loose.  This
@@@ -1296,9 -1203,6 +1297,9 @@@ extern int has_pack_index(const unsigne
  
  extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
  
 +/* Helper to check and "touch" a file */
 +extern int check_and_freshen_file(const char *fn, int freshen);
 +
  extern const signed char hexval_table[256];
  static inline unsigned int hexval(unsigned char c)
  {
@@@ -1389,46 -1293,7 +1390,46 @@@ extern char *oid_to_hex_r(char *out, co
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
  
 -extern int interpret_branch_name(const char *str, int len, struct strbuf *);
 +/*
 + * Parse a 40-character hexadecimal object ID starting from hex, updating the
 + * pointer specified by end when parsing stops.  The resulting object ID is
 + * stored in oid.  Returns 0 on success.  Parsing will stop on the first NUL or
 + * other invalid character.  end is only updated on success; otherwise, it is
 + * unmodified.
 + */
 +extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
 +
 +/*
 + * This reads short-hand syntax that not only evaluates to a commit
 + * object name, but also can act as if the end user spelled the name
 + * of the branch from the command line.
 + *
 + * - "@{-N}" finds the name of the Nth previous branch we were on, and
 + *   places the name of the branch in the given buf and returns the
 + *   number of characters parsed if successful.
 + *
 + * - "<branch>@{upstream}" finds the name of the other ref that
 + *   <branch> is configured to merge with (missing <branch> defaults
 + *   to the current branch), and places the name of the branch in the
 + *   given buf and returns the number of characters parsed if
 + *   successful.
 + *
 + * If the input is not of the accepted format, it returns a negative
 + * number to signal an error.
 + *
 + * If the input was ok but there are not N branch switches in the
 + * reflog, it returns 0.
 + *
 + * If "allowed" is non-zero, it is a treated as a bitfield of allowable
 + * expansions: local branches ("refs/heads/"), remote branches
 + * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
 + * allowed, even ones to refs outside of those namespaces.
 + */
 +#define INTERPRET_BRANCH_LOCAL (1<<0)
 +#define INTERPRET_BRANCH_REMOTE (1<<1)
 +#define INTERPRET_BRANCH_HEAD (1<<2)
 +extern int interpret_branch_name(const char *str, int len, struct strbuf *,
 +                               unsigned allowed);
  extern int get_oid_mb(const char *str, struct object_id *oid);
  
  extern int validate_headref(const char *ref);
@@@ -1672,27 -1537,6 +1673,27 @@@ extern struct packed_git *find_sha1_pac
  
  extern void pack_report(void);
  
 +/*
 + * Create a temporary file rooted in the object database directory.
 + */
 +extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
 +
 +/*
 + * Generate the filename to be used for a pack file with checksum "sha1" and
 + * extension "ext". The result is written into the strbuf "buf", overwriting
 + * any existing contents. A pointer to buf->buf is returned as a convenience.
 + *
 + * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
 + */
 +extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
 +
 +/*
 + * Create a pack .keep file named "name" (which should generally be the output
 + * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
 + * error.
 + */
 +extern int odb_pack_keep(const char *name);
 +
  /*
   * mmap the index file for the specified packfile (if it is not
   * already mmapped).  Return 0 on success.
@@@ -1729,12 -1573,6 +1730,12 @@@ extern void check_pack_index_ptr(const 
   * error.
   */
  extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
 +/*
 + * Like nth_packed_object_sha1, but write the data into the object specified by
 + * the the first argument.  Returns the first argument on success, and NULL on
 + * error.
 + */
 +extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
  
  /*
   * Return the offset of the nth object within the specified packfile.
@@@ -1776,7 -1614,7 +1777,7 @@@ extern int unpack_object_header(struct 
   * scratch buffer, but restored to its original contents before
   * the function returns.
   */
 -typedef int each_loose_object_fn(const unsigned char *sha1,
 +typedef int each_loose_object_fn(const struct object_id *oid,
                                 const char *path,
                                 void *data);
  typedef int each_loose_cruft_fn(const char *basename,
@@@ -1802,7 -1640,7 +1803,7 @@@ int for_each_loose_file_in_objdir_buf(s
   * LOCAL_ONLY flag is set).
   */
  #define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 -typedef int each_packed_object_fn(const unsigned char *sha1,
 +typedef int each_packed_object_fn(const struct object_id *oid,
                                  struct packed_git *pack,
                                  uint32_t pos,
                                  void *data);
@@@ -1885,11 -1723,8 +1886,11 @@@ extern int git_default_config(const cha
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
  extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
 +extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
 +                                   const unsigned char *sha1, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
 +extern void read_early_config(config_fn_t cb, void *data);
  extern void git_config(config_fn_t fn, void *);
  extern int git_config_with_options(config_fn_t fn, void *,
                                   struct git_config_source *config_source,
@@@ -1956,11 -1791,8 +1957,11 @@@ extern int git_config_include(const cha
   *
   * (i.e., what gets handed to a config_fn_t). The caller provides the section;
   * we return -1 if it does not match, 0 otherwise. The subsection and key
 - * out-parameters are filled by the function (and subsection is NULL if it is
 + * out-parameters are filled by the function (and *subsection is NULL if it is
   * missing).
 + *
 + * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
 + * there is no subsection at all.
   */
  extern int parse_config_key(const char *var,
                            const char *section,
@@@ -2022,11 -1854,6 +2023,11 @@@ extern int git_config_get_bool_or_int(c
  extern int git_config_get_maybe_bool(const char *key, int *dest);
  extern int git_config_get_pathname(const char *key, const char **dest);
  extern int git_config_get_untracked_cache(void);
 +extern int git_config_get_split_index(void);
 +extern int git_config_get_max_percent_split_change(void);
 +
 +/* This dies if the configured or default date is in the future */
 +extern int git_config_get_expiry(const char *key, const char **output);
  
  /*
   * This is a hack for test programs like test-dump-untracked-cache to