]> git.proxmox.com Git - libgit2.git/blame - src/tree.c
repository: Change ownership semantics
[libgit2.git] / src / tree.c
CommitLineData
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
da37654d
VM
18#define ENTRY_IS_TREE(e) ((e)->attr & 040000)
19
8e9bfa4c
VM
20static int valid_attributes(const int attributes)
21{
932d1baf 22 return attributes >= 0 && attributes <= MAX_FILEMODE;
0ad6efa1
VM
23}
24
28c1451a
VM
25static int valid_entry_name(const char *filename)
26{
27 return strlen(filename) > 0 && strchr(filename, '/') == NULL;
28}
29
30static int entry_sort_cmp(const void *a, const void *b)
31{
32 const git_tree_entry *entry_a = (const git_tree_entry *)(a);
33 const git_tree_entry *entry_b = (const git_tree_entry *)(b);
34
35 return git_futils_cmp_path(
da37654d
VM
36 entry_a->filename, entry_a->filename_len, ENTRY_IS_TREE(entry_a),
37 entry_b->filename, entry_b->filename_len, ENTRY_IS_TREE(entry_b));
28c1451a
VM
38}
39
40
761aa2aa
VM
41struct tree_key_search {
42 const char *filename;
43 size_t filename_len;
44};
45
28c1451a 46static int homing_search_cmp(const void *key, const void *array_member)
2a884588 47{
0cbbdc26
KS
48 const struct tree_key_search *ksearch = key;
49 const git_tree_entry *entry = array_member;
2a884588 50
28c1451a
VM
51 const size_t len1 = ksearch->filename_len;
52 const size_t len2 = entry->filename_len;
e6629d83 53
28c1451a
VM
54 return memcmp(
55 ksearch->filename,
56 entry->filename,
57 len1 < len2 ? len1 : len2
58 );
9de27ad0
SJ
59}
60
28c1451a
VM
61/*
62 * Search for an entry in a given tree.
63 *
64 * Note that this search is performed in two steps because
65 * of the way tree entries are sorted internally in git:
66 *
67 * Entries in a tree are not sorted alphabetically; two entries
68 * with the same root prefix will have different positions
69 * depending on whether they are folders (subtrees) or normal files.
70 *
71 * Consequently, it is not possible to find an entry on the tree
72 * with a binary search if you don't know whether the filename
73 * you're looking for is a folder or a normal file.
74 *
75 * To work around this, we first perform a homing binary search
76 * on the tree, using the minimal length root prefix of our filename.
77 * Once the comparisons for this homing search start becoming
78 * ambiguous because of folder vs file sorting, we look linearly
79 * around the area for our target file.
80 */
81static int tree_key_search(git_vector *entries, const char *filename)
2a884588 82{
28c1451a
VM
83 struct tree_key_search ksearch;
84 const git_tree_entry *entry;
2a884588 85
28c1451a 86 int homing, i;
2a884588 87
28c1451a
VM
88 ksearch.filename = filename;
89 ksearch.filename_len = strlen(filename);
e6629d83 90
28c1451a
VM
91 /* Initial homing search; find an entry on the tree with
92 * the same prefix as the filename we're looking for */
93 homing = git_vector_bsearch2(entries, &homing_search_cmp, &ksearch);
94 if (homing < 0)
95 return homing;
96
97 /* We found a common prefix. Look forward as long as
98 * there are entries that share the common prefix */
99 for (i = homing; i < (int)entries->length; ++i) {
100 entry = entries->contents[i];
101
102 if (homing_search_cmp(&ksearch, entry) != 0)
103 break;
104
105 if (strcmp(filename, entry->filename) == 0)
106 return i;
8cf2de07 107 }
e6629d83 108
28c1451a
VM
109 /* If we haven't found our filename yet, look backwards
110 * too as long as we have entries with the same prefix */
111 for (i = homing - 1; i >= 0; --i) {
112 entry = entries->contents[i];
e6629d83 113
28c1451a
VM
114 if (homing_search_cmp(&ksearch, entry) != 0)
115 break;
e6629d83 116
28c1451a
VM
117 if (strcmp(filename, entry->filename) == 0)
118 return i;
119 }
120
121 /* The filename doesn't exist at all */
122 return GIT_ENOTFOUND;
e6629d83
VM
123}
124
72a3fe42 125void git_tree__free(git_tree *tree)
225fe215 126{
c4034e63 127 unsigned int i;
003c2690 128
c4034e63
VM
129 for (i = 0; i < tree->entries.length; ++i) {
130 git_tree_entry *e;
131 e = git_vector_get(&tree->entries, i);
132
3286c408
VM
133 git__free(e->filename);
134 git__free(e);
58519018 135 }
003c2690 136
c8f5ff8f 137 git_vector_free(&tree->entries);
3286c408 138 git__free(tree);
225fe215
VM
139}
140
d45b4a9a
VM
141const git_oid *git_tree_id(git_tree *c)
142{
143 return git_object_id((git_object *)c);
225fe215
VM
144}
145
0ad6efa1 146unsigned int git_tree_entry_attributes(const git_tree_entry *entry)
003c2690 147{
2a884588 148 return entry->attr;
003c2690
VM
149}
150
0ad6efa1 151const char *git_tree_entry_name(const git_tree_entry *entry)
003c2690 152{
c4b5bedc 153 assert(entry);
2a884588
VM
154 return entry->filename;
155}
003c2690 156
0ad6efa1 157const git_oid *git_tree_entry_id(const git_tree_entry *entry)
2a884588 158{
c4b5bedc 159 assert(entry);
2a884588 160 return &entry->oid;
003c2690
VM
161}
162
ff9a4c13
RG
163git_otype git_tree_entry_type(const git_tree_entry *entry)
164{
165 assert(entry);
166
167 if (S_ISGITLINK(entry->attr))
168 return GIT_OBJ_COMMIT;
169 else if (S_ISDIR(entry->attr))
170 return GIT_OBJ_TREE;
171 else
172 return GIT_OBJ_BLOB;
173}
174
0ad6efa1 175int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry)
003c2690 176{
1795f879 177 assert(entry && object_out);
72a3fe42 178 return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY);
122c3405
VM
179}
180
0ad6efa1 181const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
2a884588 182{
c4034e63 183 int idx;
c4b5bedc
VM
184
185 assert(tree && filename);
186
28c1451a 187 idx = tree_key_search(&tree->entries, filename);
c4034e63
VM
188 if (idx == GIT_ENOTFOUND)
189 return NULL;
190
191 return git_vector_get(&tree->entries, idx);
2a884588
VM
192}
193
e5c80097 194const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx)
003c2690 195{
c4b5bedc 196 assert(tree);
e5c80097 197 return git_vector_get(&tree->entries, idx);
003c2690
VM
198}
199
e5c80097 200unsigned int git_tree_entrycount(git_tree *tree)
003c2690 201{
c4b5bedc 202 assert(tree);
c4034e63 203 return tree->entries.length;
003c2690
VM
204}
205
720d5472 206static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end)
d8603ed9 207{
6f02c3ba 208 int error = GIT_SUCCESS;
2a884588 209
72a3fe42
VM
210 if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS)
211 return GIT_ENOMEM;
e4def81a 212
003c2690
VM
213 while (buffer < buffer_end) {
214 git_tree_entry *entry;
ad196c6a 215 int tmp;
d8603ed9 216
29e1789b 217 entry = git__calloc(1, sizeof(git_tree_entry));
2a884588
VM
218 if (entry == NULL) {
219 error = GIT_ENOMEM;
220 break;
003c2690 221 }
d8603ed9 222
6f02c3ba 223 if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS)
c4034e63 224 return GIT_ENOMEM;
003c2690 225
0b2c4061 226 if (git__strtol32(&tmp, buffer, &buffer, 8) < GIT_SUCCESS ||
8e9bfa4c 227 !buffer || !valid_attributes(tmp))
d6de92b6 228 return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Can't parse attributes");
8e9bfa4c 229
0b2c4061 230 entry->attr = tmp;
d8603ed9
VM
231
232 if (*buffer++ != ' ') {
d6de92b6 233 error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted");
d8603ed9
VM
234 break;
235 }
236
58519018 237 if (memchr(buffer, 0, buffer_end - buffer) == NULL) {
d6de92b6 238 error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted");
58519018
VM
239 break;
240 }
241
242 entry->filename = git__strdup(buffer);
0ad6efa1 243 entry->filename_len = strlen(buffer);
d8603ed9 244
2a884588
VM
245 while (buffer < buffer_end && *buffer != 0)
246 buffer++;
003c2690 247
2a884588 248 buffer++;
d8603ed9 249
fa48608e 250 git_oid_fromraw(&entry->oid, (const unsigned char *)buffer);
d8603ed9 251 buffer += GIT_OID_RAWSZ;
d8603ed9
VM
252 }
253
bc06a4ee 254 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse buffer");
d8603ed9 255}
58519018 256
72a3fe42 257int git_tree__parse(git_tree *tree, git_odb_object *obj)
58519018 258{
72a3fe42
VM
259 assert(tree);
260 return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len);
58519018
VM
261}
262
8255c69b
CMN
263static unsigned int find_next_dir(const char *dirname, git_index *index, unsigned int start)
264{
265 unsigned int i, entries = git_index_entrycount(index);
266 size_t dirlen;
267
268 dirlen = strlen(dirname);
269 for (i = start; i < entries; ++i) {
270 git_index_entry *entry = git_index_get(index, i);
271 if (strlen(entry->path) < dirlen ||
272 memcmp(entry->path, dirname, dirlen) ||
273 (dirlen > 0 && entry->path[dirlen] != '/')) {
274 break;
275 }
276 }
277
278 return i;
279}
280
9ef9e8c3
VM
281static int append_entry(git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
282{
283 git_tree_entry *entry;
284
285 if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
286 return GIT_ENOMEM;
287
288 memset(entry, 0x0, sizeof(git_tree_entry));
289 entry->filename = git__strdup(filename);
290 entry->filename_len = strlen(entry->filename);
291
292 bld->entry_count++;
293
294 git_oid_cpy(&entry->oid, id);
295 entry->attr = attributes;
296
297 if (git_vector_insert(&bld->entries, entry) < 0)
298 return GIT_ENOMEM;
299
300 return GIT_SUCCESS;
301}
302
9462c471
VM
303static int write_tree(
304 git_oid *oid,
305 git_repository *repo,
306 git_index *index,
307 const char *dirname,
308 unsigned int start)
47d8ec56 309{
4a619797 310 git_treebuilder *bld = NULL;
9462c471 311
4a619797
CMN
312 unsigned int i, entries = git_index_entrycount(index);
313 int error;
314 size_t dirname_len = strlen(dirname);
8255c69b
CMN
315 const git_tree_cache *cache;
316
317 cache = git_tree_cache_get(index->tree, dirname);
318 if (cache != NULL && cache->entries >= 0){
319 git_oid_cpy(oid, &cache->oid);
320 return find_next_dir(dirname, index, start);
321 }
29e1789b 322
4a619797
CMN
323 error = git_treebuilder_create(&bld, NULL);
324 if (bld == NULL) {
47d8ec56 325 return GIT_ENOMEM;
4a619797 326 }
932d1baf 327
4a619797
CMN
328 /*
329 * This loop is unfortunate, but necessary. The index doesn't have
330 * any directores, so we need to handle that manually, and we
331 * need to keep track of the current position.
332 */
333 for (i = start; i < entries; ++i) {
334 git_index_entry *entry = git_index_get(index, i);
335 char *filename, *next_slash;
336
337 /*
338 * If we've left our (sub)tree, exit the loop and return. The
339 * first check is an early out (and security for the
340 * third). The second check is a simple prefix comparison. The
341 * third check catches situations where there is a directory
342 * win32/sys and a file win32mmap.c. Without it, the following
343 * code believes there is a file win32/mmap.c
344 */
345 if (strlen(entry->path) < dirname_len ||
346 memcmp(entry->path, dirname, dirname_len) ||
347 (dirname_len > 0 && entry->path[dirname_len] != '/')) {
47d8ec56 348 break;
4a619797 349 }
932d1baf 350
4a619797
CMN
351 filename = entry->path + dirname_len;
352 if (*filename == '/')
353 filename++;
354 next_slash = strchr(filename, '/');
355 if (next_slash) {
356 git_oid sub_oid;
357 int written;
358 char *subdir, *last_comp;
359
360 subdir = git__strndup(entry->path, next_slash - entry->path);
361 if (subdir == NULL) {
362 error = GIT_ENOMEM;
363 goto cleanup;
29e1789b 364 }
29e1789b 365
4a619797 366 /* Write out the subtree */
9462c471 367 written = write_tree(&sub_oid, repo, index, subdir, i);
4a619797
CMN
368 if (written < 0) {
369 error = git__rethrow(written, "Failed to write subtree %s", subdir);
370 } else {
371 i = written - 1; /* -1 because of the loop increment */
29e1789b 372 }
932d1baf 373
4a619797
CMN
374 /*
375 * We need to figure out what we want toinsert
376 * into this tree. If we're traversing
377 * deps/zlib/, then we only want to write
378 * 'zlib' into the tree.
379 */
380 last_comp = strrchr(subdir, '/');
381 if (last_comp) {
382 last_comp++; /* Get rid of the '/' */
383 } else {
384 last_comp = subdir;
385 }
9ef9e8c3 386 error = append_entry(bld, last_comp, &sub_oid, S_IFDIR);
3286c408 387 git__free(subdir);
4a619797
CMN
388 if (error < GIT_SUCCESS) {
389 error = git__rethrow(error, "Failed to insert dir");
390 goto cleanup;
391 }
392 } else {
9ef9e8c3 393 error = append_entry(bld, filename, &entry->oid, entry->mode);
4a619797
CMN
394 if (error < GIT_SUCCESS) {
395 error = git__rethrow(error, "Failed to insert file");
396 }
47d8ec56 397 }
29e1789b 398 }
932d1baf 399
9462c471 400 error = git_treebuilder_write(oid, repo, bld);
4a619797
CMN
401 if (error < GIT_SUCCESS)
402 error = git__rethrow(error, "Failed to write tree to db");
29e1789b 403
4a619797
CMN
404 cleanup:
405 git_treebuilder_free(bld);
406
407 if (error < GIT_SUCCESS)
408 return error;
409 else
410 return i;
29e1789b
VM
411}
412
413int git_tree_create_fromindex(git_oid *oid, git_index *index)
414{
9462c471 415 git_repository *repo;
29e1789b
VM
416 int error;
417
9462c471
VM
418 repo = (git_repository *)GIT_REFCOUNT_OWNER(index);
419
420 if (repo == NULL)
421 return git__throw(GIT_EBAREINDEX,
422 "Failed to create tree. "
423 "The index file is not backed up by an existing repository");
47d8ec56 424
8255c69b
CMN
425 if (index->tree != NULL && index->tree->entries >= 0) {
426 git_oid_cpy(oid, &index->tree->oid);
427 return GIT_SUCCESS;
428 }
429
4a619797 430 /* The tree cache didn't help us */
9462c471 431 error = write_tree(oid, repo, index, "", 0);
bc06a4ee 432 return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS;
47d8ec56 433}
0ad6efa1
VM
434
435static void sort_entries(git_treebuilder *bld)
436{
437 git_vector_sort(&bld->entries);
438}
439
440int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
441{
442 git_treebuilder *bld;
c5d8745f 443 unsigned int i, source_entries = DEFAULT_TREE_SIZE;
0ad6efa1
VM
444
445 assert(builder_p);
446
447 bld = git__calloc(1, sizeof(git_treebuilder));
448 if (bld == NULL)
449 return GIT_ENOMEM;
450
451 if (source != NULL)
452 source_entries = source->entries.length;
453
454 if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < GIT_SUCCESS) {
3286c408 455 git__free(bld);
0ad6efa1
VM
456 return GIT_ENOMEM;
457 }
458
459 if (source != NULL) {
0ad6efa1
VM
460 for (i = 0; i < source->entries.length; ++i) {
461 git_tree_entry *entry_src = source->entries.contents[i];
0ad6efa1 462
9ef9e8c3 463 if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0) {
0ad6efa1
VM
464 git_treebuilder_free(bld);
465 return GIT_ENOMEM;
466 }
0ad6efa1
VM
467 }
468 }
469
470 *builder_p = bld;
471 return GIT_SUCCESS;
472}
473
474int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
475{
476 git_tree_entry *entry;
477 int pos;
478
479 assert(bld && id && filename);
480
481 if (!valid_attributes(attributes))
f4ad64c1 482 return git__throw(GIT_ERROR, "Failed to insert entry. Invalid attributes");
0ad6efa1 483
28c1451a
VM
484 if (!valid_entry_name(filename))
485 return git__throw(GIT_ERROR, "Failed to insert entry. Invalid name for a tree entry");
f4ad64c1 486
28c1451a 487 pos = tree_key_search(&bld->entries, filename);
8cf2de07 488
28c1451a 489 if (pos >= 0) {
0ad6efa1
VM
490 entry = git_vector_get(&bld->entries, pos);
491 if (entry->removed) {
492 entry->removed = 0;
493 bld->entry_count++;
494 }
495 } else {
496 if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
497 return GIT_ENOMEM;
498
499 memset(entry, 0x0, sizeof(git_tree_entry));
500 entry->filename = git__strdup(filename);
501 entry->filename_len = strlen(entry->filename);
502
503 bld->entry_count++;
504 }
505
506 git_oid_cpy(&entry->oid, id);
507 entry->attr = attributes;
508
98ac6780 509 if (pos == GIT_ENOTFOUND) {
0ad6efa1
VM
510 if (git_vector_insert(&bld->entries, entry) < 0)
511 return GIT_ENOMEM;
512 }
513
514 if (entry_out != NULL)
515 *entry_out = entry;
516
517 return GIT_SUCCESS;
518}
519
0cbbdc26 520static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
0ad6efa1
VM
521{
522 int idx;
523 git_tree_entry *entry;
524
525 assert(bld && filename);
526
28c1451a
VM
527 idx = tree_key_search(&bld->entries, filename);
528 if (idx < 0)
0ad6efa1
VM
529 return NULL;
530
531 entry = git_vector_get(&bld->entries, idx);
532 if (entry->removed)
533 return NULL;
534
535 return entry;
536}
537
0cbbdc26
KS
538const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename)
539{
540 return treebuilder_get(bld, filename);
541}
542
0ad6efa1
VM
543int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
544{
0cbbdc26 545 git_tree_entry *remove_ptr = treebuilder_get(bld, filename);
0ad6efa1
VM
546
547 if (remove_ptr == NULL || remove_ptr->removed)
d6de92b6 548 return git__throw(GIT_ENOTFOUND, "Failed to remove entry. File isn't in the tree");
0ad6efa1
VM
549
550 remove_ptr->removed = 1;
551 bld->entry_count--;
552 return GIT_SUCCESS;
553}
554
555int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
556{
afeecf4f 557 unsigned int i;
0ad6efa1 558 int error;
afeecf4f 559 git_buf tree = GIT_BUF_INIT;
9462c471 560 git_odb *odb;
0ad6efa1
VM
561
562 assert(bld);
563
564 sort_entries(bld);
565
afeecf4f
VM
566 /* Grow the buffer beforehand to an estimated size */
567 git_buf_grow(&tree, bld->entries.length * 72);
568
0ad6efa1
VM
569 for (i = 0; i < bld->entries.length; ++i) {
570 git_tree_entry *entry = bld->entries.contents[i];
571
572 if (entry->removed)
573 continue;
574
afeecf4f
VM
575 git_buf_printf(&tree, "%o ", entry->attr);
576 git_buf_put(&tree, entry->filename, entry->filename_len + 1);
577 git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
0ad6efa1
VM
578 }
579
afeecf4f
VM
580 if (git_buf_oom(&tree)) {
581 git_buf_free(&tree);
582 return git__throw(GIT_ENOMEM, "Not enough memory to build the tree data");
c5d8745f 583 }
0ad6efa1 584
9462c471
VM
585 error = git_repository_odb__weakptr(&odb, repo);
586 if (error < GIT_SUCCESS) {
587 git_buf_free(&tree);
588 return error;
589 }
590
591 error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
afeecf4f 592 git_buf_free(&tree);
0ad6efa1 593
bc06a4ee 594 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write tree");
0ad6efa1
VM
595}
596
597void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)
598{
c5d8745f 599 unsigned int i;
0ad6efa1
VM
600
601 assert(bld && filter);
602
603 for (i = 0; i < bld->entries.length; ++i) {
604 git_tree_entry *entry = bld->entries.contents[i];
605 if (!entry->removed && filter(entry, payload))
606 entry->removed = 1;
607 }
608}
609
610void git_treebuilder_clear(git_treebuilder *bld)
611{
c5d8745f 612 unsigned int i;
0ad6efa1
VM
613 assert(bld);
614
615 for (i = 0; i < bld->entries.length; ++i) {
616 git_tree_entry *e = bld->entries.contents[i];
3286c408
VM
617 git__free(e->filename);
618 git__free(e);
0ad6efa1
VM
619 }
620
621 git_vector_clear(&bld->entries);
622}
623
624void git_treebuilder_free(git_treebuilder *bld)
625{
626 git_treebuilder_clear(bld);
627 git_vector_free(&bld->entries);
3286c408 628 git__free(bld);
0ad6efa1
VM
629}
630
9432af36
VM
631static int tree_frompath(
632 git_tree **parent_out,
633 git_tree *root,
634 const char *treeentry_path,
635 int offset)
3fa735ca 636{
637 char *slash_pos = NULL;
638 const git_tree_entry* entry;
639 int error = GIT_SUCCESS;
640 git_tree *subtree;
641
642 if (!*(treeentry_path + offset))
9432af36
VM
643 return git__rethrow(GIT_EINVALIDPATH,
644 "Invalid relative path to a tree entry '%s'.", treeentry_path);
3fa735ca 645
646 slash_pos = (char *)strchr(treeentry_path + offset, '/');
647
648 if (slash_pos == NULL)
9432af36
VM
649 return git_tree_lookup(
650 parent_out,
651 root->object.repo,
652 git_object_id((const git_object *)root)
653 );
3fa735ca 654
655 if (slash_pos == treeentry_path + offset)
9432af36
VM
656 return git__rethrow(GIT_EINVALIDPATH,
657 "Invalid relative path to a tree entry '%s'.", treeentry_path);
3fa735ca 658
659 *slash_pos = '\0';
660
661 entry = git_tree_entry_byname(root, treeentry_path + offset);
662
663 if (slash_pos != NULL)
664 *slash_pos = '/';
665
666 if (entry == NULL)
9432af36
VM
667 return git__rethrow(GIT_ENOTFOUND,
668 "No tree entry can be found from "
669 "the given tree and relative path '%s'.", treeentry_path);
0ad6efa1 670
9432af36
VM
671
672 error = git_tree_lookup(&subtree, root->object.repo, &entry->oid);
673 if (error < GIT_SUCCESS)
3fa735ca 674 return error;
675
9432af36
VM
676 error = tree_frompath(
677 parent_out,
678 subtree,
679 treeentry_path,
680 slash_pos - treeentry_path + 1
681 );
3fa735ca 682
683 git_tree_close(subtree);
684 return error;
685}
686
9432af36
VM
687int git_tree_get_subtree(
688 git_tree **subtree,
689 git_tree *root,
690 const char *subtree_path)
3fa735ca 691{
692 char buffer[GIT_PATH_MAX];
693
9432af36 694 assert(subtree && root && subtree_path);
3fa735ca 695
9432af36
VM
696 strncpy(buffer, subtree_path, GIT_PATH_MAX);
697 return tree_frompath(subtree, root, buffer, 0);
3fa735ca 698}
da37654d 699
9432af36
VM
700static int tree_walk_post(
701 git_tree *tree,
702 git_treewalk_cb callback,
703 char *root,
2ba14f23
VM
704 size_t root_len,
705 void *payload)
da37654d
VM
706{
707 int error;
708 unsigned int i;
709
710 for (i = 0; i < tree->entries.length; ++i) {
711 git_tree_entry *entry = tree->entries.contents[i];
712
713 root[root_len] = '\0';
714
2ba14f23 715 if (callback(root, entry, payload) < 0)
da37654d
VM
716 continue;
717
718 if (ENTRY_IS_TREE(entry)) {
719 git_tree *subtree;
720
9432af36
VM
721 if ((error = git_tree_lookup(
722 &subtree, tree->object.repo, &entry->oid)) < 0)
da37654d
VM
723 return error;
724
725 strcpy(root + root_len, entry->filename);
726 root[root_len + entry->filename_len] = '/';
727
9432af36 728 tree_walk_post(subtree,
2ba14f23
VM
729 callback, root,
730 root_len + entry->filename_len + 1,
731 payload
732 );
da37654d
VM
733
734 git_tree_close(subtree);
735 }
736 }
737
738 return GIT_SUCCESS;
739}
740
2ba14f23 741int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload)
da37654d
VM
742{
743 char root_path[GIT_PATH_MAX];
744
745 root_path[0] = '\0';
746 switch (mode) {
747 case GIT_TREEWALK_POST:
2ba14f23 748 return tree_walk_post(tree, callback, root_path, 0, payload);
da37654d
VM
749
750 case GIT_TREEWALK_PRE:
751 return git__throw(GIT_ENOTIMPLEMENTED,
752 "Preorder tree walking is still not implemented");
753
754 default:
755 return git__throw(GIT_EINVALIDARGS,
756 "Invalid walking mode for tree walk");
757 }
758}