Merge branch 'tg/split-index-fixes' into maint
authorJunio C Hamano <gitster@pobox.com>
Thu, 22 Mar 2018 21:24:10 +0000 (14:24 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 22 Mar 2018 21:24:10 +0000 (14:24 -0700)
The split-index mode had a few corner case bugs fixed.

* tg/split-index-fixes:
travis: run tests with GIT_TEST_SPLIT_INDEX
split-index: don't write cache tree with null oid entries
read-cache: fix reading the shared index for other repos

1  2 
cache-tree.c
cache.h
ci/run-build-and-tests.sh
read-cache.c
repository.c
revision.c
t/t1700-split-index.sh
diff --combined cache-tree.c
index e03e72c34a5c1fc618994ee63f38875d28d91886,e006215edc3a593fdbe31a169ed42c097430100b..0dd6292a94e55baff0a42df89c7006a757f61065
@@@ -602,13 -602,13 +602,13 @@@ static struct cache_tree *cache_tree_fi
  
  int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
  {
 -      int entries, was_valid, newfd;
 +      int entries, was_valid;
        struct lock_file lock_file = LOCK_INIT;
        int ret = 0;
  
 -      newfd = hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
 +      hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
  
-       entries = read_index_from(index_state, index_path);
+       entries = read_index_from(index_state, index_path, get_git_dir());
        if (entries < 0) {
                ret = WRITE_TREE_UNREADABLE_INDEX;
                goto out;
                        ret = WRITE_TREE_UNMERGED_INDEX;
                        goto out;
                }
 -              if (0 <= newfd) {
 -                      if (!write_locked_index(index_state, &lock_file, COMMIT_LOCK))
 -                              newfd = -1;
 -              }
 +              write_locked_index(index_state, &lock_file, COMMIT_LOCK);
                /* Not being able to write is fine -- we are only interested
                 * in updating the cache-tree part, and if the next caller
                 * ends up using the old index with unupdated cache-tree part
                hashcpy(sha1, index_state->cache_tree->oid.hash);
  
  out:
 -      if (0 <= newfd)
 -              rollback_lock_file(&lock_file);
 +      rollback_lock_file(&lock_file);
        return ret;
  }
  
diff --combined cache.h
index d8b975a5715e8a5ec7c32e176958725cba61d67a,add5f9f50ac75828221b42392441d086b1df1e9d..fd755c32cf5daf91115f6f534fdd9029609ee546
+++ b/cache.h
@@@ -14,7 -14,6 +14,7 @@@
  #include "hash.h"
  #include "path.h"
  #include "sha1-array.h"
 +#include "repository.h"
  
  #ifndef platform_SHA_CTX
  /*
@@@ -78,8 -77,6 +78,8 @@@ struct object_id 
        unsigned char hash[GIT_MAX_RAWSZ];
  };
  
 +#define the_hash_algo the_repository->hash_algo
 +
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
  #else
@@@ -207,7 -204,6 +207,7 @@@ struct cache_entry 
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 +#define CE_FSMONITOR_VALID   (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -331,7 -327,6 +331,7 @@@ static inline unsigned int canon_mode(u
  #define CACHE_TREE_CHANGED    (1 << 5)
  #define SPLIT_INDEX_ORDERED   (1 << 6)
  #define UNTRACKED_CHANGED     (1 << 7)
 +#define FSMONITOR_CHANGED     (1 << 8)
  
  struct split_index;
  struct untracked_cache;
@@@ -345,13 -340,12 +345,14 @@@ struct index_state 
        struct split_index *split_index;
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
-                initialized : 1;
+                initialized : 1,
+                drop_cache_tree : 1;
        struct hashmap name_hash;
        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;
@@@ -371,7 -365,7 +372,7 @@@ extern void free_name_hash(struct index
  #define active_cache_tree (the_index.cache_tree)
  
  #define read_cache() read_index(&the_index)
- #define read_cache_from(path) read_index_from(&the_index, (path))
+ #define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
  #define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
  #define is_cache_unborn() is_index_unborn(&the_index)
  #define read_cache_unmerged() read_index_unmerged(&the_index)
@@@ -453,16 -447,6 +454,16 @@@ static inline enum object_type object_t
  #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
  #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
  
 +/*
 + * Environment variable used in handshaking the wire protocol.
 + * Contains a colon ':' separated list of keys with optional values
 + * 'key[=value]'.  Presence of unknown keys and values must be
 + * ignored.
 + */
 +#define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
 +/* HTTP header used to handshake the wire protocol */
 +#define GIT_PROTOCOL_HEADER "Git-Protocol"
 +
  /*
   * This environment variable is expected to contain a boolean indicating
   * whether we should or should not treat:
@@@ -616,43 -600,16 +617,44 @@@ extern int read_index(struct index_stat
  extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
  extern int do_read_index(struct index_state *istate, const char *path,
                         int must_exist); /* for testting only! */
- extern int read_index_from(struct index_state *, const char *path);
+ extern int read_index_from(struct index_state *, const char *path,
+                          const char *gitdir);
  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 -683,11 +728,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 -718,12 +766,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 -777,6 +830,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
@@@ -919,7 -867,6 +921,7 @@@ struct repository_format 
        int version;
        int precious_objects;
        int is_bare;
 +      int hash_algo;
        char *work_tree;
        struct string_list unknown_extensions;
  };
@@@ -1052,22 -999,22 +1054,22 @@@ extern const struct object_id empty_blo
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
 +      return !hashcmp(sha1, the_hash_algo->empty_blob->hash);
  }
  
  static inline int is_empty_blob_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
 +      return !oidcmp(oid, the_hash_algo->empty_blob);
  }
  
  static inline int is_empty_tree_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
 +      return !hashcmp(sha1, the_hash_algo->empty_tree->hash);
  }
  
  static inline int is_empty_tree_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
 +      return !oidcmp(oid, the_hash_algo->empty_tree);
  }
  
  /* set default permissions by passing mode arguments to open(2) */
@@@ -1372,13 -1319,6 +1374,13 @@@ extern int set_disambiguate_hint_config
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
 +/*
 + * Read `len` pairs of hexadecimal digits from `hex` and write the
 + * values to `binary` as `len` bytes. Return 0 on success, or -1 if
 + * the input does not consist of hex digits).
 + */
 +extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 +
  /*
   * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
@@@ -1500,7 -1440,6 +1502,7 @@@ extern const char *ident_default_name(v
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
 +extern int is_terminal_dumb(void);
  extern int git_ident_config(const char *, const char *, void *);
  extern void reset_ident_date(void);
  
@@@ -1982,10 -1921,4 +1984,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 */
index d3a094603f030752ccb8bd49dd5d855a3a10c36c,0000000000000000000000000000000000000000..3e23e65f9eed88d1251c9ae32cb9a24e28da8ebe
mode 100755,000000..100755
--- /dev/null
@@@ -1,15 -1,0 +1,19 @@@
 +#!/bin/sh
 +#
 +# Build and test Git
 +#
 +
 +. ${0%/*}/lib-travisci.sh
 +
 +ln -s $HOME/travis-cache/.prove t/.prove
 +
 +make --jobs=2
 +make --quiet test
++if test "$jobname" = "linux-gcc"
++then
++      GIT_TEST_SPLIT_INDEX=YesPlease make --quiet test
++fi
 +
 +check_unignored_build_artifacts
 +
 +save_good_tree
diff --combined read-cache.c
index 2eb81a66b941325dc339d61a0ab804a60cbb8be2,5cd14e2f726489f331425ff762068bb1c184a665..d13ce83794340f36cc09596767d154952c8e42a3
@@@ -19,7 -19,6 +19,7 @@@
  #include "varint.h"
  #include "split-index.h"
  #include "utf8.h"
 +#include "fsmonitor.h"
  
  /* Mask for the name length in ce_flags in the on-disk index */
  
  #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
  #define CACHE_EXT_LINK 0x6c696e6b       /* "link" */
  #define CACHE_EXT_UNTRACKED 0x554E5452          /* "UNTR" */
 +#define CACHE_EXT_FSMONITOR 0x46534D4E          /* "FSMN" */
  
  /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
  #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
                 CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
 -               SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED)
 +               SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED | FSMONITOR_CHANGED)
  
  struct index_state the_index;
  static const char *alternate_index_output;
@@@ -64,7 -62,6 +64,7 @@@ static void replace_index_entry(struct 
        free(old);
        set_index_entry(istate, nr, ce);
        ce->ce_flags |= CE_UPDATE_IN_BASE;
 +      mark_fsmonitor_invalid(istate, ce);
        istate->cache_changed |= CE_ENTRY_CHANGED;
  }
  
@@@ -153,10 -150,8 +153,10 @@@ void fill_stat_cache_info(struct cache_
        if (assume_unchanged)
                ce->ce_flags |= CE_VALID;
  
 -      if (S_ISREG(st->st_mode))
 +      if (S_ISREG(st->st_mode)) {
                ce_mark_uptodate(ce);
 +              mark_fsmonitor_valid(ce);
 +      }
  }
  
  static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
@@@ -196,7 -191,7 +196,7 @@@ static int ce_compare_link(const struc
  
  static int ce_compare_gitlink(const struct cache_entry *ce)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
        /*
         * We don't actually require that the .git directory
         *
         * If so, we consider it always to match.
         */
 -      if (resolve_gitlink_ref(ce->name, "HEAD", sha1) < 0)
 +      if (resolve_gitlink_ref(ce->name, "HEAD", &oid) < 0)
                return 0;
 -      return hashcmp(sha1, ce->oid.hash);
 +      return oidcmp(&oid, &ce->oid);
  }
  
  static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
@@@ -306,7 -301,7 +306,7 @@@ int match_stat_data_racy(const struct i
        return match_stat_data(sd, st);
  }
  
 -int ie_match_stat(const struct index_state *istate,
 +int ie_match_stat(struct index_state *istate,
                  const struct cache_entry *ce, struct stat *st,
                  unsigned int options)
  {
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
        int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
 +      int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
  
 +      if (!ignore_fsmonitor)
 +              refresh_fsmonitor(istate);
        /*
         * If it's marked as always valid in the index, it's
         * valid whatever the checked-out copy says.
                return 0;
        if (!ignore_valid && (ce->ce_flags & CE_VALID))
                return 0;
 +      if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID))
 +              return 0;
  
        /*
         * Intent-to-add entries have not been added, so the index entry
        return changed;
  }
  
 -int ie_modified(const struct index_state *istate,
 +int ie_modified(struct index_state *istate,
                const struct cache_entry *ce,
                struct stat *st, unsigned int options)
  {
@@@ -641,17 -631,13 +641,17 @@@ int add_to_index(struct index_state *is
  {
        int size, namelen, was_same;
        mode_t st_mode = st->st_mode;
 -      struct cache_entry *ce, *alias;
 +      struct cache_entry *ce, *alias = NULL;
        unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
        int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
        int pretend = flags & ADD_CACHE_PRETEND;
        int intent_only = flags & ADD_CACHE_INTENT;
        int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
                          (intent_only ? ADD_CACHE_NEW_ONLY : 0));
 +      int newflags = HASH_WRITE_OBJECT;
 +
 +      if (flags & HASH_RENORMALIZE)
 +              newflags |= HASH_RENORMALIZE;
  
        if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
                return error("%s: can only add regular files, symbolic links or git-directories", path);
        if (ignore_case) {
                adjust_dirname_case(istate, ce->name);
        }
 +      if (!(flags & HASH_RENORMALIZE)) {
 +              alias = index_file_exists(istate, ce->name,
 +                                        ce_namelen(ce), ignore_case);
 +              if (alias &&
 +                  !ce_stage(alias) &&
 +                  !ie_match_stat(istate, alias, st, ce_option)) {
 +                      /* Nothing changed, really */
 +                      if (!S_ISGITLINK(alias->ce_mode))
 +                              ce_mark_uptodate(alias);
 +                      alias->ce_flags |= CE_ADDED;
  
 -      alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case);
 -      if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
 -              /* Nothing changed, really */
 -              if (!S_ISGITLINK(alias->ce_mode))
 -                      ce_mark_uptodate(alias);
 -              alias->ce_flags |= CE_ADDED;
 -
 -              free(ce);
 -              return 0;
 +                      free(ce);
 +                      return 0;
 +              }
        }
        if (!intent_only) {
 -              if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
 +              if (index_path(&ce->oid, path, st, newflags)) {
                        free(ce);
                        return error("unable to index file %s", path);
                }
@@@ -796,7 -778,6 +796,7 @@@ int chmod_index_entry(struct index_stat
        }
        cache_tree_invalidate_path(istate, ce->name);
        ce->ce_flags |= CE_UPDATE_IN_BASE;
 +      mark_fsmonitor_invalid(istate, ce);
        istate->cache_changed |= CE_ENTRY_CHANGED;
  
        return 0;
@@@ -1248,13 -1229,10 +1248,13 @@@ static struct cache_entry *refresh_cach
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
        int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int ignore_missing = options & CE_MATCH_IGNORE_MISSING;
 +      int ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;
  
        if (!refresh || ce_uptodate(ce))
                return ce;
  
 +      if (!ignore_fsmonitor)
 +              refresh_fsmonitor(istate);
        /*
         * CE_VALID or CE_SKIP_WORKTREE means the user promised us
         * that the change to the work tree does not matter and told
                ce_mark_uptodate(ce);
                return ce;
        }
 +      if (!ignore_fsmonitor && (ce->ce_flags & CE_FSMONITOR_VALID)) {
 +              ce_mark_uptodate(ce);
 +              return ce;
 +      }
  
        if (has_symlink_leading_path(ce->name, ce_namelen(ce))) {
                if (ignore_missing)
                         * because CE_UPTODATE flag is in-core only;
                         * we are not going to write this change out.
                         */
 -                      if (!S_ISGITLINK(ce->ce_mode))
 +                      if (!S_ISGITLINK(ce->ce_mode)) {
                                ce_mark_uptodate(ce);
 +                              mark_fsmonitor_valid(ce);
 +                      }
                        return ce;
                }
        }
@@@ -1420,7 -1392,6 +1420,7 @@@ int refresh_index(struct index_state *i
                                 */
                                ce->ce_flags &= ~CE_VALID;
                                ce->ce_flags |= CE_UPDATE_IN_BASE;
 +                              mark_fsmonitor_invalid(istate, ce);
                                istate->cache_changed |= CE_ENTRY_CHANGED;
                        }
                        if (quiet)
@@@ -1540,9 -1511,6 +1540,9 @@@ struct ondisk_cache_entry_extended 
  /* Allow fsck to force verification of the index checksum. */
  int verify_index_checksum;
  
 +/* Allow fsck to force verification of the cache entry order. */
 +int verify_ce_order;
 +
  static int verify_hdr(struct cache_header *hdr, unsigned long size)
  {
        git_SHA_CTX c;
@@@ -1583,9 -1551,6 +1583,9 @@@ static int read_index_extension(struct 
        case CACHE_EXT_UNTRACKED:
                istate->untracked = read_untracked_extension(data, sz);
                break;
 +      case CACHE_EXT_FSMONITOR:
 +              read_fsmonitor_extension(istate, data, sz);
 +              break;
        default:
                if (*ext < 'A' || 'Z' < *ext)
                        return error("index uses %.4s extension, which we do not understand",
@@@ -1603,7 -1568,7 +1603,7 @@@ int hold_locked_index(struct lock_file 
  
  int read_index(struct index_state *istate)
  {
-       return read_index_from(istate, get_index_file());
+       return read_index_from(istate, get_index_file(), get_git_dir());
  }
  
  static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *ondisk,
@@@ -1703,9 -1668,6 +1703,9 @@@ static void check_ce_order(struct index
  {
        unsigned int i;
  
 +      if (!verify_ce_order)
 +              return;
 +
        for (i = 1; i < istate->cache_nr; i++) {
                struct cache_entry *ce = istate->cache[i - 1];
                struct cache_entry *next_ce = istate->cache[i];
@@@ -1761,7 -1723,6 +1761,7 @@@ static void post_read_index_from(struc
        check_ce_order(istate);
        tweak_untracked_cache(istate);
        tweak_split_index(istate);
 +      tweak_fsmonitor(istate);
  }
  
  /* remember to discard_cache() before reading a different cache! */
@@@ -1863,20 -1824,19 +1863,19 @@@ unmap
   * This way, shared index can be removed if they have not been used
   * for some time.
   */
- static void freshen_shared_index(char *base_sha1_hex, int warn)
+ static void freshen_shared_index(const char *shared_index, int warn)
  {
-       char *shared_index = git_pathdup("sharedindex.%s", base_sha1_hex);
        if (!check_and_freshen_file(shared_index, 1) && warn)
                warning("could not freshen shared index '%s'", shared_index);
-       free(shared_index);
  }
  
- int read_index_from(struct index_state *istate, const char *path)
+ int read_index_from(struct index_state *istate, const char *path,
+                   const char *gitdir)
  {
        struct split_index *split_index;
        int ret;
        char *base_sha1_hex;
-       const char *base_path;
+       char *base_path;
  
        /* istate->initialized covers both .git/index and .git/sharedindex.xxx */
        if (istate->initialized)
                split_index->base = xcalloc(1, sizeof(*split_index->base));
  
        base_sha1_hex = sha1_to_hex(split_index->base_sha1);
-       base_path = git_path("sharedindex.%s", base_sha1_hex);
+       base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_sha1_hex);
        ret = do_read_index(split_index->base, base_path, 1);
        if (hashcmp(split_index->base_sha1, split_index->base->sha1))
                die("broken index, expect %s in %s, got %s",
                    base_sha1_hex, base_path,
                    sha1_to_hex(split_index->base->sha1));
  
-       freshen_shared_index(base_sha1_hex, 0);
+       freshen_shared_index(base_path, 0);
        merge_base_index(istate);
        post_read_index_from(istate);
+       free(base_path);
        return ret;
  }
  
@@@ -2215,22 -2176,17 +2215,22 @@@ static int has_racy_timestamp(struct in
        return 0;
  }
  
 -/*
 - * Opportunistically update the index but do not complain if we can't
 - */
  void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
  {
        if ((istate->cache_changed || has_racy_timestamp(istate)) &&
 -          verify_index(istate) &&
 -          write_locked_index(istate, lockfile, COMMIT_LOCK))
 +          verify_index(istate))
 +              write_locked_index(istate, lockfile, COMMIT_LOCK);
 +      else
                rollback_lock_file(lockfile);
  }
  
 +/*
 + * On success, `tempfile` is closed. If it is the temporary file
 + * of a `struct lock_file`, we will therefore effectively perform
 + * a 'close_lock_file_gently()`. Since that is an implementation
 + * detail of lockfiles, callers of `do_write_index()` should not
 + * rely on it.
 + */
  static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                          int strip_extensions)
  {
        struct stat st;
        struct ondisk_cache_entry_extended ondisk;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
-       int drop_cache_tree = 0;
+       int drop_cache_tree = istate->drop_cache_tree;
  
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
                if (err)
                        return -1;
        }
 +      if (!strip_extensions && istate->fsmonitor_last_update) {
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              write_fsmonitor_extension(&sb, istate);
 +              err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
 +                      || ce_write(&c, newfd, sb.buf, sb.len) < 0;
 +              strbuf_release(&sb);
 +              if (err)
 +                      return -1;
 +      }
  
        if (ce_flush(&c, newfd, istate->sha1))
                return -1;
        if (close_tempfile_gently(tempfile)) {
                error(_("could not close '%s'"), tempfile->filename.buf);
 -              delete_tempfile(&tempfile);
                return -1;
        }
        if (stat(tempfile->filename.buf, &st))
@@@ -2396,9 -2343,14 +2396,9 @@@ static int do_write_locked_index(struc
        int ret = do_write_index(istate, lock->tempfile, 0);
        if (ret)
                return ret;
 -      assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
 -             (COMMIT_LOCK | CLOSE_LOCK));
        if (flags & COMMIT_LOCK)
                return commit_locked_index(lock);
 -      else if (flags & CLOSE_LOCK)
 -              return close_lock_file_gently(lock);
 -      else
 -              return ret;
 +      return close_lock_file_gently(lock);
  }
  
  static int write_split_index(struct index_state *istate,
@@@ -2543,15 -2495,11 +2543,15 @@@ int write_locked_index(struct index_sta
        int new_shared_index, ret;
        struct split_index *si = istate->split_index;
  
 +      if (istate->fsmonitor_last_update)
 +              fill_fsmonitor_bitmap(istate);
 +
        if (!si || alternate_index_output ||
            (istate->cache_changed & ~EXTMASK)) {
                if (si)
                        hashclr(si->base_sha1);
 -              return do_write_locked_index(istate, lock, flags);
 +              ret = do_write_locked_index(istate, lock, flags);
 +              goto out;
        }
  
        if (getenv("GIT_TEST_SPLIT_INDEX")) {
        if (new_shared_index) {
                ret = write_shared_index(istate, lock, flags);
                if (ret)
 -                      return ret;
 +                      goto out;
        }
  
        ret = write_split_index(istate, lock, flags);
  
        /* Freshen the shared index only if the split-index was written */
-       if (!ret && !new_shared_index)
-               freshen_shared_index(sha1_to_hex(si->base_sha1), 1);
+       if (!ret && !new_shared_index) {
+               const char *shared_index = git_path("sharedindex.%s",
+                                                   sha1_to_hex(si->base_sha1));
+               freshen_shared_index(shared_index, 1);
+       }
  
 +out:
 +      if (flags & COMMIT_LOCK)
 +              rollback_lock_file(lock);
        return ret;
  }
  
diff --combined repository.c
index f66fcb13425ab53e15ca47407e7c54be5dcdfc8b,161e2d712aa29625bc6f8d616c5827258922d54a..4ffbe9bc94edc18314cb49c945038e2f20a40922
@@@ -5,7 -5,7 +5,7 @@@
  
  /* The main repository */
  static struct repository the_repo = {
 -      NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &the_index, 0, 0
 +      NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &the_index, &hash_algos[GIT_HASH_SHA1], 0, 0
  };
  struct repository *the_repository = &the_repo;
  
@@@ -64,11 -64,6 +64,11 @@@ void repo_set_gitdir(struct repository 
        free(old_gitdir);
  }
  
 +void repo_set_hash_algo(struct repository *repo, int hash_algo)
 +{
 +      repo->hash_algo = &hash_algos[hash_algo];
 +}
 +
  /*
   * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
   * Return 0 upon success and a non-zero value upon failure.
@@@ -141,8 -136,6 +141,8 @@@ int repo_init(struct repository *repo, 
        if (read_and_verify_repository_format(&format, repo->commondir))
                goto error;
  
 +      repo_set_hash_algo(repo, format.hash_algo);
 +
        if (worktree)
                repo_set_worktree(repo, worktree);
  
@@@ -236,5 -229,5 +236,5 @@@ int repo_read_index(struct repository *
        if (!repo->index)
                repo->index = xcalloc(1, sizeof(*repo->index));
  
-       return read_index_from(repo->index, repo->index_file);
+       return read_index_from(repo->index, repo->index_file, repo->gitdir);
  }
diff --combined revision.c
index 1f7454c947a683eb2df28e51504bf67ebe8379cd,d6a89c5af1a05ea169b9b0afab36dbadf3915b00..a2ba54459d42ba032cd3c16aecdef2eeff98562f
@@@ -395,16 -395,8 +395,16 @@@ static struct commit *one_relevant_pare
   * if the whole diff is removal of old data, and otherwise
   * REV_TREE_DIFFERENT (of course if the trees are the same we
   * want REV_TREE_SAME).
 - * That means that once we get to REV_TREE_DIFFERENT, we do not
 - * have to look any further.
 + *
 + * The only time we care about the distinction is when
 + * remove_empty_trees is in effect, in which case we care only about
 + * whether the whole change is REV_TREE_NEW, or if there's another type
 + * of change. Which means we can stop the diff early in either of these
 + * cases:
 + *
 + *   1. We're not using remove_empty_trees at all.
 + *
 + *   2. We saw anything except REV_TREE_NEW.
   */
  static int tree_difference = REV_TREE_SAME;
  
@@@ -415,11 -407,10 +415,11 @@@ static void file_add_remove(struct diff
                    const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
 +      struct rev_info *revs = options->change_fn_data;
  
        tree_difference |= diff;
 -      if (tree_difference == REV_TREE_DIFFERENT)
 -              DIFF_OPT_SET(options, HAS_CHANGES);
 +      if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW)
 +              options->flags.has_changes = 1;
  }
  
  static void file_change(struct diff_options *options,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
        tree_difference = REV_TREE_DIFFERENT;
 -      DIFF_OPT_SET(options, HAS_CHANGES);
 +      options->flags.has_changes = 1;
  }
  
  static int rev_compare_tree(struct rev_info *revs,
        }
  
        tree_difference = REV_TREE_SAME;
 -      DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
 +      revs->pruning.flags.has_changes = 0;
        if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@@ -480,7 -471,7 +480,7 @@@ static int rev_same_tree_as_empty(struc
                return 0;
  
        tree_difference = REV_TREE_SAME;
 -      DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
 +      revs->pruning.flags.has_changes = 0;
        retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
  
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
@@@ -1352,7 -1343,8 +1352,8 @@@ void add_index_objects_to_pending(struc
                        continue; /* current index already taken care of */
  
                if (read_index_from(&istate,
-                                   worktree_git_path(wt, "index")) > 0)
+                                   worktree_git_path(wt, "index"),
+                                   get_worktree_git_dir(wt)) > 0)
                        do_add_index_objects_to_pending(revs, &istate);
                discard_index(&istate);
        }
@@@ -1412,11 -1404,10 +1413,11 @@@ void init_revisions(struct rev_info *re
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
 -      DIFF_OPT_SET(&revs->pruning, RECURSIVE);
 -      DIFF_OPT_SET(&revs->pruning, QUICK);
 +      revs->pruning.flags.recursive = 1;
 +      revs->pruning.flags.quick = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
 +      revs->pruning.change_fn_data = revs;
        revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
        revs->dense = 1;
        revs->prefix = prefix;
@@@ -1832,7 -1823,7 +1833,7 @@@ static int handle_revision_opt(struct r
                revs->simplify_by_decoration = 1;
                revs->limited = 1;
                revs->prune = 1;
 -              load_ref_decorations(DECORATE_SHORT_REFS);
 +              load_ref_decorations(NULL, DECORATE_SHORT_REFS);
        } else if (!strcmp(arg, "--date-order")) {
                revs->sort_order = REV_SORT_BY_COMMIT_DATE;
                revs->topo_order = 1;
                revs->dense = 0;
        } else if (!strcmp(arg, "--show-all")) {
                revs->show_all = 1;
 +      } else if (!strcmp(arg, "--in-commit-order")) {
 +              revs->tree_blobs_in_commit_order = 1;
        } else if (!strcmp(arg, "--remove-empty")) {
                revs->remove_empty_trees = 1;
        } else if (!strcmp(arg, "--merges")) {
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
 -              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 +              revs->diffopt.flags.recursive = 1;
        } else if (!strcmp(arg, "-t")) {
                revs->diff = 1;
 -              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 -              DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
 +              revs->diffopt.flags.recursive = 1;
 +              revs->diffopt.flags.tree_in_recursive = 1;
        } else if (!strcmp(arg, "-m")) {
                revs->ignore_merges = 0;
        } else if (!strcmp(arg, "-c")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.ignore_case = 1;
 -              DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
 +              revs->diffopt.flags.pickaxe_ignore_case = 1;
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
        } else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) {
@@@ -2411,7 -2400,7 +2412,7 @@@ int setup_revisions(int argc, const cha
        /* Pickaxe, diff-filter and rename following need diffs */
        if (revs->diffopt.pickaxe ||
            revs->diffopt.filter ||
 -          DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 +          revs->diffopt.flags.follow_renames)
                revs->diff = 1;
  
        if (revs->topo_order)
        if (revs->prune_data.nr) {
                copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
                /* Can't prune commits with rename following: the paths change.. */
 -              if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 +              if (!revs->diffopt.flags.follow_renames)
                        revs->prune = 1;
                if (!revs->full_diff)
                        copy_pathspec(&revs->diffopt.pathspec,
@@@ -2862,7 -2851,8 +2863,7 @@@ int prepare_revision_walk(struct rev_in
                        }
                }
        }
 -      if (!revs->leak_pending)
 -              object_array_clear(&old_pending);
 +      object_array_clear(&old_pending);
  
        /* Signal whether we need per-parent treesame decoration */
        if (revs->simplify_merges ||
diff --combined t/t1700-split-index.sh
index af9b847761f3c25ddbda8d1e1e978d1a323fa561,322721dd646b408fdb4c63d9a91427769a44608a..c087b6336788054671a307759a9ac67654b624da
@@@ -6,7 -6,6 +6,7 @@@ test_description='split index mode test
  
  # We need total control of index splitting here
  sane_unset GIT_TEST_SPLIT_INDEX
 +sane_unset GIT_FSMONITOR_TEST
  
  test_expect_success 'enable split index' '
        git config splitIndex.maxPercentChange 100 &&
@@@ -401,4 -400,23 +401,23 @@@ done <<\EO
  0642 -rw-r---w-
  EOF
  
+ test_expect_success 'writing split index with null sha1 does not write cache tree' '
+       git config core.splitIndex true &&
+       git config splitIndex.maxPercentChange 0 &&
+       git commit -m "commit" &&
+       {
+               git ls-tree HEAD &&
+               printf "160000 commit $_z40\\tbroken\\n"
+       } >broken-tree &&
+       echo "add broken entry" >msg &&
+       tree=$(git mktree <broken-tree) &&
+       test_tick &&
+       commit=$(git commit-tree $tree -p HEAD <msg) &&
+       git update-ref HEAD "$commit" &&
+       GIT_ALLOW_NULL_SHA1=1 git reset --hard &&
+       (test-dump-cache-tree >cache-tree.out || true) &&
+       test_line_count = 0 cache-tree.out
+ '
  test_done