]> git.proxmox.com Git - libgit2.git/blame - src/tree-cache.c
Merge pull request #3303 from libgit2/cmn/index-add-submodule
[libgit2.git] / src / tree-cache.c
CommitLineData
b4171320 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
b4171320
CMN
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8#include "tree-cache.h"
19c88310 9#include "pool.h"
6843cebe 10#include "tree.h"
b4171320 11
1fa17b5c
RB
12static git_tree_cache *find_child(
13 const git_tree_cache *tree, const char *path, const char *end)
69bffab9 14{
1fa17b5c 15 size_t i, dirlen = end ? (size_t)(end - path) : strlen(path);
69bffab9
CMN
16
17 for (i = 0; i < tree->children_count; ++i) {
1fa17b5c 18 git_tree_cache *child = tree->children[i];
69bffab9 19
1fa17b5c
RB
20 if (child->namelen == dirlen && !memcmp(path, child->name, dirlen))
21 return child;
69bffab9
CMN
22 }
23
24 return NULL;
25}
26
27void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path)
28{
29 const char *ptr = path, *end;
30
31 if (tree == NULL)
32 return;
33
c2f8b215 34 tree->entry_count = -1;
69bffab9
CMN
35
36 while (ptr != NULL) {
37 end = strchr(ptr, '/');
38
39 if (end == NULL) /* End of path */
40 break;
41
1fa17b5c 42 tree = find_child(tree, ptr, end);
69bffab9
CMN
43 if (tree == NULL) /* We don't have that tree */
44 return;
45
c2f8b215 46 tree->entry_count = -1;
69bffab9
CMN
47 ptr = end + 1;
48 }
49}
50
3ba69ba8
CMN
51const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path)
52{
53 const char *ptr = path, *end;
54
55 if (tree == NULL) {
56 return NULL;
57 }
58
59 while (1) {
60 end = strchr(ptr, '/');
61
1fa17b5c
RB
62 tree = find_child(tree, ptr, end);
63 if (tree == NULL) /* Can't find it */
3ba69ba8 64 return NULL;
3ba69ba8 65
3f035860 66 if (end == NULL || *end + 1 == '\0')
3ba69ba8
CMN
67 return tree;
68
69 ptr = end + 1;
70 }
71}
72
b4171320 73static int read_tree_internal(git_tree_cache **out,
19c88310 74 const char **buffer_in, const char *buffer_end,
46bb0067 75 git_pool *pool)
b4171320 76{
69bffab9 77 git_tree_cache *tree = NULL;
b4171320
CMN
78 const char *name_start, *buffer;
79 int count;
b4171320
CMN
80
81 buffer = name_start = *buffer_in;
82
3fbcac89
VM
83 if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
84 goto corrupted;
b4171320 85
3fbcac89
VM
86 if (++buffer >= buffer_end)
87 goto corrupted;
b4171320 88
46bb0067 89 if (git_tree_cache_new(&tree, name_start, pool) < 0)
d091a9db 90 return -1;
b183ffe7 91
b4171320 92 /* Blank-terminated ASCII decimal number of entries in this tree */
08f3d6ca 93 if (git__strtol32(&count, buffer, &buffer, 10) < 0)
3fbcac89 94 goto corrupted;
b4171320 95
c2f8b215 96 tree->entry_count = count;
b4171320 97
3fbcac89
VM
98 if (*buffer != ' ' || ++buffer >= buffer_end)
99 goto corrupted;
b4171320
CMN
100
101 /* Number of children of the tree, newline-terminated */
3fbcac89
VM
102 if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < 0)
103 goto corrupted;
b4171320
CMN
104
105 tree->children_count = count;
106
3fbcac89
VM
107 if (*buffer != '\n' || ++buffer > buffer_end)
108 goto corrupted;
b4171320 109
acd31b4a 110 /* The SHA1 is only there if it's not invalidated */
c2f8b215 111 if (tree->entry_count >= 0) {
acd31b4a 112 /* 160-bit SHA-1 for this tree and it's children */
3fbcac89
VM
113 if (buffer + GIT_OID_RAWSZ > buffer_end)
114 goto corrupted;
b4171320 115
acd31b4a
CMN
116 git_oid_fromraw(&tree->oid, (const unsigned char *)buffer);
117 buffer += GIT_OID_RAWSZ;
118 }
b4171320
CMN
119
120 /* Parse children: */
121 if (tree->children_count > 0) {
122 unsigned int i;
b4171320 123
19c88310 124 tree->children = git_pool_malloc(pool, tree->children_count * sizeof(git_tree_cache *));
3fbcac89 125 GITERR_CHECK_ALLOC(tree->children);
b4171320 126
82e6a42c 127 memset(tree->children, 0x0, tree->children_count * sizeof(git_tree_cache *));
128
b4171320 129 for (i = 0; i < tree->children_count; ++i) {
46bb0067 130 if (read_tree_internal(&tree->children[i], &buffer, buffer_end, pool) < 0)
7b69289f 131 goto corrupted;
b4171320
CMN
132 }
133 }
134
135 *buffer_in = buffer;
136 *out = tree;
3fbcac89 137 return 0;
b4171320 138
3fbcac89 139 corrupted:
a5d73188 140 giterr_set(GITERR_INDEX, "Corrupted TREE extension in index");
3fbcac89 141 return -1;
b4171320
CMN
142}
143
19c88310 144int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool)
b4171320
CMN
145{
146 const char *buffer_end = buffer + buffer_size;
b4171320 147
46bb0067 148 if (read_tree_internal(tree, &buffer, buffer_end, pool) < 0)
3fbcac89 149 return -1;
b4171320 150
3fbcac89 151 if (buffer < buffer_end) {
a5d73188 152 giterr_set(GITERR_INDEX, "Corrupted TREE extension in index (unexpected trailing data)");
3fbcac89
VM
153 return -1;
154 }
b4171320 155
3fbcac89 156 return 0;
b4171320
CMN
157}
158
6843cebe
CMN
159static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_pool *pool)
160{
161 git_repository *repo;
162 size_t i, j, nentries, ntrees;
163 int error;
164
165 repo = git_tree_owner(tree);
166
167 git_oid_cpy(&cache->oid, git_tree_id(tree));
168 nentries = git_tree_entrycount(tree);
169
170 /*
171 * We make sure we know how many trees we need to allocate for
172 * so we don't have to realloc and change the pointers for the
173 * parents.
174 */
175 ntrees = 0;
176 for (i = 0; i < nentries; i++) {
177 const git_tree_entry *entry;
178
179 entry = git_tree_entry_byindex(tree, i);
180 if (git_tree_entry_filemode(entry) == GIT_FILEMODE_TREE)
181 ntrees++;
182 }
183
184 cache->children_count = ntrees;
185 cache->children = git_pool_mallocz(pool, ntrees * sizeof(git_tree_cache *));
186 GITERR_CHECK_ALLOC(cache->children);
187
188 j = 0;
189 for (i = 0; i < nentries; i++) {
190 const git_tree_entry *entry;
191 git_tree *subtree;
192
193 entry = git_tree_entry_byindex(tree, i);
bb0757d5
CMN
194 if (git_tree_entry_filemode(entry) != GIT_FILEMODE_TREE) {
195 cache->entry_count++;
6843cebe 196 continue;
bb0757d5 197 }
6843cebe 198
46bb0067 199 if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), pool)) < 0)
6843cebe
CMN
200 return error;
201
202 if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0)
203 return error;
204
205 error = read_tree_recursive(cache->children[j], subtree, pool);
206 git_tree_free(subtree);
bb0757d5 207 cache->entry_count += cache->children[j]->entry_count;
6843cebe
CMN
208 j++;
209
210 if (error < 0)
211 return error;
212 }
213
214 return 0;
215}
216
217int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool)
218{
219 int error;
220 git_tree_cache *cache;
221
46bb0067 222 if ((error = git_tree_cache_new(&cache, "", pool)) < 0)
6843cebe
CMN
223 return error;
224
225 if ((error = read_tree_recursive(cache, tree, pool)) < 0)
226 return error;
227
228 *out = cache;
229 return 0;
230}
231
46bb0067 232int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool)
d091a9db
CMN
233{
234 size_t name_len;
235 git_tree_cache *tree;
236
237 name_len = strlen(name);
19c88310 238 tree = git_pool_malloc(pool, sizeof(git_tree_cache) + name_len + 1);
d091a9db
CMN
239 GITERR_CHECK_ALLOC(tree);
240
241 memset(tree, 0x0, sizeof(git_tree_cache));
d091a9db
CMN
242 /* NUL-terminated tree name */
243 tree->namelen = name_len;
244 memcpy(tree->name, name, name_len);
245 tree->name[name_len] = '\0';
246
247 *out = tree;
248 return 0;
249}
c2f8b215 250
c2f8b215
CMN
251static void write_tree(git_buf *out, git_tree_cache *tree)
252{
253 size_t i;
254
cf1013a8 255 git_buf_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count);
c2f8b215 256
795d8e93
CMN
257 if (tree->entry_count != -1)
258 git_buf_put(out, (const char *) &tree->oid, GIT_OID_RAWSZ);
c2f8b215
CMN
259
260 for (i = 0; i < tree->children_count; i++)
261 write_tree(out, tree->children[i]);
262}
263
264int git_tree_cache_write(git_buf *out, git_tree_cache *tree)
265{
c2f8b215
CMN
266 write_tree(out, tree);
267
268 return git_buf_oom(out) ? -1 : 0;
269}