Commit 6843cebe17b7ce15eb9a6d1a88ac2e7e9c00d5b9

Carlos Martín Nieto 2014-07-10T14:10:39

index: fill the tree cache when reading from a tree When reading from a tree, we know what every tree is going to look like, so we can fill in the tree cache completely, making use of the index for modification of trees a lot quicker.

diff --git a/src/index.c b/src/index.c
index 8ea1fd4..be043a9 100644
--- a/src/index.c
+++ b/src/index.c
@@ -2277,6 +2277,7 @@ typedef struct read_tree_data {
 	git_vector *old_entries;
 	git_vector *new_entries;
 	git_vector_cmp entry_cmp;
+	git_tree_cache *tree;
 } read_tree_data;
 
 static int read_tree_cb(
@@ -2338,6 +2339,9 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
 	data.new_entries = &entries;
 	data.entry_cmp   = index->entries_search;
 
+	index->tree = NULL;
+	git_pool_clear(&index->tree_pool);
+
 	if (index_sort_if_needed(index, true) < 0)
 		return -1;
 
@@ -2358,6 +2362,10 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
 	}
 
 	git_vector_free(&entries);
+	if (error < 0)
+		return error;
+
+	error = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool);
 
 	return error;
 }
diff --git a/src/tree-cache.c b/src/tree-cache.c
index d76d928..86521ce 100644
--- a/src/tree-cache.c
+++ b/src/tree-cache.c
@@ -7,6 +7,7 @@
 
 #include "tree-cache.h"
 #include "pool.h"
+#include "tree.h"
 
 static git_tree_cache *find_child(
 	const git_tree_cache *tree, const char *path, const char *end)
@@ -155,6 +156,76 @@ int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer
 	return 0;
 }
 
+static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_pool *pool)
+{
+	git_repository *repo;
+	size_t i, j, nentries, ntrees;
+	int error;
+
+	repo = git_tree_owner(tree);
+
+	git_oid_cpy(&cache->oid, git_tree_id(tree));
+	nentries = git_tree_entrycount(tree);
+
+	/*
+	 * We make sure we know how many trees we need to allocate for
+	 * so we don't have to realloc and change the pointers for the
+	 * parents.
+	 */
+	ntrees = 0;
+	for (i = 0; i < nentries; i++) {
+		const git_tree_entry *entry;
+
+		entry = git_tree_entry_byindex(tree, i);
+		if (git_tree_entry_filemode(entry) == GIT_FILEMODE_TREE)
+			ntrees++;
+	}
+
+	cache->children_count = ntrees;
+	cache->children = git_pool_mallocz(pool, ntrees * sizeof(git_tree_cache *));
+	GITERR_CHECK_ALLOC(cache->children);
+
+	j = 0;
+	for (i = 0; i < nentries; i++) {
+		const git_tree_entry *entry;
+		git_tree *subtree;
+
+		entry = git_tree_entry_byindex(tree, i);
+		if (git_tree_entry_filemode(entry) != GIT_FILEMODE_TREE)
+			continue;
+
+		if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), cache, pool)) < 0)
+			return error;
+
+		if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0)
+			return error;
+
+		error = read_tree_recursive(cache->children[j], subtree, pool);
+		git_tree_free(subtree);
+		j++;
+
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool)
+{
+	int error;
+	git_tree_cache *cache;
+
+	if ((error = git_tree_cache_new(&cache, "", NULL, pool)) < 0)
+		return error;
+
+	if ((error = read_tree_recursive(cache, tree, pool)) < 0)
+		return error;
+
+	*out = cache;
+	return 0;
+}
+
 int git_tree_cache_new(git_tree_cache **out, const char *name, git_tree_cache *parent, git_pool *pool)
 {
 	size_t name_len;
diff --git a/src/tree-cache.h b/src/tree-cache.h
index d41a51f..d75049d 100644
--- a/src/tree-cache.h
+++ b/src/tree-cache.h
@@ -27,6 +27,10 @@ int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer
 void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path);
 const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path);
 int git_tree_cache_new(git_tree_cache **out, const char *name, git_tree_cache *parent, git_pool *pool);
+/**
+ * Read a tree as the root of the tree cache (like for `git read-tree`)
+ */
+int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool);
 void git_tree_cache_free(git_tree_cache *tree);
 
 #endif