Commit 6ec37f723269da9060bdd5c192e81fdf59ac171b

Vicent Martí 2013-02-20T11:42:15

Merge pull request #1350 from arrbee/fix-1292 Add explicit entrycount to tree builder

diff --git a/src/tree.c b/src/tree.c
index 59bd92a..ec57e8b 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -216,12 +216,11 @@ git_tree_entry *git_tree_entry_dup(const git_tree_entry *entry)
 
 void git_tree__free(git_tree *tree)
 {
-	unsigned int i;
+	size_t i;
+	git_tree_entry *e;
 
-	for (i = 0; i < tree->entries.length; ++i) {
-		git_tree_entry *e = git_vector_get(&tree->entries, i);
+	git_vector_foreach(&tree->entries, i, e)
 		git_tree_entry_free(e);
-	}
 
 	git_vector_free(&tree->entries);
 	git__free(tree);
@@ -355,7 +354,7 @@ size_t git_tree_entrycount(const git_tree *tree)
 unsigned int git_treebuilder_entrycount(git_treebuilder *bld)
 {
 	assert(bld);
-	return (int)bld->entries.length;
+	return (unsigned int)bld->entrycount;
 }
 
 static int tree_error(const char *str, const char *path)
@@ -392,8 +391,10 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
 			entry = alloc_entry(buffer);
 			GITERR_CHECK_ALLOC(entry);
 
-			if (git_vector_insert(&tree->entries, entry) < 0)
+			if (git_vector_insert(&tree->entries, entry) < 0) {
+				git__free(entry);
 				return -1;
+			}
 
 			entry->attr = attr;
 		}
@@ -450,9 +451,12 @@ static int append_entry(
 	git_oid_cpy(&entry->oid, id);
 	entry->attr = (uint16_t)filemode;
 
-	if (git_vector_insert(&bld->entries, entry) < 0)
+	if (git_vector_insert(&bld->entries, entry) < 0) {
+		git__free(entry);
 		return -1;
+	}
 
+	bld->entrycount++;
 	return 0;
 }
 
@@ -581,11 +585,6 @@ int git_tree__write_index(
 	return ret < 0 ? ret : 0;
 }
 
-static void sort_entries(git_treebuilder *bld)
-{
-	git_vector_sort(&bld->entries);
-}
-
 int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
 {
 	git_treebuilder *bld;
@@ -603,9 +602,9 @@ int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
 		goto on_error;
 
 	if (source != NULL) {
-		for (i = 0; i < source->entries.length; ++i) {
-			git_tree_entry *entry_src = source->entries.contents[i];
+		git_tree_entry *entry_src;
 
+		git_vector_foreach(&source->entries, i, entry_src) {
 			if (append_entry(
 				bld, entry_src->filename,
 				&entry_src->oid,
@@ -642,14 +641,20 @@ int git_treebuilder_insert(
 
 	if (!tree_key_search(&pos, &bld->entries, filename, strlen(filename))) {
 		entry = git_vector_get(&bld->entries, pos);
-		if (entry->removed)
+		if (entry->removed) {
 			entry->removed = 0;
+			bld->entrycount++;
+		}
 	} else {
 		entry = alloc_entry(filename);
 		GITERR_CHECK_ALLOC(entry);
 
-		if (git_vector_insert(&bld->entries, entry) < 0)
+		if (git_vector_insert(&bld->entries, entry) < 0) {
+			git__free(entry);
 			return -1;
+		}
+
+		bld->entrycount++;
 	}
 
 	git_oid_cpy(&entry->oid, id);
@@ -691,24 +696,26 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
 		return tree_error("Failed to remove entry. File isn't in the tree", filename);
 
 	remove_ptr->removed = 1;
+	bld->entrycount--;
 	return 0;
 }
 
 int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
 {
-	unsigned int i;
+	int error = 0;
+	size_t i;
 	git_buf tree = GIT_BUF_INIT;
 	git_odb *odb;
 
 	assert(bld);
 
-	sort_entries(bld);
+	git_vector_sort(&bld->entries);
 
 	/* Grow the buffer beforehand to an estimated size */
-	git_buf_grow(&tree, bld->entries.length * 72);
+	error = git_buf_grow(&tree, bld->entries.length * 72);
 
-	for (i = 0; i < bld->entries.length; ++i) {
-		git_tree_entry *entry = bld->entries.contents[i];
+	for (i = 0; i < bld->entries.length && !error; ++i) {
+		git_tree_entry *entry = git_vector_get(&bld->entries, i);
 
 		if (entry->removed)
 			continue;
@@ -716,24 +723,17 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
 		git_buf_printf(&tree, "%o ", entry->attr);
 		git_buf_put(&tree, entry->filename, entry->filename_len + 1);
 		git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
-	}
-
-	if (git_buf_oom(&tree))
-		goto on_error;
-
-	if (git_repository_odb__weakptr(&odb, repo) < 0)
-		goto on_error;
-
 
-	if (git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE) < 0)
-		goto on_error;
+		if (git_buf_oom(&tree))
+			error = -1;
+	}
 
-	git_buf_free(&tree);
-	return 0;
+	if (!error &&
+		!(error = git_repository_odb__weakptr(&odb, repo)))
+		error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
 
-on_error:
 	git_buf_free(&tree);
-	return -1;
+	return error;
 }
 
 void git_treebuilder_filter(
@@ -741,28 +741,31 @@ void git_treebuilder_filter(
 	git_treebuilder_filter_cb filter,
 	void *payload)
 {
-	unsigned int i;
+	size_t i;
+	git_tree_entry *entry;
 
 	assert(bld && filter);
 
-	for (i = 0; i < bld->entries.length; ++i) {
-		git_tree_entry *entry = bld->entries.contents[i];
-		if (!entry->removed && filter(entry, payload))
+	git_vector_foreach(&bld->entries, i, entry) {
+		if (!entry->removed && filter(entry, payload)) {
 			entry->removed = 1;
+			bld->entrycount--;
+		}
 	}
 }
 
 void git_treebuilder_clear(git_treebuilder *bld)
 {
-	unsigned int i;
+	size_t i;
+	git_tree_entry *e;
+
 	assert(bld);
 
-	for (i = 0; i < bld->entries.length; ++i) {
-		git_tree_entry *e = bld->entries.contents[i];
+	git_vector_foreach(&bld->entries, i, e)
 		git_tree_entry_free(e);
-	}
 
 	git_vector_clear(&bld->entries);
+	bld->entrycount = 0;
 }
 
 void git_treebuilder_free(git_treebuilder *bld)
@@ -856,16 +859,17 @@ static int tree_walk(
 {
 	int error = 0;
 	size_t i;
+	const git_tree_entry *entry;
 
-	for (i = 0; i < tree->entries.length; ++i) {
-		const git_tree_entry *entry = tree->entries.contents[i];
-
+	git_vector_foreach(&tree->entries, i, entry) {
 		if (preorder) {
 			error = callback(path->ptr, entry, payload);
 			if (error > 0)
 				continue;
-			if (error < 0)
+			if (error < 0) {
+				giterr_clear();
 				return GIT_EUSER;
+			}
 		}
 
 		if (git_tree_entry__is_tree(entry)) {
@@ -892,6 +896,7 @@ static int tree_walk(
 		}
 
 		if (!preorder && callback(path->ptr, entry, payload) < 0) {
+			giterr_clear();
 			error = GIT_EUSER;
 			break;
 		}
@@ -909,20 +914,14 @@ int git_tree_walk(
 	int error = 0;
 	git_buf root_path = GIT_BUF_INIT;
 
-	switch (mode) {
-	case GIT_TREEWALK_POST:
-		error = tree_walk(tree, callback, &root_path, payload, false);
-		break;
-
-	case GIT_TREEWALK_PRE:
-		error = tree_walk(tree, callback, &root_path, payload, true);
-		break;
-
-	default:
+	if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) {
 		giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
 		return -1;
 	}
 
+	error = tree_walk(
+		tree, callback, &root_path, payload, (mode == GIT_TREEWALK_PRE));
+
 	git_buf_free(&root_path);
 
 	return error;
diff --git a/src/tree.h b/src/tree.h
index 27afd4f..567b584 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -27,6 +27,7 @@ struct git_tree {
 
 struct git_treebuilder {
 	git_vector entries;
+	size_t entrycount; /* vector may contain "removed" entries */
 };
 
 GIT_INLINE(int) git_tree__dup(git_tree **dest, git_tree *source)