]> git.ipfire.org Git - thirdparty/git.git/blobdiff - read-cache.c
Merge branch 'rs/make-verify-path-really-verify-again'
[thirdparty/git.git] / read-cache.c
index 42d804f9623f0a86322df2b21cd8c976bfb4f985..8a50ff66b3010a736db85f769007c64208590503 100644 (file)
@@ -26,6 +26,8 @@
 #include "thread-utils.h"
 #include "progress.h"
 #include "sparse-index.h"
+#include "csum-file.h"
+#include "promisor-remote.h"
 
 /* Mask for the name length in ce_flags in the on-disk index */
 
@@ -839,8 +841,11 @@ struct cache_entry *make_empty_cache_entry(struct index_state *istate, size_t le
        return mem_pool__ce_calloc(find_mem_pool(istate), len);
 }
 
-struct cache_entry *make_empty_transient_cache_entry(size_t len)
+struct cache_entry *make_empty_transient_cache_entry(size_t len,
+                                                    struct mem_pool *ce_mem_pool)
 {
+       if (ce_mem_pool)
+               return mem_pool__ce_calloc(ce_mem_pool, len);
        return xcalloc(1, cache_entry_size(len));
 }
 
@@ -887,8 +892,11 @@ struct cache_entry *make_cache_entry(struct index_state *istate,
        return ret;
 }
 
-struct cache_entry *make_transient_cache_entry(unsigned int mode, const struct object_id *oid,
-                                              const char *path, int stage)
+struct cache_entry *make_transient_cache_entry(unsigned int mode,
+                                              const struct object_id *oid,
+                                              const char *path,
+                                              int stage,
+                                              struct mem_pool *ce_mem_pool)
 {
        struct cache_entry *ce;
        int len;
@@ -899,7 +907,7 @@ struct cache_entry *make_transient_cache_entry(unsigned int mode, const struct o
        }
 
        len = strlen(path);
-       ce = make_empty_transient_cache_entry(len);
+       ce = make_empty_transient_cache_entry(len, ce_mem_pool);
 
        oidcpy(&ce->oid, oid);
        memcpy(ce->name, path, len);
@@ -1025,7 +1033,7 @@ inside:
                                }
                        }
                        if (protect_ntfs) {
-#ifdef GIT_WINDOWS_NATIVE
+#if defined GIT_WINDOWS_NATIVE || defined __CYGWIN__
                                if (c == '\\')
                                        return PATH_INVALID;
 #endif
@@ -1561,6 +1569,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
        int quiet = (flags & REFRESH_QUIET) != 0;
        int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
        int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
+       int ignore_skip_worktree = (flags & REFRESH_IGNORE_SKIP_WORKTREE) != 0;
        int first = 1;
        int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
        unsigned int options = (CE_MATCH_REFRESH |
@@ -1592,8 +1601,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
         */
        preload_index(istate, pathspec, 0);
        trace2_region_enter("index", "refresh", NULL);
-       /* TODO: audit for interaction with sparse-index. */
-       ensure_full_index(istate);
+
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce, *new_entry;
                int cache_errno = 0;
@@ -1605,6 +1613,15 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                ce = istate->cache[i];
                if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
                        continue;
+               if (ignore_skip_worktree && ce_skip_worktree(ce))
+                       continue;
+
+               /*
+                * If this entry is a sparse directory, then there isn't
+                * any stat() information to update. Ignore the entry.
+                */
+               if (S_ISSPARSEDIR(ce->ce_mode))
+                       continue;
 
                if (pathspec && !ce_path_match(istate, ce, pathspec, seen))
                        filtered = 1;
@@ -1633,8 +1650,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
                t2_sum_scan += t2_did_scan;
                if (new_entry == ce)
                        continue;
-               if (progress)
-                       display_progress(progress, i);
+               display_progress(progress, i);
                if (!new_entry) {
                        const char *fmt;
 
@@ -1669,10 +1685,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,
        trace2_data_intmax("index", NULL, "refresh/sum_lstat", t2_sum_lstat);
        trace2_data_intmax("index", NULL, "refresh/sum_scan", t2_sum_scan);
        trace2_region_leave("index", "refresh", NULL);
-       if (progress) {
-               display_progress(progress, istate->cache_nr);
-               stop_progress(&progress);
-       }
+       display_progress(progress, istate->cache_nr);
+       stop_progress(&progress);
        trace_performance_leave("refresh index");
        return has_errors;
 }
@@ -1898,7 +1912,7 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        ce->index = 0;
-       hashcpy(ce->oid.hash, ondisk->data);
+       oidread(&ce->oid, ondisk->data);
        memcpy(ce->name, name, len);
        ce->name[len] = '\0';
 
@@ -1945,13 +1959,22 @@ static void tweak_untracked_cache(struct index_state *istate)
 
        prepare_repo_settings(r);
 
-       if (r->settings.core_untracked_cache  == UNTRACKED_CACHE_REMOVE) {
+       switch (r->settings.core_untracked_cache) {
+       case UNTRACKED_CACHE_REMOVE:
                remove_untracked_cache(istate);
-               return;
-       }
-
-       if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
+               break;
+       case UNTRACKED_CACHE_WRITE:
                add_untracked_cache(istate);
+               break;
+       case UNTRACKED_CACHE_KEEP:
+               /*
+                * Either an explicit "core.untrackedCache=keep", the
+                * default if "core.untrackedCache" isn't configured,
+                * or a fallback on an unknown "core.untrackedCache"
+                * value.
+                */
+               break;
+       }
 }
 
 static void tweak_split_index(struct index_state *istate)
@@ -2241,14 +2264,15 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
 
        mmap = xmmap_gently(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (mmap == MAP_FAILED)
-               die_errno(_("%s: unable to map index file"), path);
+               die_errno(_("%s: unable to map index file%s"), path,
+                       mmap_os_err());
        close(fd);
 
        hdr = (const struct cache_header *)mmap;
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
 
-       hashcpy(istate->oid.hash, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz);
+       oidread(&istate->oid, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz);
        istate->version = ntohl(hdr->hdr_version);
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
@@ -2391,9 +2415,21 @@ int read_index_from(struct index_state *istate, const char *path,
        base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
        trace2_region_enter_printf("index", "shared/do_read_index",
                                   the_repository, "%s", base_path);
-       ret = do_read_index(split_index->base, base_path, 1);
+       ret = do_read_index(split_index->base, base_path, 0);
        trace2_region_leave_printf("index", "shared/do_read_index",
                                   the_repository, "%s", base_path);
+       if (!ret) {
+               char *path_copy = xstrdup(path);
+               const char *base_path2 = xstrfmt("%s/sharedindex.%s",
+                                                dirname(path_copy),
+                                                base_oid_hex);
+               free(path_copy);
+               trace2_region_enter_printf("index", "shared/do_read_index",
+                                          the_repository, "%s", base_path2);
+               ret = do_read_index(split_index->base, base_path2, 1);
+               trace2_region_leave_printf("index", "shared/do_read_index",
+                                          the_repository, "%s", base_path2);
+       }
        if (!oideq(&split_index->base_oid, &split_index->base->oid))
                die(_("broken index, expect %s in %s, got %s"),
                    base_oid_hex, base_path,
@@ -2506,6 +2542,7 @@ int repo_index_has_changes(struct repository *repo,
                opt.flags.exit_with_status = 1;
                if (!sb)
                        opt.flags.quick = 1;
+               diff_setup_done(&opt);
                do_diff_cache(&cmp, &opt);
                diffcore_std(&opt);
                for (i = 0; sb && i < diff_queued_diff.nr; i++) {
@@ -2527,80 +2564,23 @@ int repo_index_has_changes(struct repository *repo,
        }
 }
 
-#define WRITE_BUFFER_SIZE (128 * 1024)
-static unsigned char write_buffer[WRITE_BUFFER_SIZE];
-static unsigned long write_buffer_len;
-
-static int ce_write_flush(git_hash_ctx *context, int fd)
+static int write_index_ext_header(struct hashfile *f,
+                                 git_hash_ctx *eoie_f,
+                                 unsigned int ext,
+                                 unsigned int sz)
 {
-       unsigned int buffered = write_buffer_len;
-       if (buffered) {
-               the_hash_algo->update_fn(context, write_buffer, buffered);
-               if (write_in_full(fd, write_buffer, buffered) < 0)
-                       return -1;
-               write_buffer_len = 0;
-       }
-       return 0;
-}
+       hashwrite_be32(f, ext);
+       hashwrite_be32(f, sz);
 
-static int ce_write(git_hash_ctx *context, int fd, void *data, unsigned int len)
-{
-       while (len) {
-               unsigned int buffered = write_buffer_len;
-               unsigned int partial = WRITE_BUFFER_SIZE - buffered;
-               if (partial > len)
-                       partial = len;
-               memcpy(write_buffer + buffered, data, partial);
-               buffered += partial;
-               if (buffered == WRITE_BUFFER_SIZE) {
-                       write_buffer_len = buffered;
-                       if (ce_write_flush(context, fd))
-                               return -1;
-                       buffered = 0;
-               }
-               write_buffer_len = buffered;
-               len -= partial;
-               data = (char *) data + partial;
+       if (eoie_f) {
+               ext = htonl(ext);
+               sz = htonl(sz);
+               the_hash_algo->update_fn(eoie_f, &ext, sizeof(ext));
+               the_hash_algo->update_fn(eoie_f, &sz, sizeof(sz));
        }
        return 0;
 }
 
-static int write_index_ext_header(git_hash_ctx *context, git_hash_ctx *eoie_context,
-                                 int fd, unsigned int ext, unsigned int sz)
-{
-       ext = htonl(ext);
-       sz = htonl(sz);
-       if (eoie_context) {
-               the_hash_algo->update_fn(eoie_context, &ext, 4);
-               the_hash_algo->update_fn(eoie_context, &sz, 4);
-       }
-       return ((ce_write(context, fd, &ext, 4) < 0) ||
-               (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
-}
-
-static int ce_flush(git_hash_ctx *context, int fd, unsigned char *hash)
-{
-       unsigned int left = write_buffer_len;
-
-       if (left) {
-               write_buffer_len = 0;
-               the_hash_algo->update_fn(context, write_buffer, left);
-       }
-
-       /* Flush first if not enough space for hash signature */
-       if (left + the_hash_algo->rawsz > WRITE_BUFFER_SIZE) {
-               if (write_in_full(fd, write_buffer, left) < 0)
-                       return -1;
-               left = 0;
-       }
-
-       /* Append the hash signature at the end */
-       the_hash_algo->final_fn(write_buffer + left, context);
-       hashcpy(hash, write_buffer + left);
-       left += the_hash_algo->rawsz;
-       return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
-}
-
 static void ce_smudge_racily_clean_entry(struct index_state *istate,
                                         struct cache_entry *ce)
 {
@@ -2679,11 +2659,10 @@ static void copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
        }
 }
 
-static int ce_write_entry(git_hash_ctx *c, int fd, struct cache_entry *ce,
+static int ce_write_entry(struct hashfile *f, struct cache_entry *ce,
                          struct strbuf *previous_name, struct ondisk_cache_entry *ondisk)
 {
        int size;
-       int result;
        unsigned int saved_namelen;
        int stripped_name = 0;
        static unsigned char padding[8] = { 0x00 };
@@ -2699,11 +2678,9 @@ static int ce_write_entry(git_hash_ctx *c, int fd, struct cache_entry *ce,
        if (!previous_name) {
                int len = ce_namelen(ce);
                copy_cache_entry_to_ondisk(ondisk, ce);
-               result = ce_write(c, fd, ondisk, size);
-               if (!result)
-                       result = ce_write(c, fd, ce->name, len);
-               if (!result)
-                       result = ce_write(c, fd, padding, align_padding_size(size, len));
+               hashwrite(f, ondisk, size);
+               hashwrite(f, ce->name, len);
+               hashwrite(f, padding, align_padding_size(size, len));
        } else {
                int common, to_remove, prefix_size;
                unsigned char to_remove_vi[16];
@@ -2717,13 +2694,10 @@ static int ce_write_entry(git_hash_ctx *c, int fd, struct cache_entry *ce,
                prefix_size = encode_varint(to_remove, to_remove_vi);
 
                copy_cache_entry_to_ondisk(ondisk, ce);
-               result = ce_write(c, fd, ondisk, size);
-               if (!result)
-                       result = ce_write(c, fd, to_remove_vi, prefix_size);
-               if (!result)
-                       result = ce_write(c, fd, ce->name + common, ce_namelen(ce) - common);
-               if (!result)
-                       result = ce_write(c, fd, padding, 1);
+               hashwrite(f, ondisk, size);
+               hashwrite(f, to_remove_vi, prefix_size);
+               hashwrite(f, ce->name + common, ce_namelen(ce) - common);
+               hashwrite(f, padding, 1);
 
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
@@ -2733,7 +2707,7 @@ static int ce_write_entry(git_hash_ctx *c, int fd, struct cache_entry *ce,
                ce->ce_flags &= ~CE_STRIP_NAME;
        }
 
-       return result;
+       return 0;
 }
 
 /*
@@ -2845,8 +2819,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                          int strip_extensions)
 {
        uint64_t start = getnanotime();
-       int newfd = tempfile->fd;
-       git_hash_ctx c, eoie_c;
+       struct hashfile *f;
+       git_hash_ctx *eoie_c = NULL;
        struct cache_header hdr;
        int i, err = 0, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
@@ -2860,6 +2834,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        struct index_entry_offset_table *ieot = NULL;
        int nr, nr_threads;
 
+       f = hashfd(tempfile->fd, tempfile->filename.buf);
+
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
                        removed++;
@@ -2872,11 +2848,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                }
        }
 
-       if (!istate->version) {
+       if (!istate->version)
                istate->version = get_index_format_default(the_repository);
-               if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0))
-                       init_split_index(istate);
-       }
 
        /* demote version 3 to version 2 when the latter suffices */
        if (istate->version == 3 || istate->version == 2)
@@ -2888,9 +2861,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        hdr.hdr_version = htonl(hdr_version);
        hdr.hdr_entries = htonl(entries - removed);
 
-       the_hash_algo->init_fn(&c);
-       if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
-               return -1;
+       hashwrite(f, &hdr, sizeof(hdr));
 
        if (!HAVE_THREADS || git_config_get_index_threads(&nr_threads))
                nr_threads = 1;
@@ -2925,12 +2896,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                }
        }
 
-       offset = lseek(newfd, 0, SEEK_CUR);
-       if (offset < 0) {
-               free(ieot);
-               return -1;
-       }
-       offset += write_buffer_len;
+       offset = hashfile_total(f);
+
        nr = 0;
        previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
 
@@ -2965,14 +2932,10 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                        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;
+
+                       offset = hashfile_total(f);
                }
-               if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
+               if (ce_write_entry(f, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
                        err = -1;
 
                if (err)
@@ -2991,14 +2954,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                return err;
        }
 
-       /* Write extension data here */
-       offset = lseek(newfd, 0, SEEK_CUR);
-       if (offset < 0) {
-               free(ieot);
-               return -1;
+       offset = hashfile_total(f);
+
+       /*
+        * The extension headers must be hashed on their own for the
+        * EOIE extension. Create a hashfile here to compute that hash.
+        */
+       if (offset && record_eoie()) {
+               CALLOC_ARRAY(eoie_c, 1);
+               the_hash_algo->init_fn(eoie_c);
        }
-       offset += write_buffer_len;
-       the_hash_algo->init_fn(&eoie_c);
 
        /*
         * Lets write out CACHE_EXT_INDEXENTRYOFFSETTABLE first so that we
@@ -3011,8 +2976,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                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;
+               err = write_index_ext_header(f, eoie_c, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0;
+               hashwrite(f, sb.buf, sb.len);
                strbuf_release(&sb);
                free(ieot);
                if (err)
@@ -3024,9 +2989,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                err = write_link_extension(&sb, istate) < 0 ||
-                       write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_LINK,
-                                              sb.len) < 0 ||
-                       ce_write(&c, newfd, sb.buf, sb.len) < 0;
+                       write_index_ext_header(f, eoie_c, CACHE_EXT_LINK,
+                                              sb.len) < 0;
+               hashwrite(f, sb.buf, sb.len);
                strbuf_release(&sb);
                if (err)
                        return -1;
@@ -3035,8 +3000,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                cache_tree_write(&sb, istate->cache_tree);
-               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_TREE, sb.len) < 0
-                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               err = write_index_ext_header(f, eoie_c, CACHE_EXT_TREE, sb.len) < 0;
+               hashwrite(f, sb.buf, sb.len);
                strbuf_release(&sb);
                if (err)
                        return -1;
@@ -3045,9 +3010,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                resolve_undo_write(&sb, istate->resolve_undo);
-               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_RESOLVE_UNDO,
-                                            sb.len) < 0
-                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               err = write_index_ext_header(f, eoie_c, CACHE_EXT_RESOLVE_UNDO,
+                                            sb.len) < 0;
+               hashwrite(f, sb.buf, sb.len);
                strbuf_release(&sb);
                if (err)
                        return -1;
@@ -3056,9 +3021,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                write_untracked_extension(&sb, istate->untracked);
-               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_UNTRACKED,
-                                            sb.len) < 0 ||
-                       ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               err = write_index_ext_header(f, eoie_c, CACHE_EXT_UNTRACKED,
+                                            sb.len) < 0;
+               hashwrite(f, sb.buf, sb.len);
                strbuf_release(&sb);
                if (err)
                        return -1;
@@ -3067,14 +3032,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                struct strbuf sb = STRBUF_INIT;
 
                write_fsmonitor_extension(&sb, istate);
-               err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
-                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               err = write_index_ext_header(f, eoie_c, CACHE_EXT_FSMONITOR, sb.len) < 0;
+               hashwrite(f, sb.buf, sb.len);
                strbuf_release(&sb);
                if (err)
                        return -1;
        }
        if (istate->sparse_index) {
-               if (write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0)
+               if (write_index_ext_header(f, eoie_c, CACHE_EXT_SPARSE_DIRECTORIES, 0) < 0)
                        return -1;
        }
 
@@ -3084,19 +3049,18 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
         * read.  Write it out regardless of the strip_extensions parameter as we need it
         * when loading the shared index.
         */
-       if (offset && record_eoie()) {
+       if (eoie_c) {
                struct strbuf sb = STRBUF_INIT;
 
-               write_eoie_extension(&sb, &eoie_c, offset);
-               err = write_index_ext_header(&c, NULL, newfd, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0
-                       || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+               write_eoie_extension(&sb, eoie_c, offset);
+               err = write_index_ext_header(f, NULL, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0;
+               hashwrite(f, sb.buf, sb.len);
                strbuf_release(&sb);
                if (err)
                        return -1;
        }
 
-       if (ce_flush(&c, newfd, istate->oid.hash))
-               return -1;
+       finalize_hashfile(f, istate->oid.hash, CSUM_HASH_IN_STREAM);
        if (close_tempfile_gently(tempfile)) {
                error(_("could not close '%s'"), get_tempfile_path(tempfile));
                return -1;
@@ -3138,7 +3102,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
        int ret;
        int was_full = !istate->sparse_index;
 
-       ret = convert_to_sparse(istate);
+       ret = convert_to_sparse(istate, 0);
 
        if (ret) {
                warning(_("failed to convert to a sparse-index"));
@@ -3251,7 +3215,7 @@ static int write_shared_index(struct index_state *istate,
        int ret, was_full = !istate->sparse_index;
 
        move_cache_to_base_index(istate);
-       convert_to_sparse(istate);
+       convert_to_sparse(istate, 0);
 
        trace2_region_enter_printf("index", "shared/do_write_index",
                                   the_repository, "%s", get_tempfile_path(*temp));
@@ -3312,7 +3276,7 @@ static int too_many_not_shared_entries(struct index_state *istate)
 int write_locked_index(struct index_state *istate, struct lock_file *lock,
                       unsigned flags)
 {
-       int new_shared_index, ret;
+       int new_shared_index, ret, test_split_index_env;
        struct split_index *si = istate->split_index;
 
        if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
@@ -3327,7 +3291,10 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
        if (istate->fsmonitor_last_update)
                fill_fsmonitor_bitmap(istate);
 
-       if (!si || alternate_index_output ||
+       test_split_index_env = git_env_bool("GIT_TEST_SPLIT_INDEX", 0);
+
+       if ((!si && !test_split_index_env) ||
+           alternate_index_output ||
            (istate->cache_changed & ~EXTMASK)) {
                if (si)
                        oidclr(&si->base_oid);
@@ -3335,10 +3302,15 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
                goto out;
        }
 
-       if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0)) {
-               int v = si->base_oid.hash[0];
-               if ((v & 15) < 6)
+       if (test_split_index_env) {
+               if (!si) {
+                       si = init_split_index(istate);
                        istate->cache_changed |= SPLIT_INDEX_ORDERED;
+               } else {
+                       int v = si->base_oid.hash[0];
+                       if ((v & 15) < 6)
+                               istate->cache_changed |= SPLIT_INDEX_ORDERED;
+               }
        }
        if (too_many_not_shared_entries(istate))
                istate->cache_changed |= SPLIT_INDEX_ORDERED;
@@ -3734,3 +3706,25 @@ static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_ta
                strbuf_add(sb, &buffer, sizeof(uint32_t));
        }
 }
+
+void prefetch_cache_entries(const struct index_state *istate,
+                           must_prefetch_predicate must_prefetch)
+{
+       int i;
+       struct oid_array to_fetch = OID_ARRAY_INIT;
+
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
+
+               if (S_ISGITLINK(ce->ce_mode) || !must_prefetch(ce))
+                       continue;
+               if (!oid_object_info_extended(the_repository, &ce->oid,
+                                             NULL,
+                                             OBJECT_INFO_FOR_PREFETCH))
+                       continue;
+               oid_array_append(&to_fetch, &ce->oid);
+       }
+       promisor_remote_get_direct(the_repository,
+                                  to_fetch.oid, to_fetch.nr);
+       oid_array_clear(&to_fetch);
+}