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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
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