]>
Commit | Line | Data |
---|---|---|
225fe215 | 1 | /* |
bb742ede | 2 | * Copyright (C) 2009-2011 the libgit2 contributors |
225fe215 | 3 | * |
bb742ede VM |
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. | |
225fe215 VM |
6 | */ |
7 | ||
8 | #include "common.h" | |
9 | #include "commit.h" | |
225fe215 | 10 | #include "tree.h" |
44908fe7 VM |
11 | #include "git2/repository.h" |
12 | #include "git2/object.h" | |
225fe215 | 13 | |
c8f5ff8f | 14 | #define DEFAULT_TREE_SIZE 16 |
9de27ad0 SJ |
15 | #define MAX_FILEMODE 0777777 |
16 | #define MAX_FILEMODE_BYTES 6 | |
c8f5ff8f | 17 | |
0ad6efa1 | 18 | static int valid_attributes(const int attributes) { |
932d1baf | 19 | return attributes >= 0 && attributes <= MAX_FILEMODE; |
0ad6efa1 VM |
20 | } |
21 | ||
761aa2aa VM |
22 | struct tree_key_search { |
23 | const char *filename; | |
24 | size_t filename_len; | |
25 | }; | |
26 | ||
d568d585 | 27 | static int entry_search_cmp(const void *key, const void *array_member) |
2a884588 | 28 | { |
0cbbdc26 KS |
29 | const struct tree_key_search *ksearch = key; |
30 | const git_tree_entry *entry = array_member; | |
2a884588 | 31 | |
e6629d83 VM |
32 | int result = |
33 | git_futils_cmp_path( | |
34 | ksearch->filename, ksearch->filename_len, entry->attr & 040000, | |
35 | entry->filename, entry->filename_len, entry->attr & 040000); | |
36 | ||
37 | return result ? result : ((int)ksearch->filename_len - (int)entry->filename_len); | |
9de27ad0 SJ |
38 | } |
39 | ||
d568d585 | 40 | static int entry_sort_cmp(const void *a, const void *b) |
2a884588 | 41 | { |
de18f276 VM |
42 | const git_tree_entry *entry_a = (const git_tree_entry *)(a); |
43 | const git_tree_entry *entry_b = (const git_tree_entry *)(b); | |
2a884588 | 44 | |
761aa2aa VM |
45 | return git_futils_cmp_path( |
46 | entry_a->filename, entry_a->filename_len, entry_a->attr & 040000, | |
47 | entry_b->filename, entry_b->filename_len, entry_b->attr & 040000); | |
2a884588 VM |
48 | } |
49 | ||
e6629d83 VM |
50 | static int build_ksearch(struct tree_key_search *ksearch, const char *path) |
51 | { | |
52 | size_t len = strlen(path); | |
53 | ||
54 | if (len && path[len - 1] == '/') | |
55 | len--; | |
56 | ||
57 | if (len == 0 || memchr(path, '/', len) != NULL) | |
58 | return GIT_ERROR; | |
59 | ||
60 | ksearch->filename = path; | |
61 | ksearch->filename_len = len; | |
62 | ||
63 | return GIT_SUCCESS; | |
64 | } | |
65 | ||
72a3fe42 | 66 | void git_tree__free(git_tree *tree) |
225fe215 | 67 | { |
c4034e63 | 68 | unsigned int i; |
003c2690 | 69 | |
c4034e63 VM |
70 | for (i = 0; i < tree->entries.length; ++i) { |
71 | git_tree_entry *e; | |
72 | e = git_vector_get(&tree->entries, i); | |
73 | ||
74 | free(e->filename); | |
75 | free(e); | |
58519018 | 76 | } |
003c2690 | 77 | |
c8f5ff8f | 78 | git_vector_free(&tree->entries); |
225fe215 VM |
79 | free(tree); |
80 | } | |
81 | ||
d45b4a9a VM |
82 | const git_oid *git_tree_id(git_tree *c) |
83 | { | |
84 | return git_object_id((git_object *)c); | |
225fe215 VM |
85 | } |
86 | ||
0ad6efa1 | 87 | unsigned int git_tree_entry_attributes(const git_tree_entry *entry) |
003c2690 | 88 | { |
2a884588 | 89 | return entry->attr; |
003c2690 VM |
90 | } |
91 | ||
0ad6efa1 | 92 | const char *git_tree_entry_name(const git_tree_entry *entry) |
003c2690 | 93 | { |
c4b5bedc | 94 | assert(entry); |
2a884588 VM |
95 | return entry->filename; |
96 | } | |
003c2690 | 97 | |
0ad6efa1 | 98 | const git_oid *git_tree_entry_id(const git_tree_entry *entry) |
2a884588 | 99 | { |
c4b5bedc | 100 | assert(entry); |
2a884588 | 101 | return &entry->oid; |
003c2690 VM |
102 | } |
103 | ||
ff9a4c13 RG |
104 | git_otype git_tree_entry_type(const git_tree_entry *entry) |
105 | { | |
106 | assert(entry); | |
107 | ||
108 | if (S_ISGITLINK(entry->attr)) | |
109 | return GIT_OBJ_COMMIT; | |
110 | else if (S_ISDIR(entry->attr)) | |
111 | return GIT_OBJ_TREE; | |
112 | else | |
113 | return GIT_OBJ_BLOB; | |
114 | } | |
115 | ||
0ad6efa1 | 116 | int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry) |
003c2690 | 117 | { |
1795f879 | 118 | assert(entry && object_out); |
72a3fe42 | 119 | return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY); |
122c3405 VM |
120 | } |
121 | ||
0ad6efa1 | 122 | const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) |
2a884588 | 123 | { |
c4034e63 | 124 | int idx; |
761aa2aa | 125 | struct tree_key_search ksearch; |
c4b5bedc VM |
126 | |
127 | assert(tree && filename); | |
128 | ||
e6629d83 VM |
129 | if (build_ksearch(&ksearch, filename) < GIT_SUCCESS) |
130 | return NULL; | |
761aa2aa VM |
131 | |
132 | idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, &ksearch); | |
c4034e63 VM |
133 | if (idx == GIT_ENOTFOUND) |
134 | return NULL; | |
135 | ||
136 | return git_vector_get(&tree->entries, idx); | |
2a884588 VM |
137 | } |
138 | ||
e5c80097 | 139 | const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx) |
003c2690 | 140 | { |
c4b5bedc | 141 | assert(tree); |
e5c80097 | 142 | return git_vector_get(&tree->entries, idx); |
003c2690 VM |
143 | } |
144 | ||
e5c80097 | 145 | unsigned int git_tree_entrycount(git_tree *tree) |
003c2690 | 146 | { |
c4b5bedc | 147 | assert(tree); |
c4034e63 | 148 | return tree->entries.length; |
003c2690 VM |
149 | } |
150 | ||
720d5472 | 151 | static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end) |
d8603ed9 | 152 | { |
6f02c3ba | 153 | int error = GIT_SUCCESS; |
2a884588 | 154 | |
72a3fe42 VM |
155 | if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) |
156 | return GIT_ENOMEM; | |
e4def81a | 157 | |
003c2690 VM |
158 | while (buffer < buffer_end) { |
159 | git_tree_entry *entry; | |
ad196c6a | 160 | int tmp; |
d8603ed9 | 161 | |
29e1789b | 162 | entry = git__calloc(1, sizeof(git_tree_entry)); |
2a884588 VM |
163 | if (entry == NULL) { |
164 | error = GIT_ENOMEM; | |
165 | break; | |
003c2690 | 166 | } |
d8603ed9 | 167 | |
6f02c3ba | 168 | if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS) |
c4034e63 | 169 | return GIT_ENOMEM; |
003c2690 | 170 | |
0b2c4061 KS |
171 | if (git__strtol32(&tmp, buffer, &buffer, 8) < GIT_SUCCESS || |
172 | !buffer || tmp > UINT_MAX || tmp < 0) | |
d6de92b6 | 173 | return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Can't parse attributes"); |
0b2c4061 | 174 | entry->attr = tmp; |
d8603ed9 VM |
175 | |
176 | if (*buffer++ != ' ') { | |
d6de92b6 | 177 | error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted"); |
d8603ed9 VM |
178 | break; |
179 | } | |
180 | ||
58519018 | 181 | if (memchr(buffer, 0, buffer_end - buffer) == NULL) { |
d6de92b6 | 182 | error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted"); |
58519018 VM |
183 | break; |
184 | } | |
185 | ||
186 | entry->filename = git__strdup(buffer); | |
0ad6efa1 | 187 | entry->filename_len = strlen(buffer); |
d8603ed9 | 188 | |
2a884588 VM |
189 | while (buffer < buffer_end && *buffer != 0) |
190 | buffer++; | |
003c2690 | 191 | |
2a884588 | 192 | buffer++; |
d8603ed9 | 193 | |
fa48608e | 194 | git_oid_fromraw(&entry->oid, (const unsigned char *)buffer); |
d8603ed9 | 195 | buffer += GIT_OID_RAWSZ; |
d8603ed9 VM |
196 | } |
197 | ||
bc06a4ee | 198 | return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse buffer"); |
d8603ed9 | 199 | } |
58519018 | 200 | |
72a3fe42 | 201 | int git_tree__parse(git_tree *tree, git_odb_object *obj) |
58519018 | 202 | { |
72a3fe42 VM |
203 | assert(tree); |
204 | return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); | |
58519018 VM |
205 | } |
206 | ||
8255c69b CMN |
207 | static unsigned int find_next_dir(const char *dirname, git_index *index, unsigned int start) |
208 | { | |
209 | unsigned int i, entries = git_index_entrycount(index); | |
210 | size_t dirlen; | |
211 | ||
212 | dirlen = strlen(dirname); | |
213 | for (i = start; i < entries; ++i) { | |
214 | git_index_entry *entry = git_index_get(index, i); | |
215 | if (strlen(entry->path) < dirlen || | |
216 | memcmp(entry->path, dirname, dirlen) || | |
217 | (dirlen > 0 && entry->path[dirlen] != '/')) { | |
218 | break; | |
219 | } | |
220 | } | |
221 | ||
222 | return i; | |
223 | } | |
224 | ||
9ef9e8c3 VM |
225 | static int append_entry(git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes) |
226 | { | |
227 | git_tree_entry *entry; | |
228 | ||
229 | if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL) | |
230 | return GIT_ENOMEM; | |
231 | ||
232 | memset(entry, 0x0, sizeof(git_tree_entry)); | |
233 | entry->filename = git__strdup(filename); | |
234 | entry->filename_len = strlen(entry->filename); | |
235 | ||
236 | bld->entry_count++; | |
237 | ||
238 | git_oid_cpy(&entry->oid, id); | |
239 | entry->attr = attributes; | |
240 | ||
241 | if (git_vector_insert(&bld->entries, entry) < 0) | |
242 | return GIT_ENOMEM; | |
243 | ||
244 | return GIT_SUCCESS; | |
245 | } | |
246 | ||
4a619797 | 247 | static int write_tree(git_oid *oid, git_index *index, const char *dirname, unsigned int start) |
47d8ec56 | 248 | { |
4a619797 CMN |
249 | git_treebuilder *bld = NULL; |
250 | unsigned int i, entries = git_index_entrycount(index); | |
251 | int error; | |
252 | size_t dirname_len = strlen(dirname); | |
8255c69b CMN |
253 | const git_tree_cache *cache; |
254 | ||
255 | cache = git_tree_cache_get(index->tree, dirname); | |
256 | if (cache != NULL && cache->entries >= 0){ | |
257 | git_oid_cpy(oid, &cache->oid); | |
258 | return find_next_dir(dirname, index, start); | |
259 | } | |
29e1789b | 260 | |
4a619797 CMN |
261 | error = git_treebuilder_create(&bld, NULL); |
262 | if (bld == NULL) { | |
47d8ec56 | 263 | return GIT_ENOMEM; |
4a619797 | 264 | } |
932d1baf | 265 | |
4a619797 CMN |
266 | /* |
267 | * This loop is unfortunate, but necessary. The index doesn't have | |
268 | * any directores, so we need to handle that manually, and we | |
269 | * need to keep track of the current position. | |
270 | */ | |
271 | for (i = start; i < entries; ++i) { | |
272 | git_index_entry *entry = git_index_get(index, i); | |
273 | char *filename, *next_slash; | |
274 | ||
275 | /* | |
276 | * If we've left our (sub)tree, exit the loop and return. The | |
277 | * first check is an early out (and security for the | |
278 | * third). The second check is a simple prefix comparison. The | |
279 | * third check catches situations where there is a directory | |
280 | * win32/sys and a file win32mmap.c. Without it, the following | |
281 | * code believes there is a file win32/mmap.c | |
282 | */ | |
283 | if (strlen(entry->path) < dirname_len || | |
284 | memcmp(entry->path, dirname, dirname_len) || | |
285 | (dirname_len > 0 && entry->path[dirname_len] != '/')) { | |
47d8ec56 | 286 | break; |
4a619797 | 287 | } |
932d1baf | 288 | |
4a619797 CMN |
289 | filename = entry->path + dirname_len; |
290 | if (*filename == '/') | |
291 | filename++; | |
292 | next_slash = strchr(filename, '/'); | |
293 | if (next_slash) { | |
294 | git_oid sub_oid; | |
295 | int written; | |
296 | char *subdir, *last_comp; | |
297 | ||
298 | subdir = git__strndup(entry->path, next_slash - entry->path); | |
299 | if (subdir == NULL) { | |
300 | error = GIT_ENOMEM; | |
301 | goto cleanup; | |
29e1789b | 302 | } |
29e1789b | 303 | |
4a619797 CMN |
304 | /* Write out the subtree */ |
305 | written = write_tree(&sub_oid, index, subdir, i); | |
306 | if (written < 0) { | |
307 | error = git__rethrow(written, "Failed to write subtree %s", subdir); | |
308 | } else { | |
309 | i = written - 1; /* -1 because of the loop increment */ | |
29e1789b | 310 | } |
932d1baf | 311 | |
4a619797 CMN |
312 | /* |
313 | * We need to figure out what we want toinsert | |
314 | * into this tree. If we're traversing | |
315 | * deps/zlib/, then we only want to write | |
316 | * 'zlib' into the tree. | |
317 | */ | |
318 | last_comp = strrchr(subdir, '/'); | |
319 | if (last_comp) { | |
320 | last_comp++; /* Get rid of the '/' */ | |
321 | } else { | |
322 | last_comp = subdir; | |
323 | } | |
9ef9e8c3 | 324 | error = append_entry(bld, last_comp, &sub_oid, S_IFDIR); |
4a619797 CMN |
325 | free(subdir); |
326 | if (error < GIT_SUCCESS) { | |
327 | error = git__rethrow(error, "Failed to insert dir"); | |
328 | goto cleanup; | |
329 | } | |
330 | } else { | |
9ef9e8c3 | 331 | error = append_entry(bld, filename, &entry->oid, entry->mode); |
4a619797 CMN |
332 | if (error < GIT_SUCCESS) { |
333 | error = git__rethrow(error, "Failed to insert file"); | |
334 | } | |
47d8ec56 | 335 | } |
29e1789b | 336 | } |
932d1baf | 337 | |
4a619797 CMN |
338 | error = git_treebuilder_write(oid, index->repository, bld); |
339 | if (error < GIT_SUCCESS) | |
340 | error = git__rethrow(error, "Failed to write tree to db"); | |
29e1789b | 341 | |
4a619797 CMN |
342 | cleanup: |
343 | git_treebuilder_free(bld); | |
344 | ||
345 | if (error < GIT_SUCCESS) | |
346 | return error; | |
347 | else | |
348 | return i; | |
29e1789b VM |
349 | } |
350 | ||
351 | int git_tree_create_fromindex(git_oid *oid, git_index *index) | |
352 | { | |
353 | int error; | |
354 | ||
355 | if (index->repository == NULL) | |
d6de92b6 | 356 | return git__throw(GIT_EBAREINDEX, "Failed to create tree. The index file is not backed up by an existing repository"); |
47d8ec56 | 357 | |
8255c69b CMN |
358 | if (index->tree != NULL && index->tree->entries >= 0) { |
359 | git_oid_cpy(oid, &index->tree->oid); | |
360 | return GIT_SUCCESS; | |
361 | } | |
362 | ||
4a619797 CMN |
363 | /* The tree cache didn't help us */ |
364 | error = write_tree(oid, index, "", 0); | |
bc06a4ee | 365 | return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS; |
47d8ec56 | 366 | } |
0ad6efa1 VM |
367 | |
368 | static void sort_entries(git_treebuilder *bld) | |
369 | { | |
370 | git_vector_sort(&bld->entries); | |
371 | } | |
372 | ||
373 | int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) | |
374 | { | |
375 | git_treebuilder *bld; | |
c5d8745f | 376 | unsigned int i, source_entries = DEFAULT_TREE_SIZE; |
0ad6efa1 VM |
377 | |
378 | assert(builder_p); | |
379 | ||
380 | bld = git__calloc(1, sizeof(git_treebuilder)); | |
381 | if (bld == NULL) | |
382 | return GIT_ENOMEM; | |
383 | ||
384 | if (source != NULL) | |
385 | source_entries = source->entries.length; | |
386 | ||
387 | if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < GIT_SUCCESS) { | |
388 | free(bld); | |
389 | return GIT_ENOMEM; | |
390 | } | |
391 | ||
392 | if (source != NULL) { | |
0ad6efa1 VM |
393 | for (i = 0; i < source->entries.length; ++i) { |
394 | git_tree_entry *entry_src = source->entries.contents[i]; | |
0ad6efa1 | 395 | |
9ef9e8c3 | 396 | if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0) { |
0ad6efa1 VM |
397 | git_treebuilder_free(bld); |
398 | return GIT_ENOMEM; | |
399 | } | |
0ad6efa1 VM |
400 | } |
401 | } | |
402 | ||
403 | *builder_p = bld; | |
404 | return GIT_SUCCESS; | |
405 | } | |
406 | ||
407 | int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes) | |
408 | { | |
409 | git_tree_entry *entry; | |
410 | int pos; | |
761aa2aa | 411 | struct tree_key_search ksearch; |
0ad6efa1 VM |
412 | |
413 | assert(bld && id && filename); | |
414 | ||
415 | if (!valid_attributes(attributes)) | |
f4ad64c1 | 416 | return git__throw(GIT_ERROR, "Failed to insert entry. Invalid attributes"); |
0ad6efa1 | 417 | |
f4ad64c1 | 418 | if (build_ksearch(&ksearch, filename) < GIT_SUCCESS) |
419 | return git__throw(GIT_ERROR, "Failed to insert entry. Invalid filename '%s'", filename); | |
420 | ||
421 | if ((pos = git_vector_bsearch2(&bld->entries, entry_search_cmp, &ksearch)) != GIT_ENOTFOUND) { | |
0ad6efa1 VM |
422 | entry = git_vector_get(&bld->entries, pos); |
423 | if (entry->removed) { | |
424 | entry->removed = 0; | |
425 | bld->entry_count++; | |
426 | } | |
427 | } else { | |
428 | if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL) | |
429 | return GIT_ENOMEM; | |
430 | ||
431 | memset(entry, 0x0, sizeof(git_tree_entry)); | |
432 | entry->filename = git__strdup(filename); | |
433 | entry->filename_len = strlen(entry->filename); | |
434 | ||
435 | bld->entry_count++; | |
436 | } | |
437 | ||
438 | git_oid_cpy(&entry->oid, id); | |
439 | entry->attr = attributes; | |
440 | ||
98ac6780 | 441 | if (pos == GIT_ENOTFOUND) { |
0ad6efa1 VM |
442 | if (git_vector_insert(&bld->entries, entry) < 0) |
443 | return GIT_ENOMEM; | |
444 | } | |
445 | ||
446 | if (entry_out != NULL) | |
447 | *entry_out = entry; | |
448 | ||
449 | return GIT_SUCCESS; | |
450 | } | |
451 | ||
0cbbdc26 | 452 | static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename) |
0ad6efa1 VM |
453 | { |
454 | int idx; | |
455 | git_tree_entry *entry; | |
761aa2aa | 456 | struct tree_key_search ksearch; |
0ad6efa1 VM |
457 | |
458 | assert(bld && filename); | |
459 | ||
e6629d83 VM |
460 | if (build_ksearch(&ksearch, filename) < GIT_SUCCESS) |
461 | return NULL; | |
761aa2aa | 462 | |
761aa2aa | 463 | idx = git_vector_bsearch2(&bld->entries, entry_search_cmp, &ksearch); |
0ad6efa1 VM |
464 | if (idx == GIT_ENOTFOUND) |
465 | return NULL; | |
466 | ||
467 | entry = git_vector_get(&bld->entries, idx); | |
468 | if (entry->removed) | |
469 | return NULL; | |
470 | ||
471 | return entry; | |
472 | } | |
473 | ||
0cbbdc26 KS |
474 | const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename) |
475 | { | |
476 | return treebuilder_get(bld, filename); | |
477 | } | |
478 | ||
0ad6efa1 VM |
479 | int git_treebuilder_remove(git_treebuilder *bld, const char *filename) |
480 | { | |
0cbbdc26 | 481 | git_tree_entry *remove_ptr = treebuilder_get(bld, filename); |
0ad6efa1 VM |
482 | |
483 | if (remove_ptr == NULL || remove_ptr->removed) | |
d6de92b6 | 484 | return git__throw(GIT_ENOTFOUND, "Failed to remove entry. File isn't in the tree"); |
0ad6efa1 VM |
485 | |
486 | remove_ptr->removed = 1; | |
487 | bld->entry_count--; | |
488 | return GIT_SUCCESS; | |
489 | } | |
490 | ||
491 | int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld) | |
492 | { | |
afeecf4f | 493 | unsigned int i; |
0ad6efa1 | 494 | int error; |
afeecf4f | 495 | git_buf tree = GIT_BUF_INIT; |
0ad6efa1 VM |
496 | |
497 | assert(bld); | |
498 | ||
499 | sort_entries(bld); | |
500 | ||
afeecf4f VM |
501 | /* Grow the buffer beforehand to an estimated size */ |
502 | git_buf_grow(&tree, bld->entries.length * 72); | |
503 | ||
0ad6efa1 VM |
504 | for (i = 0; i < bld->entries.length; ++i) { |
505 | git_tree_entry *entry = bld->entries.contents[i]; | |
506 | ||
507 | if (entry->removed) | |
508 | continue; | |
509 | ||
afeecf4f VM |
510 | git_buf_printf(&tree, "%o ", entry->attr); |
511 | git_buf_put(&tree, entry->filename, entry->filename_len + 1); | |
512 | git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ); | |
0ad6efa1 VM |
513 | } |
514 | ||
afeecf4f VM |
515 | if (git_buf_oom(&tree)) { |
516 | git_buf_free(&tree); | |
517 | return git__throw(GIT_ENOMEM, "Not enough memory to build the tree data"); | |
c5d8745f | 518 | } |
0ad6efa1 | 519 | |
afeecf4f VM |
520 | error = git_odb_write(oid, git_repository_database(repo), tree.ptr, tree.size, GIT_OBJ_TREE); |
521 | git_buf_free(&tree); | |
0ad6efa1 | 522 | |
bc06a4ee | 523 | return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write tree"); |
0ad6efa1 VM |
524 | } |
525 | ||
526 | void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload) | |
527 | { | |
c5d8745f | 528 | unsigned int i; |
0ad6efa1 VM |
529 | |
530 | assert(bld && filter); | |
531 | ||
532 | for (i = 0; i < bld->entries.length; ++i) { | |
533 | git_tree_entry *entry = bld->entries.contents[i]; | |
534 | if (!entry->removed && filter(entry, payload)) | |
535 | entry->removed = 1; | |
536 | } | |
537 | } | |
538 | ||
539 | void git_treebuilder_clear(git_treebuilder *bld) | |
540 | { | |
c5d8745f | 541 | unsigned int i; |
0ad6efa1 VM |
542 | assert(bld); |
543 | ||
544 | for (i = 0; i < bld->entries.length; ++i) { | |
545 | git_tree_entry *e = bld->entries.contents[i]; | |
546 | free(e->filename); | |
547 | free(e); | |
548 | } | |
549 | ||
550 | git_vector_clear(&bld->entries); | |
551 | } | |
552 | ||
553 | void git_treebuilder_free(git_treebuilder *bld) | |
554 | { | |
555 | git_treebuilder_clear(bld); | |
556 | git_vector_free(&bld->entries); | |
557 | free(bld); | |
558 | } | |
559 | ||
560 |