diff --git a/cache.h b/cache.h index 1bf2d4bde6..770d8bc950 100644 --- a/cache.h +++ b/cache.h @@ -445,6 +445,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name); extern int remove_index_entry_at(struct index_state *, int pos); +extern void remove_marked_cache_entries(struct index_state *istate); extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_VERBOSE 1 #define ADD_CACHE_PRETEND 2 diff --git a/read-cache.c b/read-cache.c index 940ec76fdf..59a274b464 100644 --- a/read-cache.c +++ b/read-cache.c @@ -443,6 +443,26 @@ int remove_index_entry_at(struct index_state *istate, int pos) return 1; } +/* + * Remove all cache ententries marked for removal, that is where + * CE_REMOVE is set in ce_flags. This is much more effective than + * calling remove_index_entry_at() for each entry to be removed. + */ +void remove_marked_cache_entries(struct index_state *istate) +{ + struct cache_entry **ce_array = istate->cache; + unsigned int i, j; + + for (i = j = 0; i < istate->cache_nr; i++) { + if (ce_array[i]->ce_flags & CE_REMOVE) + remove_name_hash(ce_array[i]); + else + ce_array[j++] = ce_array[i]; + } + istate->cache_changed = 1; + istate->cache_nr = j; +} + int remove_file_from_index(struct index_state *istate, const char *path) { int pos = index_name_pos(istate, path, strlen(path)); diff --git a/unpack-trees.c b/unpack-trees.c index e3c3fa12aa..273b5da0a2 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -93,11 +93,9 @@ static int check_updates(struct unpack_trees_options *o) display_progress(progress, ++cnt); if (o->update) unlink_entry(ce); - remove_index_entry_at(&o->result, i); - i--; - continue; } } + remove_marked_cache_entries(&o->result); remove_scheduled_dirs(); for (i = 0; i < index->cache_nr; i++) {