read-cache: avoid allocating every ondisk entry when writing
[gitweb.git] / split-index.c
index b03a250e7cb9072aae525e646184239111f09c3f..83e39ec8d7739816188ba909a4cb9da302d48f73 100644 (file)
@@ -41,13 +41,6 @@ int read_link_extension(struct index_state *istate,
        return 0;
 }
 
-static int write_strbuf(void *user_data, const void *data, size_t len)
-{
-       struct strbuf *sb = user_data;
-       strbuf_add(sb, data, len);
-       return len;
-}
-
 int write_link_extension(struct strbuf *sb,
                         struct index_state *istate)
 {
@@ -55,8 +48,8 @@ int write_link_extension(struct strbuf *sb,
        strbuf_add(sb, si->base_sha1, 20);
        if (!si->delete_bitmap && !si->replace_bitmap)
                return 0;
-       ewah_serialize_to(si->delete_bitmap, write_strbuf, sb);
-       ewah_serialize_to(si->replace_bitmap, write_strbuf, sb);
+       ewah_serialize_strbuf(si->delete_bitmap, sb);
+       ewah_serialize_strbuf(si->replace_bitmap, sb);
        return 0;
 }
 
@@ -67,13 +60,35 @@ static void mark_base_index_entries(struct index_state *base)
         * To keep track of the shared entries between
         * istate->base->cache[] and istate->cache[], base entry
         * position is stored in each base entry. All positions start
-        * from 1 instead of 0, which is resrved to say "this is a new
+        * from 1 instead of 0, which is reserved to say "this is a new
         * entry".
         */
        for (i = 0; i < base->cache_nr; i++)
                base->cache[i]->index = i + 1;
 }
 
+void move_cache_to_base_index(struct index_state *istate)
+{
+       struct split_index *si = istate->split_index;
+       int i;
+
+       /*
+        * do not delete old si->base, its index entries may be shared
+        * with istate->cache[]. Accept a bit of leaking here because
+        * this code is only used by short-lived update-index.
+        */
+       si->base = xcalloc(1, sizeof(*si->base));
+       si->base->version = istate->version;
+       /* zero timestamp disables racy test in ce_write_index() */
+       si->base->timestamp = istate->timestamp;
+       ALLOC_GROW(si->base->cache, istate->cache_nr, si->base->cache_alloc);
+       si->base->cache_nr = istate->cache_nr;
+       COPY_ARRAY(si->base->cache, istate->cache, istate->cache_nr);
+       mark_base_index_entries(si->base);
+       for (i = 0; i < si->base->cache_nr; i++)
+               si->base->cache[i]->ce_flags &= ~CE_UPDATE_IN_BASE;
+}
+
 static void mark_entry_for_delete(size_t pos, void *data)
 {
        struct index_state *istate = data;
@@ -89,6 +104,7 @@ static void replace_entry(size_t pos, void *data)
        struct index_state *istate = data;
        struct split_index *si = istate->split_index;
        struct cache_entry *dst, *src;
+
        if (pos >= istate->cache_nr)
                die("position for replacement %d exceeds base index size %d",
                    (int)pos, istate->cache_nr);
@@ -100,10 +116,14 @@ static void replace_entry(size_t pos, void *data)
                die("entry %d is marked as both replaced and deleted",
                    (int)pos);
        src = si->saved_cache[si->nr_replacements];
+       if (ce_namelen(src))
+               die("corrupt link extension, entry %d should have "
+                   "zero length name", (int)pos);
        src->index = pos + 1;
        src->ce_flags |= CE_UPDATE_IN_BASE;
-       free(dst);
-       dst = src;
+       src->ce_namelen = dst->ce_namelen;
+       copy_cache_entry(dst, src);
+       free(src);
        si->nr_replacements++;
 }
 
@@ -120,8 +140,7 @@ void merge_base_index(struct index_state *istate)
        istate->cache       = NULL;
        istate->cache_alloc = 0;
        ALLOC_GROW(istate->cache, istate->cache_nr, istate->cache_alloc);
-       memcpy(istate->cache, si->base->cache,
-              sizeof(*istate->cache) * istate->cache_nr);
+       COPY_ARRAY(istate->cache, si->base->cache, istate->cache_nr);
 
        si->nr_deletions = 0;
        si->nr_replacements = 0;
@@ -131,8 +150,12 @@ void merge_base_index(struct index_state *istate)
                remove_marked_cache_entries(istate);
 
        for (i = si->nr_replacements; i < si->saved_cache_nr; i++) {
+               if (!ce_namelen(si->saved_cache[i]))
+                       die("corrupt link extension, entry %d should "
+                           "have non-zero length name", i);
                add_index_entry(istate, si->saved_cache[i],
                                ADD_CACHE_OK_TO_ADD |
+                               ADD_CACHE_KEEP_CACHE_TREE |
                                /*
                                 * we may have to replay what
                                 * merge-recursive.c:update_stages()
@@ -144,10 +167,9 @@ void merge_base_index(struct index_state *istate)
 
        ewah_free(si->delete_bitmap);
        ewah_free(si->replace_bitmap);
-       free(si->saved_cache);
+       FREE_AND_NULL(si->saved_cache);
        si->delete_bitmap  = NULL;
        si->replace_bitmap = NULL;
-       si->saved_cache    = NULL;
        si->saved_cache_nr = 0;
 }
 
@@ -164,7 +186,7 @@ void prepare_to_write_split_index(struct index_state *istate)
                /* Go through istate->cache[] and mark CE_MATCHED to
                 * entry with positive index. We'll go through
                 * base->cache[] later to delete all entries in base
-                * that are not marked eith either CE_MATCHED or
+                * that are not marked with either CE_MATCHED or
                 * CE_UPDATE_IN_BASE. If istate->cache[i] is a
                 * duplicate, deduplicate it.
                 */
@@ -212,6 +234,7 @@ void prepare_to_write_split_index(struct index_state *istate)
                                ewah_set(si->delete_bitmap, i);
                        else if (ce->ce_flags & CE_UPDATE_IN_BASE) {
                                ewah_set(si->replace_bitmap, i);
+                               ce->ce_flags |= CE_STRIP_NAME;
                                ALLOC_GROW(entries, nr_entries+1, nr_alloc);
                                entries[nr_entries++] = ce;
                        }
@@ -221,6 +244,7 @@ void prepare_to_write_split_index(struct index_state *istate)
        for (i = 0; i < istate->cache_nr; i++) {
                ce = istate->cache[i];
                if ((!si->base || !ce->index) && !(ce->ce_flags & CE_REMOVE)) {
+                       assert(!(ce->ce_flags & CE_STRIP_NAME));
                        ALLOC_GROW(entries, nr_entries+1, nr_alloc);
                        entries[nr_entries++] = ce;
                }
@@ -292,3 +316,25 @@ void replace_index_entry_in_base(struct index_state *istate,
                istate->split_index->base->cache[new->index - 1] = new;
        }
 }
+
+void add_split_index(struct index_state *istate)
+{
+       if (!istate->split_index) {
+               init_split_index(istate);
+               istate->cache_changed |= SPLIT_INDEX_ORDERED;
+       }
+}
+
+void remove_split_index(struct index_state *istate)
+{
+       if (istate->split_index) {
+               /*
+                * can't discard_split_index(&the_index); because that
+                * will destroy split_index->base->cache[], which may
+                * be shared with the_index.cache[]. So yeah we're
+                * leaking a bit here.
+                */
+               istate->split_index = NULL;
+               istate->cache_changed |= SOMETHING_CHANGED;
+       }
+}