config.txt: move reset.* to a separate file
[gitweb.git] / read-cache.c
index 4781515252771a15f8eeaadbb4f7c00a4fcc1b79..d57958233e82df6551fdc890f1238ae65beb8940 100644 (file)
@@ -23,6 +23,8 @@
 #include "split-index.h"
 #include "utf8.h"
 #include "fsmonitor.h"
+#include "thread-utils.h"
+#include "progress.h"
 
 /* Mask for the name length in ce_flags in the on-disk index */
 
@@ -44,6 +46,7 @@
 #define CACHE_EXT_UNTRACKED 0x554E5452   /* "UNTR" */
 #define CACHE_EXT_FSMONITOR 0x46534D4E   /* "FSMN" */
 #define CACHE_EXT_ENDOFINDEXENTRIES 0x454F4945 /* "EOIE" */
+#define CACHE_EXT_INDEXENTRYOFFSETTABLE 0x49454F54 /* "IEOT" */
 
 /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
 #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
@@ -206,14 +209,16 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
        }
 }
 
-static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
+static int ce_compare_data(struct index_state *istate,
+                          const struct cache_entry *ce,
+                          struct stat *st)
 {
        int match = -1;
        int fd = git_open_cloexec(ce->name, O_RDONLY);
 
        if (fd >= 0) {
                struct object_id oid;
-               if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
+               if (!index_fd(istate, &oid, fd, st, OBJ_BLOB, ce->name, 0))
                        match = !oideq(&oid, &ce->oid);
                /* index_fd() closed the file descriptor already */
        }
@@ -258,11 +263,13 @@ static int ce_compare_gitlink(const struct cache_entry *ce)
        return !oideq(&oid, &ce->oid);
 }
 
-static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
+static int ce_modified_check_fs(struct index_state *istate,
+                               const struct cache_entry *ce,
+                               struct stat *st)
 {
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
-               if (ce_compare_data(ce, st))
+               if (ce_compare_data(istate, ce, st))
                        return DATA_CHANGED;
                break;
        case S_IFLNK:
@@ -338,7 +345,7 @@ static int is_racy_stat(const struct index_state *istate,
                );
 }
 
-static int is_racy_timestamp(const struct index_state *istate,
+int is_racy_timestamp(const struct index_state *istate,
                             const struct cache_entry *ce)
 {
        return (!S_ISGITLINK(ce->ce_mode) &&
@@ -408,7 +415,7 @@ int ie_match_stat(struct index_state *istate,
                if (assume_racy_is_modified)
                        changed |= DATA_CHANGED;
                else
-                       changed |= ce_modified_check_fs(ce, st);
+                       changed |= ce_modified_check_fs(istate, ce, st);
        }
 
        return changed;
@@ -448,7 +455,7 @@ int ie_modified(struct index_state *istate,
            (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
                return changed;
 
-       changed_fs = ce_modified_check_fs(ce, st);
+       changed_fs = ce_modified_check_fs(istate, ce, st);
        if (changed_fs)
                return changed | changed_fs;
        return 0;
@@ -754,7 +761,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                }
        }
        if (!intent_only) {
-               if (index_path(&ce->oid, path, st, newflags)) {
+               if (index_path(istate, &ce->oid, path, st, newflags)) {
                        discard_cache_entry(ce);
                        return error("unable to index file %s", path);
                }
@@ -824,7 +831,7 @@ struct cache_entry *make_cache_entry(struct index_state *istate,
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
 
-       ret = refresh_cache_entry(&the_index, ce, refresh_options);
+       ret = refresh_cache_entry(istate, ce, refresh_options);
        if (ret != ce)
                discard_cache_entry(ce);
        return ret;
@@ -1477,6 +1484,11 @@ int refresh_index(struct index_state *istate, unsigned int flags,
        const char *typechange_fmt;
        const char *added_fmt;
        const char *unmerged_fmt;
+       struct progress *progress = NULL;
+
+       if (flags & REFRESH_PROGRESS && isatty(2))
+               progress = start_delayed_progress(_("Refresh index"),
+                                                 istate->cache_nr);
 
        trace_performance_enter();
        modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
@@ -1494,7 +1506,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
                        continue;
 
-               if (pathspec && !ce_path_match(&the_index, ce, pathspec, seen))
+               if (pathspec && !ce_path_match(istate, ce, pathspec, seen))
                        filtered = 1;
 
                if (ce_stage(ce)) {
@@ -1517,6 +1529,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
                if (new_entry == ce)
                        continue;
+               if (progress)
+                       display_progress(progress, i);
                if (!new_entry) {
                        const char *fmt;
 
@@ -1548,6 +1562,10 @@ int refresh_index(struct index_state *istate, unsigned int flags,
 
                replace_index_entry(istate, i, new_entry);
        }
+       if (progress) {
+               display_progress(progress, istate->cache_nr);
+               stop_progress(&progress);
+       }
        trace_performance_leave("refresh index");
        return has_errors;
 }
@@ -1695,6 +1713,7 @@ static int read_index_extension(struct index_state *istate,
                read_fsmonitor_extension(istate, data, sz);
                break;
        case CACHE_EXT_ENDOFINDEXENTRIES:
+       case CACHE_EXT_INDEXENTRYOFFSETTABLE:
                /* already handled in do_read_index() */
                break;
        default:
@@ -1717,7 +1736,8 @@ int read_index(struct index_state *istate)
        return read_index_from(istate, get_index_file(), get_git_dir());
 }
 
-static struct cache_entry *create_from_disk(struct index_state *istate,
+static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
+                                           unsigned int version,
                                            struct ondisk_cache_entry *ondisk,
                                            unsigned long *ent_size,
                                            const struct cache_entry *previous_ce)
@@ -1734,7 +1754,7 @@ static struct cache_entry *create_from_disk(struct index_state *istate,
         * number of bytes to be stripped from the end of the previous name,
         * and the bytes to append to the result, to come up with its name.
         */
-       int expand_name_field = istate->version == 4;
+       int expand_name_field = version == 4;
 
        /* On-disk flags are just 16 bits */
        flags = get_be16(&ondisk->flags);
@@ -1758,16 +1778,17 @@ static struct cache_entry *create_from_disk(struct index_state *istate,
                const unsigned char *cp = (const unsigned char *)name;
                size_t strip_len, previous_len;
 
-               previous_len = previous_ce ? previous_ce->ce_namelen : 0;
+               /* If we're at the begining of a block, ignore the previous name */
                strip_len = decode_varint(&cp);
-               if (previous_len < strip_len) {
-                       if (previous_ce)
+               if (previous_ce) {
+                       previous_len = previous_ce->ce_namelen;
+                       if (previous_len < strip_len)
                                die(_("malformed name field in the index, near path '%s'"),
-                                   previous_ce->name);
-                       else
-                               die(_("malformed name field in the index in the first path"));
+                                       previous_ce->name);
+                       copy_len = previous_len - strip_len;
+               } else {
+                       copy_len = 0;
                }
-               copy_len = previous_len - strip_len;
                name = (const char *)cp;
        }
 
@@ -1777,7 +1798,7 @@ static struct cache_entry *create_from_disk(struct index_state *istate,
                        len += copy_len;
        }
 
-       ce = mem_pool__ce_alloc(istate->ce_mem_pool, len);
+       ce = mem_pool__ce_alloc(ce_mem_pool, len);
 
        ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
        ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
@@ -1887,19 +1908,237 @@ static size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
        return ondisk_size + entries * per_entry;
 }
 
+struct index_entry_offset
+{
+       /* starting byte offset into index file, count of index entries in this block */
+       int offset, nr;
+};
+
+struct index_entry_offset_table
+{
+       int nr;
+       struct index_entry_offset entries[FLEX_ARRAY];
+};
+
+#ifndef NO_PTHREADS
+static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset);
+static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot);
+#endif
+
 static size_t read_eoie_extension(const char *mmap, size_t mmap_size);
 static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset);
 
+struct load_index_extensions
+{
+#ifndef NO_PTHREADS
+       pthread_t pthread;
+#endif
+       struct index_state *istate;
+       const char *mmap;
+       size_t mmap_size;
+       unsigned long src_offset;
+};
+
+static void *load_index_extensions(void *_data)
+{
+       struct load_index_extensions *p = _data;
+       unsigned long src_offset = p->src_offset;
+
+       while (src_offset <= p->mmap_size - the_hash_algo->rawsz - 8) {
+               /* After an array of active_nr index entries,
+                * there can be arbitrary number of extended
+                * sections, each of which is prefixed with
+                * extension name (4-byte) and section length
+                * in 4-byte network byte order.
+                */
+               uint32_t extsize = get_be32(p->mmap + src_offset + 4);
+               if (read_index_extension(p->istate,
+                                        p->mmap + src_offset,
+                                        p->mmap + src_offset + 8,
+                                        extsize) < 0) {
+                       munmap((void *)p->mmap, p->mmap_size);
+                       die(_("index file corrupt"));
+               }
+               src_offset += 8;
+               src_offset += extsize;
+       }
+
+       return NULL;
+}
+
+/*
+ * A helper function that will load the specified range of cache entries
+ * from the memory mapped file and add them to the given index.
+ */
+static unsigned long load_cache_entry_block(struct index_state *istate,
+                       struct mem_pool *ce_mem_pool, int offset, int nr, const char *mmap,
+                       unsigned long start_offset, const struct cache_entry *previous_ce)
+{
+       int i;
+       unsigned long src_offset = start_offset;
+
+       for (i = offset; i < offset + nr; i++) {
+               struct ondisk_cache_entry *disk_ce;
+               struct cache_entry *ce;
+               unsigned long consumed;
+
+               disk_ce = (struct ondisk_cache_entry *)(mmap + src_offset);
+               ce = create_from_disk(ce_mem_pool, istate->version, disk_ce, &consumed, previous_ce);
+               set_index_entry(istate, i, ce);
+
+               src_offset += consumed;
+               previous_ce = ce;
+       }
+       return src_offset - start_offset;
+}
+
+static unsigned long load_all_cache_entries(struct index_state *istate,
+                       const char *mmap, size_t mmap_size, unsigned long src_offset)
+{
+       unsigned long consumed;
+
+       if (istate->version == 4) {
+               mem_pool_init(&istate->ce_mem_pool,
+                               estimate_cache_size_from_compressed(istate->cache_nr));
+       } else {
+               mem_pool_init(&istate->ce_mem_pool,
+                               estimate_cache_size(mmap_size, istate->cache_nr));
+       }
+
+       consumed = load_cache_entry_block(istate, istate->ce_mem_pool,
+                                       0, istate->cache_nr, mmap, src_offset, NULL);
+       return consumed;
+}
+
+#ifndef NO_PTHREADS
+
+/*
+ * Mostly randomly chosen maximum thread counts: we
+ * cap the parallelism to online_cpus() threads, and we want
+ * to have at least 10000 cache entries per thread for it to
+ * be worth starting a thread.
+ */
+
+#define THREAD_COST            (10000)
+
+struct load_cache_entries_thread_data
+{
+       pthread_t pthread;
+       struct index_state *istate;
+       struct mem_pool *ce_mem_pool;
+       int offset;
+       const char *mmap;
+       struct index_entry_offset_table *ieot;
+       int ieot_start;         /* starting index into the ieot array */
+       int ieot_blocks;        /* count of ieot entries to process */
+       unsigned long consumed; /* return # of bytes in index file processed */
+};
+
+/*
+ * A thread proc to run the load_cache_entries() computation
+ * across multiple background threads.
+ */
+static void *load_cache_entries_thread(void *_data)
+{
+       struct load_cache_entries_thread_data *p = _data;
+       int i;
+
+       /* iterate across all ieot blocks assigned to this thread */
+       for (i = p->ieot_start; i < p->ieot_start + p->ieot_blocks; i++) {
+               p->consumed += load_cache_entry_block(p->istate, p->ce_mem_pool,
+                       p->offset, p->ieot->entries[i].nr, p->mmap, p->ieot->entries[i].offset, NULL);
+               p->offset += p->ieot->entries[i].nr;
+       }
+       return NULL;
+}
+
+static unsigned long load_cache_entries_threaded(struct index_state *istate, const char *mmap, size_t mmap_size,
+                       unsigned long src_offset, int nr_threads, struct index_entry_offset_table *ieot)
+{
+       int i, offset, ieot_blocks, ieot_start, err;
+       struct load_cache_entries_thread_data *data;
+       unsigned long consumed = 0;
+
+       /* a little sanity checking */
+       if (istate->name_hash_initialized)
+               BUG("the name hash isn't thread safe");
+
+       mem_pool_init(&istate->ce_mem_pool, 0);
+
+       /* ensure we have no more threads than we have blocks to process */
+       if (nr_threads > ieot->nr)
+               nr_threads = ieot->nr;
+       data = xcalloc(nr_threads, sizeof(*data));
+
+       offset = ieot_start = 0;
+       ieot_blocks = DIV_ROUND_UP(ieot->nr, nr_threads);
+       for (i = 0; i < nr_threads; i++) {
+               struct load_cache_entries_thread_data *p = &data[i];
+               int nr, j;
+
+               if (ieot_start + ieot_blocks > ieot->nr)
+                       ieot_blocks = ieot->nr - ieot_start;
+
+               p->istate = istate;
+               p->offset = offset;
+               p->mmap = mmap;
+               p->ieot = ieot;
+               p->ieot_start = ieot_start;
+               p->ieot_blocks = ieot_blocks;
+
+               /* create a mem_pool for each thread */
+               nr = 0;
+               for (j = p->ieot_start; j < p->ieot_start + p->ieot_blocks; j++)
+                       nr += p->ieot->entries[j].nr;
+               if (istate->version == 4) {
+                       mem_pool_init(&p->ce_mem_pool,
+                               estimate_cache_size_from_compressed(nr));
+               } else {
+                       mem_pool_init(&p->ce_mem_pool,
+                               estimate_cache_size(mmap_size, nr));
+               }
+
+               err = pthread_create(&p->pthread, NULL, load_cache_entries_thread, p);
+               if (err)
+                       die(_("unable to create load_cache_entries thread: %s"), strerror(err));
+
+               /* increment by the number of cache entries in the ieot block being processed */
+               for (j = 0; j < ieot_blocks; j++)
+                       offset += ieot->entries[ieot_start + j].nr;
+               ieot_start += ieot_blocks;
+       }
+
+       for (i = 0; i < nr_threads; i++) {
+               struct load_cache_entries_thread_data *p = &data[i];
+
+               err = pthread_join(p->pthread, NULL);
+               if (err)
+                       die(_("unable to join load_cache_entries thread: %s"), strerror(err));
+               mem_pool_combine(istate->ce_mem_pool, p->ce_mem_pool);
+               consumed += p->consumed;
+       }
+
+       free(data);
+
+       return consumed;
+}
+#endif
+
 /* remember to discard_cache() before reading a different cache! */
 int do_read_index(struct index_state *istate, const char *path, int must_exist)
 {
-       int fd, i;
+       int fd;
        struct stat st;
        unsigned long src_offset;
        const struct cache_header *hdr;
        const char *mmap;
        size_t mmap_size;
-       const struct cache_entry *previous_ce = NULL;
+       struct load_index_extensions p;
+       size_t extension_offset = 0;
+#ifndef NO_PTHREADS
+       int nr_threads, cpus;
+       struct index_entry_offset_table *ieot = NULL;
+#endif
 
        if (istate->initialized)
                return istate->cache_nr;
@@ -1936,46 +2175,68 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache));
        istate->initialized = 1;
 
-       if (istate->version == 4) {
-               mem_pool_init(&istate->ce_mem_pool,
-                             estimate_cache_size_from_compressed(istate->cache_nr));
-       } else {
-               mem_pool_init(&istate->ce_mem_pool,
-                             estimate_cache_size(mmap_size, istate->cache_nr));
-       }
+       p.istate = istate;
+       p.mmap = mmap;
+       p.mmap_size = mmap_size;
 
        src_offset = sizeof(*hdr);
-       for (i = 0; i < istate->cache_nr; i++) {
-               struct ondisk_cache_entry *disk_ce;
-               struct cache_entry *ce;
-               unsigned long consumed;
 
-               disk_ce = (struct ondisk_cache_entry *)(mmap + src_offset);
-               ce = create_from_disk(istate, disk_ce, &consumed, previous_ce);
-               set_index_entry(istate, i, ce);
+#ifndef NO_PTHREADS
+       nr_threads = git_config_get_index_threads();
 
-               src_offset += consumed;
-               previous_ce = ce;
+       /* TODO: does creating more threads than cores help? */
+       if (!nr_threads) {
+               nr_threads = istate->cache_nr / THREAD_COST;
+               cpus = online_cpus();
+               if (nr_threads > cpus)
+                       nr_threads = cpus;
        }
+
+       if (nr_threads > 1) {
+               extension_offset = read_eoie_extension(mmap, mmap_size);
+               if (extension_offset) {
+                       int err;
+
+                       p.src_offset = extension_offset;
+                       err = pthread_create(&p.pthread, NULL, load_index_extensions, &p);
+                       if (err)
+                               die(_("unable to create load_index_extensions thread: %s"), strerror(err));
+
+                       nr_threads--;
+               }
+       }
+
+       /*
+        * Locate and read the index entry offset table so that we can use it
+        * to multi-thread the reading of the cache entries.
+        */
+       if (extension_offset && nr_threads > 1)
+               ieot = read_ieot_extension(mmap, mmap_size, extension_offset);
+
+       if (ieot) {
+               src_offset += load_cache_entries_threaded(istate, mmap, mmap_size, src_offset, nr_threads, ieot);
+               free(ieot);
+       } else {
+               src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset);
+       }
+#else
+       src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset);
+#endif
+
        istate->timestamp.sec = st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
 
-       while (src_offset <= mmap_size - the_hash_algo->rawsz - 8) {
-               /* After an array of active_nr index entries,
-                * there can be arbitrary number of extended
-                * sections, each of which is prefixed with
-                * extension name (4-byte) and section length
-                * in 4-byte network byte order.
-                */
-               uint32_t extsize;
-               extsize = get_be32(mmap + src_offset + 4);
-               if (read_index_extension(istate,
-                                        mmap + src_offset,
-                                        mmap + src_offset + 8,
-                                        extsize) < 0)
-                       goto unmap;
-               src_offset += 8;
-               src_offset += extsize;
+       /* if we created a thread, join it otherwise load the extensions on the primary thread */
+#ifndef NO_PTHREADS
+       if (extension_offset) {
+               int ret = pthread_join(p.pthread, NULL);
+               if (ret)
+                       die(_("unable to join load_index_extensions thread: %s"), strerror(ret));
+       }
+#endif
+       if (!extension_offset) {
+               p.src_offset = src_offset;
+               load_index_extensions(&p);
        }
        munmap((void *)mmap, mmap_size);
        return istate->cache_nr;
@@ -2121,7 +2382,7 @@ int unmerged_index(const struct index_state *istate)
        return 0;
 }
 
-int index_has_changes(const struct index_state *istate,
+int index_has_changes(struct index_state *istate,
                      struct tree *tree,
                      struct strbuf *sb)
 {
@@ -2136,7 +2397,7 @@ int index_has_changes(const struct index_state *istate,
        if (tree || !get_oid_tree("HEAD", &cmp)) {
                struct diff_options opt;
 
-               diff_setup(&opt);
+               repo_diff_setup(the_repository, &opt);
                opt.flags.exit_with_status = 1;
                if (!sb)
                        opt.flags.quick = 1;
@@ -2233,7 +2494,8 @@ static int ce_flush(git_hash_ctx *context, int fd, unsigned char *hash)
        return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
 }
 
-static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
+static void ce_smudge_racily_clean_entry(struct index_state *istate,
+                                        struct cache_entry *ce)
 {
        /*
         * The only thing we care about in this function is to smudge the
@@ -2252,7 +2514,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
                return;
        if (ce_match_stat_basic(ce, &st))
                return;
-       if (ce_modified_check_fs(ce, &st)) {
+       if (ce_modified_check_fs(istate, ce, &st)) {
                /* This is "racily clean"; smudge it.  Note that this
                 * is a tricky code.  At first glance, it may appear
                 * that it can break with this sequence:
@@ -2458,6 +2720,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
        int drop_cache_tree = istate->drop_cache_tree;
        off_t offset;
+       int ieot_entries = 1;
+       struct index_entry_offset_table *ieot = NULL;
+       int nr, nr_threads;
 
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
@@ -2491,10 +2756,46 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
                return -1;
 
+#ifndef NO_PTHREADS
+       nr_threads = git_config_get_index_threads();
+       if (nr_threads != 1) {
+               int ieot_blocks, cpus;
+
+               /*
+                * ensure default number of ieot blocks maps evenly to the
+                * default number of threads that will process them leaving
+                * room for the thread to load the index extensions.
+                */
+               if (!nr_threads) {
+                       ieot_blocks = istate->cache_nr / THREAD_COST;
+                       cpus = online_cpus();
+                       if (ieot_blocks > cpus - 1)
+                               ieot_blocks = cpus - 1;
+               } else {
+                       ieot_blocks = nr_threads;
+                       if (ieot_blocks > istate->cache_nr)
+                               ieot_blocks = istate->cache_nr;
+               }
+
+               /*
+                * no reason to write out the IEOT extension if we don't
+                * have enough blocks to utilize multi-threading
+                */
+               if (ieot_blocks > 1) {
+                       ieot = xcalloc(1, sizeof(struct index_entry_offset_table)
+                               + (ieot_blocks * sizeof(struct index_entry_offset)));
+                       ieot_entries = DIV_ROUND_UP(entries, ieot_blocks);
+               }
+       }
+#endif
+
        offset = lseek(newfd, 0, SEEK_CUR);
-       if (offset < 0)
+       if (offset < 0) {
+               free(ieot);
                return -1;
+       }
        offset += write_buffer_len;
+       nr = 0;
        previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
 
        for (i = 0; i < entries; i++) {
@@ -2502,7 +2803,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                if (ce->ce_flags & CE_REMOVE)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
-                       ce_smudge_racily_clean_entry(ce);
+                       ce_smudge_racily_clean_entry(istate, ce);
                if (is_null_oid(&ce->oid)) {
                        static const char msg[] = "cache entry has null sha1: %s";
                        static int allow = -1;
@@ -2516,24 +2817,74 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
 
                        drop_cache_tree = 1;
                }
+               if (ieot && i && (i % ieot_entries == 0)) {
+                       ieot->entries[ieot->nr].nr = nr;
+                       ieot->entries[ieot->nr].offset = offset;
+                       ieot->nr++;
+                       /*
+                        * If we have a V4 index, set the first byte to an invalid
+                        * character to ensure there is nothing common with the previous
+                        * entry
+                        */
+                       if (previous_name)
+                               previous_name->buf[0] = 0;
+                       nr = 0;
+                       offset = lseek(newfd, 0, SEEK_CUR);
+                       if (offset < 0) {
+                               free(ieot);
+                               return -1;
+                       }
+                       offset += write_buffer_len;
+               }
                if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
                        err = -1;
 
                if (err)
                        break;
+               nr++;
+       }
+       if (ieot && nr) {
+               ieot->entries[ieot->nr].nr = nr;
+               ieot->entries[ieot->nr].offset = offset;
+               ieot->nr++;
        }
        strbuf_release(&previous_name_buf);
 
-       if (err)
+       if (err) {
+               free(ieot);
                return err;
+       }
 
        /* Write extension data here */
        offset = lseek(newfd, 0, SEEK_CUR);
-       if (offset < 0)
+       if (offset < 0) {
+               free(ieot);
                return -1;
+       }
        offset += write_buffer_len;
        the_hash_algo->init_fn(&eoie_c);
 
+       /*
+        * Lets write out CACHE_EXT_INDEXENTRYOFFSETTABLE first so that we
+        * can minimize the number of extensions we have to scan through to
+        * find it during load.  Write it out regardless of the
+        * strip_extensions parameter as we need it when loading the shared
+        * index.
+        */
+#ifndef NO_PTHREADS
+       if (ieot) {
+               struct strbuf sb = STRBUF_INIT;
+
+               write_ieot_extension(&sb, ieot);
+               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0
+                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               strbuf_release(&sb);
+               free(ieot);
+               if (err)
+                       return -1;
+       }
+#endif
+
        if (!strip_extensions && istate->split_index) {
                struct strbuf sb = STRBUF_INIT;
 
@@ -3117,3 +3468,78 @@ static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context,
        the_hash_algo->final_fn(hash, eoie_context);
        strbuf_add(sb, hash, the_hash_algo->rawsz);
 }
+
+#ifndef NO_PTHREADS
+#define IEOT_VERSION   (1)
+
+static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset)
+{
+       const char *index = NULL;
+       uint32_t extsize, ext_version;
+       struct index_entry_offset_table *ieot;
+       int i, nr;
+
+       /* find the IEOT extension */
+       if (!offset)
+              return NULL;
+       while (offset <= mmap_size - the_hash_algo->rawsz - 8) {
+              extsize = get_be32(mmap + offset + 4);
+              if (CACHE_EXT((mmap + offset)) == CACHE_EXT_INDEXENTRYOFFSETTABLE) {
+                      index = mmap + offset + 4 + 4;
+                      break;
+              }
+              offset += 8;
+              offset += extsize;
+       }
+       if (!index)
+              return NULL;
+
+       /* validate the version is IEOT_VERSION */
+       ext_version = get_be32(index);
+       if (ext_version != IEOT_VERSION) {
+              error("invalid IEOT version %d", ext_version);
+              return NULL;
+       }
+       index += sizeof(uint32_t);
+
+       /* extension size - version bytes / bytes per entry */
+       nr = (extsize - sizeof(uint32_t)) / (sizeof(uint32_t) + sizeof(uint32_t));
+       if (!nr) {
+              error("invalid number of IEOT entries %d", nr);
+              return NULL;
+       }
+       ieot = xmalloc(sizeof(struct index_entry_offset_table)
+              + (nr * sizeof(struct index_entry_offset)));
+       ieot->nr = nr;
+       for (i = 0; i < nr; i++) {
+              ieot->entries[i].offset = get_be32(index);
+              index += sizeof(uint32_t);
+              ieot->entries[i].nr = get_be32(index);
+              index += sizeof(uint32_t);
+       }
+
+       return ieot;
+}
+
+static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot)
+{
+       uint32_t buffer;
+       int i;
+
+       /* version */
+       put_be32(&buffer, IEOT_VERSION);
+       strbuf_add(sb, &buffer, sizeof(uint32_t));
+
+       /* ieot */
+       for (i = 0; i < ieot->nr; i++) {
+
+              /* offset */
+              put_be32(&buffer, ieot->entries[i].offset);
+              strbuf_add(sb, &buffer, sizeof(uint32_t));
+
+              /* count */
+              put_be32(&buffer, ieot->entries[i].nr);
+              strbuf_add(sb, &buffer, sizeof(uint32_t));
+       }
+}
+#endif