Commit 54edbb9871ad543baefc62310512719fc70539be

Russell Belfer 2014-02-07T16:48:27

Add index snapshot and use it for iterator

diff --git a/src/index.c b/src/index.c
index 8bc5cb1..58b359b 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1056,21 +1056,26 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage)
 	return error;
 }
 
-int git_index__find(
-	size_t *out, git_index *index, const char *path, size_t path_len, int stage)
+int git_index__find_in_entries(
+	size_t *out, git_vector *entries, git_vector_cmp entry_cmp,
+	const char *path, size_t path_len, int stage)
 {
 	struct entry_srch_key srch_key;
-
-	assert(path);
-
-	git_vector_sort(&index->entries);
-
 	srch_key.path = path;
 	srch_key.path_len = !path_len ? strlen(path) : path_len;
 	srch_key.stage = stage;
+	return git_vector_bsearch2(out, entries, entry_cmp, &srch_key);
+}
 
-	return git_vector_bsearch2(
-		out, &index->entries, index->entries_search, &srch_key);
+int git_index__find(
+	size_t *out, git_index *index, const char *path, size_t path_len, int stage)
+{
+	assert(index && path);
+
+	git_vector_sort(&index->entries);
+
+	return git_index__find_in_entries(
+		out, &index->entries, index->entries_search, path, path_len, stage);
 }
 
 int git_index_find(size_t *at_pos, git_index *index, const char *path)
@@ -2442,3 +2447,22 @@ int git_index_update_all(
 
 	return error;
 }
+
+int git_index__snapshot(git_vector *entries, git_index *index)
+{
+	int error;
+
+	GIT_REFCOUNT_INC(index);
+	git_vector_sort(&index->entries);
+	error = git_vector_dup(entries, &index->entries, index->entries._cmp);
+
+	if (error < 0)
+		git_index_free(index);
+
+	return error;
+}
+
+void git_index__release_snapshot(git_index *index)
+{
+	git_index_free(index);
+}
diff --git a/src/index.h b/src/index.h
index cabdbca..2b6f5c9 100644
--- a/src/index.h
+++ b/src/index.h
@@ -71,4 +71,16 @@ GIT_INLINE(const git_futils_filestamp *) git_index__filestamp(git_index *index)
 
 extern int git_index__changed_relative_to(git_index *index, const git_futils_filestamp *fs);
 
+/* Copy the current entries vector *and* increment the index refcount.
+ * Call `git_index__release_snapshot` when done.
+ */
+extern int git_index__snapshot(git_vector *entries, git_index *index);
+extern void git_index__release_snapshot(git_index *index);
+
+/* Allow searching in a snapshot; entries must already be sorted! */
+extern int git_index__find_in_entries(
+	size_t *at_pos,
+	git_vector *entries, git_vector_cmp entry_cmp,
+	const char *path, size_t path_len, int stage);
+
 #endif
diff --git a/src/iterator.c b/src/iterator.c
index 4565525..a84f4d3 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -644,6 +644,8 @@ typedef struct {
 	git_iterator base;
 	git_iterator_callbacks cb;
 	git_index *index;
+	git_vector entries;
+	git_vector_cmp entry_srch;
 	size_t current;
 	/* when not in autoexpand mode, use these to represent "tree" state */
 	git_buf partial;
@@ -654,10 +656,10 @@ typedef struct {
 
 static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
 {
-	const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
+	const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
 
 	if (ie != NULL && iterator__past_end(ii, ie->path)) {
-		ii->current = git_index_entrycount(ii->index);
+		ii->current = git_vector_length(&ii->entries);
 		ie = NULL;
 	}
 
@@ -726,7 +728,7 @@ static int index_iterator__current(
 	const git_index_entry **entry, git_iterator *self)
 {
 	index_iterator *ii = (index_iterator *)self;
-	const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
+	const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
 
 	if (ie != NULL && index_iterator__at_tree(ii)) {
 		ii->tree_entry.path = ii->partial.ptr;
@@ -744,14 +746,14 @@ static int index_iterator__current(
 static int index_iterator__at_end(git_iterator *self)
 {
 	index_iterator *ii = (index_iterator *)self;
-	return (ii->current >= git_index_entrycount(ii->index));
+	return (ii->current >= git_vector_length(&ii->entries));
 }
 
 static int index_iterator__advance(
 	const git_index_entry **entry, git_iterator *self)
 {
 	index_iterator *ii = (index_iterator *)self;
-	size_t entrycount = git_index_entrycount(ii->index);
+	size_t entrycount = git_vector_length(&ii->entries);
 	const git_index_entry *ie;
 
 	if (!iterator__has_been_accessed(ii))
@@ -766,7 +768,7 @@ static int index_iterator__advance(
 			while (ii->current < entrycount) {
 				ii->current++;
 
-				if (!(ie = git_index_get_byindex(ii->index, ii->current)) ||
+				if (!(ie = git_vector_get(&ii->entries, ii->current)) ||
 					ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
 					break;
 			}
@@ -789,7 +791,7 @@ static int index_iterator__advance_into(
 	const git_index_entry **entry, git_iterator *self)
 {
 	index_iterator *ii = (index_iterator *)self;
-	const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
+	const git_index_entry *ie = git_vector_get(&ii->entries, ii->current);
 
 	if (ie != NULL && index_iterator__at_tree(ii)) {
 		if (ii->restore_terminator)
@@ -818,7 +820,8 @@ static int index_iterator__reset(
 	ii->current = 0;
 
 	if (ii->base.start)
-		git_index__find(&ii->current, ii->index, ii->base.start, 0, 0);
+		git_index__find_in_entries(
+			&ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0);
 
 	if ((ie = index_iterator__skip_conflicts(ii)) == NULL)
 		return 0;
@@ -843,9 +846,9 @@ static int index_iterator__reset(
 static void index_iterator__free(git_iterator *self)
 {
 	index_iterator *ii = (index_iterator *)self;
-	git_index_free(ii->index);
+	git_index__release_snapshot(ii->index);
 	ii->index = NULL;
-
+	git_vector_free(&ii->entries);
 	git_buf_free(&ii->partial);
 }
 
@@ -856,9 +859,17 @@ int git_iterator_for_index(
 	const char *start,
 	const char *end)
 {
+	int error = 0;
 	index_iterator *ii = git__calloc(1, sizeof(index_iterator));
 	GITERR_CHECK_ALLOC(ii);
 
+	if ((error = git_index__snapshot(&ii->entries, index)) < 0) {
+		git__free(ii);
+		return error;
+	}
+	ii->index = index;
+	ii->entry_srch = index->entries_search;
+
 	ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
 
 	if (index->ignore_case) {
@@ -866,8 +877,7 @@ int git_iterator_for_index(
 		ii->base.prefixcomp = git__prefixcmp_icase;
 	}
 
-	ii->index = index;
-	GIT_REFCOUNT_INC(index);
+	/* TODO: resort entries to match desired ignore_case behavior */
 
 	git_buf_init(&ii->partial, 0);
 	ii->tree_entry.mode = GIT_FILEMODE_TREE;
@@ -875,7 +885,6 @@ int git_iterator_for_index(
 	index_iterator__reset((git_iterator *)ii, NULL, NULL);
 
 	*iter = (git_iterator *)ii;
-
 	return 0;
 }