2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
11 #include "git2/repository.h"
12 #include "git2/object.h"
14 #include "tree-cache.h"
17 #define DEFAULT_TREE_SIZE 16
18 #define MAX_FILEMODE_BYTES 6
20 #define TREE_ENTRY_CHECK_NAMELEN(n) \
21 if (n > UINT16_MAX) { giterr_set(GITERR_INVALID, "tree entry path too long"); }
25 static bool valid_filemode(const int filemode
)
27 return (filemode
== GIT_FILEMODE_TREE
28 || filemode
== GIT_FILEMODE_BLOB
29 || filemode
== GIT_FILEMODE_BLOB_EXECUTABLE
30 || filemode
== GIT_FILEMODE_LINK
31 || filemode
== GIT_FILEMODE_COMMIT
);
34 GIT_INLINE(git_filemode_t
) normalize_filemode(git_filemode_t filemode
)
36 /* Tree bits set, but it's not a commit */
37 if (GIT_MODE_TYPE(filemode
) == GIT_FILEMODE_TREE
)
38 return GIT_FILEMODE_TREE
;
40 /* If any of the x bits are set */
41 if (GIT_PERMS_IS_EXEC(filemode
))
42 return GIT_FILEMODE_BLOB_EXECUTABLE
;
44 /* 16XXXX means commit */
45 if (GIT_MODE_TYPE(filemode
) == GIT_FILEMODE_COMMIT
)
46 return GIT_FILEMODE_COMMIT
;
48 /* 12XXXX means commit */
49 if (GIT_MODE_TYPE(filemode
) == GIT_FILEMODE_LINK
)
50 return GIT_FILEMODE_LINK
;
52 /* Otherwise, return a blob */
53 return GIT_FILEMODE_BLOB
;
56 static int valid_entry_name(git_repository
*repo
, const char *filename
)
58 return *filename
!= '\0' &&
59 git_path_isvalid(repo
, filename
,
60 GIT_PATH_REJECT_TRAVERSAL
| GIT_PATH_REJECT_DOT_GIT
| GIT_PATH_REJECT_SLASH
);
63 static int entry_sort_cmp(const void *a
, const void *b
)
65 const git_tree_entry
*e1
= (const git_tree_entry
*)a
;
66 const git_tree_entry
*e2
= (const git_tree_entry
*)b
;
69 e1
->filename
, e1
->filename_len
, git_tree_entry__is_tree(e1
),
70 e2
->filename
, e2
->filename_len
, git_tree_entry__is_tree(e2
),
74 int git_tree_entry_cmp(const git_tree_entry
*e1
, const git_tree_entry
*e2
)
76 return entry_sort_cmp(e1
, e2
);
79 int git_tree_entry_icmp(const git_tree_entry
*e1
, const git_tree_entry
*e2
)
82 e1
->filename
, e1
->filename_len
, git_tree_entry__is_tree(e1
),
83 e2
->filename
, e2
->filename_len
, git_tree_entry__is_tree(e2
),
88 * Allocate either from the pool or from the system allocator
90 static git_tree_entry
*alloc_entry_base(git_pool
*pool
, const char *filename
, size_t filename_len
)
92 git_tree_entry
*entry
= NULL
;
95 TREE_ENTRY_CHECK_NAMELEN(filename_len
);
97 if (GIT_ADD_SIZET_OVERFLOW(&tree_len
, sizeof(git_tree_entry
), filename_len
) ||
98 GIT_ADD_SIZET_OVERFLOW(&tree_len
, tree_len
, 1))
101 entry
= pool
? git_pool_malloc(pool
, tree_len
) :
102 git__malloc(tree_len
);
106 memset(entry
, 0x0, sizeof(git_tree_entry
));
107 memcpy(entry
->filename
, filename
, filename_len
);
108 entry
->filename
[filename_len
] = 0;
109 entry
->filename_len
= (uint16_t)filename_len
;
115 * Allocate a tree entry, using the poolin the tree which owns
116 * it. This is useful when reading trees, so we don't allocate a ton
117 * of small strings but can use the pool.
119 static git_tree_entry
*alloc_entry_pooled(git_pool
*pool
, const char *filename
, size_t filename_len
)
121 git_tree_entry
*entry
= NULL
;
123 if (!(entry
= alloc_entry_base(pool
, filename
, filename_len
)))
126 entry
->pooled
= true;
131 static git_tree_entry
*alloc_entry(const char *filename
)
133 return alloc_entry_base(NULL
, filename
, strlen(filename
));
136 struct tree_key_search
{
137 const char *filename
;
138 uint16_t filename_len
;
141 static int homing_search_cmp(const void *key
, const void *array_member
)
143 const struct tree_key_search
*ksearch
= key
;
144 const git_tree_entry
*entry
= array_member
;
146 const uint16_t len1
= ksearch
->filename_len
;
147 const uint16_t len2
= entry
->filename_len
;
152 len1
< len2
? len1
: len2
157 * Search for an entry in a given tree.
159 * Note that this search is performed in two steps because
160 * of the way tree entries are sorted internally in git:
162 * Entries in a tree are not sorted alphabetically; two entries
163 * with the same root prefix will have different positions
164 * depending on whether they are folders (subtrees) or normal files.
166 * Consequently, it is not possible to find an entry on the tree
167 * with a binary search if you don't know whether the filename
168 * you're looking for is a folder or a normal file.
170 * To work around this, we first perform a homing binary search
171 * on the tree, using the minimal length root prefix of our filename.
172 * Once the comparisons for this homing search start becoming
173 * ambiguous because of folder vs file sorting, we look linearly
174 * around the area for our target file.
176 static int tree_key_search(
177 size_t *at_pos
, git_vector
*entries
, const char *filename
, size_t filename_len
)
179 struct tree_key_search ksearch
;
180 const git_tree_entry
*entry
;
183 TREE_ENTRY_CHECK_NAMELEN(filename_len
);
185 ksearch
.filename
= filename
;
186 ksearch
.filename_len
= (uint16_t)filename_len
;
188 /* Initial homing search; find an entry on the tree with
189 * the same prefix as the filename we're looking for */
190 if (git_vector_bsearch2(&homing
, entries
, &homing_search_cmp
, &ksearch
) < 0)
191 return GIT_ENOTFOUND
; /* just a signal error; not passed back to user */
193 /* We found a common prefix. Look forward as long as
194 * there are entries that share the common prefix */
195 for (i
= homing
; i
< entries
->length
; ++i
) {
196 entry
= entries
->contents
[i
];
198 if (homing_search_cmp(&ksearch
, entry
) < 0)
201 if (entry
->filename_len
== filename_len
&&
202 memcmp(filename
, entry
->filename
, filename_len
) == 0) {
210 /* If we haven't found our filename yet, look backwards
211 * too as long as we have entries with the same prefix */
216 entry
= entries
->contents
[i
];
218 if (homing_search_cmp(&ksearch
, entry
) > 0)
221 if (entry
->filename_len
== filename_len
&&
222 memcmp(filename
, entry
->filename
, filename_len
) == 0) {
231 /* The filename doesn't exist at all */
232 return GIT_ENOTFOUND
;
235 void git_tree_entry_free(git_tree_entry
*entry
)
237 if (entry
== NULL
|| entry
->pooled
)
243 int git_tree_entry_dup(git_tree_entry
**dest
, const git_tree_entry
*source
)
246 git_tree_entry
*copy
;
250 GITERR_CHECK_ALLOC_ADD(&total_size
, sizeof(git_tree_entry
), source
->filename_len
);
251 GITERR_CHECK_ALLOC_ADD(&total_size
, total_size
, 1);
253 copy
= git__malloc(total_size
);
254 GITERR_CHECK_ALLOC(copy
);
256 memcpy(copy
, source
, total_size
);
264 void git_tree__free(void *_tree
)
266 git_tree
*tree
= _tree
;
270 git_vector_foreach(&tree
->entries
, i
, e
)
271 git_tree_entry_free(e
);
273 git_vector_free(&tree
->entries
);
274 git_pool_clear(&tree
->pool
);
278 git_filemode_t
git_tree_entry_filemode(const git_tree_entry
*entry
)
280 return normalize_filemode(entry
->attr
);
283 git_filemode_t
git_tree_entry_filemode_raw(const git_tree_entry
*entry
)
288 const char *git_tree_entry_name(const git_tree_entry
*entry
)
291 return entry
->filename
;
294 const git_oid
*git_tree_entry_id(const git_tree_entry
*entry
)
300 git_otype
git_tree_entry_type(const git_tree_entry
*entry
)
304 if (S_ISGITLINK(entry
->attr
))
305 return GIT_OBJ_COMMIT
;
306 else if (S_ISDIR(entry
->attr
))
312 int git_tree_entry_to_object(
313 git_object
**object_out
,
314 git_repository
*repo
,
315 const git_tree_entry
*entry
)
317 assert(entry
&& object_out
);
318 return git_object_lookup(object_out
, repo
, &entry
->oid
, GIT_OBJ_ANY
);
321 static const git_tree_entry
*entry_fromname(
322 const git_tree
*tree
, const char *name
, size_t name_len
)
326 /* be safe when we cast away constness - i.e. don't trigger a sort */
327 assert(git_vector_is_sorted(&tree
->entries
));
329 if (tree_key_search(&idx
, (git_vector
*)&tree
->entries
, name
, name_len
) < 0)
332 return git_vector_get(&tree
->entries
, idx
);
335 const git_tree_entry
*git_tree_entry_byname(
336 const git_tree
*tree
, const char *filename
)
338 assert(tree
&& filename
);
340 return entry_fromname(tree
, filename
, strlen(filename
));
343 const git_tree_entry
*git_tree_entry_byindex(
344 const git_tree
*tree
, size_t idx
)
347 return git_vector_get(&tree
->entries
, idx
);
350 const git_tree_entry
*git_tree_entry_byid(
351 const git_tree
*tree
, const git_oid
*id
)
354 const git_tree_entry
*e
;
358 git_vector_foreach(&tree
->entries
, i
, e
) {
359 if (memcmp(&e
->oid
.id
, &id
->id
, sizeof(id
->id
)) == 0)
366 int git_tree__prefix_position(const git_tree
*tree
, const char *path
)
368 const git_vector
*entries
= &tree
->entries
;
369 struct tree_key_search ksearch
;
370 size_t at_pos
, path_len
;
375 path_len
= strlen(path
);
376 TREE_ENTRY_CHECK_NAMELEN(path_len
);
378 ksearch
.filename
= path
;
379 ksearch
.filename_len
= (uint16_t)path_len
;
381 /* be safe when we cast away constness - i.e. don't trigger a sort */
382 assert(git_vector_is_sorted(&tree
->entries
));
384 /* Find tree entry with appropriate prefix */
386 &at_pos
, (git_vector
*)entries
, &homing_search_cmp
, &ksearch
);
388 for (; at_pos
< entries
->length
; ++at_pos
) {
389 const git_tree_entry
*entry
= entries
->contents
[at_pos
];
390 if (homing_search_cmp(&ksearch
, entry
) < 0)
394 for (; at_pos
> 0; --at_pos
) {
395 const git_tree_entry
*entry
= entries
->contents
[at_pos
- 1];
396 if (homing_search_cmp(&ksearch
, entry
) > 0)
403 size_t git_tree_entrycount(const git_tree
*tree
)
406 return tree
->entries
.length
;
409 unsigned int git_treebuilder_entrycount(git_treebuilder
*bld
)
413 return git_strmap_num_entries(bld
->map
);
416 static int tree_error(const char *str
, const char *path
)
419 giterr_set(GITERR_TREE
, "%s - %s", str
, path
);
421 giterr_set(GITERR_TREE
, "%s", str
);
425 static int parse_mode(unsigned int *modep
, const char *buffer
, const char **buffer_out
)
428 unsigned int mode
= 0;
433 while ((c
= *buffer
++) != ' ') {
434 if (c
< '0' || c
> '7')
436 mode
= (mode
<< 3) + (c
- '0');
439 *buffer_out
= buffer
;
444 int git_tree__parse(void *_tree
, git_odb_object
*odb_obj
)
446 git_tree
*tree
= _tree
;
447 const char *buffer
= git_odb_object_data(odb_obj
);
448 const char *buffer_end
= buffer
+ git_odb_object_size(odb_obj
);
450 git_pool_init(&tree
->pool
, 1);
451 if (git_vector_init(&tree
->entries
, DEFAULT_TREE_SIZE
, entry_sort_cmp
) < 0)
454 while (buffer
< buffer_end
) {
455 git_tree_entry
*entry
;
460 if (parse_mode(&attr
, buffer
, &buffer
) < 0 || !buffer
)
461 return tree_error("Failed to parse tree. Can't parse filemode", NULL
);
463 if ((nul
= memchr(buffer
, 0, buffer_end
- buffer
)) == NULL
)
464 return tree_error("Failed to parse tree. Object is corrupted", NULL
);
466 filename_len
= nul
- buffer
;
467 /** Allocate the entry and store it in the entries vector */
469 entry
= alloc_entry_pooled(&tree
->pool
, buffer
, filename_len
);
470 GITERR_CHECK_ALLOC(entry
);
472 if (git_vector_insert(&tree
->entries
, entry
) < 0)
478 /* Advance to the ID just after the path */
479 buffer
+= filename_len
+ 1;
481 git_oid_fromraw(&entry
->oid
, (const unsigned char *)buffer
);
482 buffer
+= GIT_OID_RAWSZ
;
485 /* The tree is sorted by definition. Bad inputs give bad outputs */
486 tree
->entries
.flags
|= GIT_VECTOR_SORTED
;
491 static size_t find_next_dir(const char *dirname
, git_index
*index
, size_t start
)
493 size_t dirlen
, i
, entries
= git_index_entrycount(index
);
495 dirlen
= strlen(dirname
);
496 for (i
= start
; i
< entries
; ++i
) {
497 const git_index_entry
*entry
= git_index_get_byindex(index
, i
);
498 if (strlen(entry
->path
) < dirlen
||
499 memcmp(entry
->path
, dirname
, dirlen
) ||
500 (dirlen
> 0 && entry
->path
[dirlen
] != '/')) {
508 static int append_entry(
509 git_treebuilder
*bld
,
510 const char *filename
,
512 git_filemode_t filemode
)
514 git_tree_entry
*entry
;
517 if (!valid_entry_name(bld
->repo
, filename
))
518 return tree_error("Failed to insert entry. Invalid name for a tree entry", filename
);
520 entry
= alloc_entry(filename
);
521 GITERR_CHECK_ALLOC(entry
);
523 git_oid_cpy(&entry
->oid
, id
);
524 entry
->attr
= (uint16_t)filemode
;
526 git_strmap_insert(bld
->map
, entry
->filename
, entry
, error
);
528 git_tree_entry_free(entry
);
529 giterr_set(GITERR_TREE
, "failed to append entry %s to the tree builder", filename
);
536 static int write_tree(
538 git_repository
*repo
,
543 git_treebuilder
*bld
= NULL
;
544 size_t i
, entries
= git_index_entrycount(index
);
546 size_t dirname_len
= strlen(dirname
);
547 const git_tree_cache
*cache
;
549 cache
= git_tree_cache_get(index
->tree
, dirname
);
550 if (cache
!= NULL
&& cache
->entry_count
>= 0){
551 git_oid_cpy(oid
, &cache
->oid
);
552 return (int)find_next_dir(dirname
, index
, start
);
555 if ((error
= git_treebuilder_new(&bld
, repo
, NULL
)) < 0 || bld
== NULL
)
559 * This loop is unfortunate, but necessary. The index doesn't have
560 * any directores, so we need to handle that manually, and we
561 * need to keep track of the current position.
563 for (i
= start
; i
< entries
; ++i
) {
564 const git_index_entry
*entry
= git_index_get_byindex(index
, i
);
565 const char *filename
, *next_slash
;
568 * If we've left our (sub)tree, exit the loop and return. The
569 * first check is an early out (and security for the
570 * third). The second check is a simple prefix comparison. The
571 * third check catches situations where there is a directory
572 * win32/sys and a file win32mmap.c. Without it, the following
573 * code believes there is a file win32/mmap.c
575 if (strlen(entry
->path
) < dirname_len
||
576 memcmp(entry
->path
, dirname
, dirname_len
) ||
577 (dirname_len
> 0 && entry
->path
[dirname_len
] != '/')) {
581 filename
= entry
->path
+ dirname_len
;
582 if (*filename
== '/')
584 next_slash
= strchr(filename
, '/');
588 char *subdir
, *last_comp
;
590 subdir
= git__strndup(entry
->path
, next_slash
- entry
->path
);
591 GITERR_CHECK_ALLOC(subdir
);
593 /* Write out the subtree */
594 written
= write_tree(&sub_oid
, repo
, index
, subdir
, i
);
599 i
= written
- 1; /* -1 because of the loop increment */
603 * We need to figure out what we want toinsert
604 * into this tree. If we're traversing
605 * deps/zlib/, then we only want to write
606 * 'zlib' into the tree.
608 last_comp
= strrchr(subdir
, '/');
610 last_comp
++; /* Get rid of the '/' */
615 error
= append_entry(bld
, last_comp
, &sub_oid
, S_IFDIR
);
620 error
= append_entry(bld
, filename
, &entry
->id
, entry
->mode
);
626 if (git_treebuilder_write(oid
, bld
) < 0)
629 git_treebuilder_free(bld
);
633 git_treebuilder_free(bld
);
637 int git_tree__write_index(
638 git_oid
*oid
, git_index
*index
, git_repository
*repo
)
642 bool old_ignore_case
= false;
644 assert(oid
&& index
&& repo
);
646 if (git_index_has_conflicts(index
)) {
647 giterr_set(GITERR_INDEX
,
648 "Cannot create a tree from a not fully merged index.");
649 return GIT_EUNMERGED
;
652 if (index
->tree
!= NULL
&& index
->tree
->entry_count
>= 0) {
653 git_oid_cpy(oid
, &index
->tree
->oid
);
657 /* The tree cache didn't help us; we'll have to write
658 * out a tree. If the index is ignore_case, we must
659 * make it case-sensitive for the duration of the tree-write
662 if (index
->ignore_case
) {
663 old_ignore_case
= true;
664 git_index__set_ignore_case(index
, false);
667 ret
= write_tree(oid
, repo
, index
, "", 0);
670 git_index__set_ignore_case(index
, true);
677 git_pool_clear(&index
->tree_pool
);
679 if ((ret
= git_tree_lookup(&tree
, repo
, oid
)) < 0)
682 /* Read the tree cache into the index */
683 ret
= git_tree_cache_read_tree(&index
->tree
, tree
, &index
->tree_pool
);
689 int git_treebuilder_new(
690 git_treebuilder
**builder_p
,
691 git_repository
*repo
,
692 const git_tree
*source
)
694 git_treebuilder
*bld
;
697 assert(builder_p
&& repo
);
699 bld
= git__calloc(1, sizeof(git_treebuilder
));
700 GITERR_CHECK_ALLOC(bld
);
704 if (git_strmap_alloc(&bld
->map
) < 0) {
709 if (source
!= NULL
) {
710 git_tree_entry
*entry_src
;
712 git_vector_foreach(&source
->entries
, i
, entry_src
) {
714 bld
, entry_src
->filename
,
716 entry_src
->attr
) < 0)
725 git_treebuilder_free(bld
);
729 static git_otype
otype_from_mode(git_filemode_t filemode
)
732 case GIT_FILEMODE_TREE
:
734 case GIT_FILEMODE_COMMIT
:
735 return GIT_OBJ_COMMIT
;
741 int git_treebuilder_insert(
742 const git_tree_entry
**entry_out
,
743 git_treebuilder
*bld
,
744 const char *filename
,
746 git_filemode_t filemode
)
748 git_tree_entry
*entry
;
752 assert(bld
&& id
&& filename
);
754 if (!valid_filemode(filemode
))
755 return tree_error("Failed to insert entry. Invalid filemode for file", filename
);
757 if (!valid_entry_name(bld
->repo
, filename
))
758 return tree_error("Failed to insert entry. Invalid name for a tree entry", filename
);
760 if (filemode
!= GIT_FILEMODE_COMMIT
&&
761 !git_object__is_valid(bld
->repo
, id
, otype_from_mode(filemode
)))
762 return tree_error("Failed to insert entry; invalid object specified", filename
);
764 pos
= git_strmap_lookup_index(bld
->map
, filename
);
765 if (git_strmap_valid_index(bld
->map
, pos
)) {
766 entry
= git_strmap_value_at(bld
->map
, pos
);
768 entry
= alloc_entry(filename
);
769 GITERR_CHECK_ALLOC(entry
);
771 git_strmap_insert(bld
->map
, entry
->filename
, entry
, error
);
774 git_tree_entry_free(entry
);
775 giterr_set(GITERR_TREE
, "failed to insert %s", filename
);
780 git_oid_cpy(&entry
->oid
, id
);
781 entry
->attr
= filemode
;
789 static git_tree_entry
*treebuilder_get(git_treebuilder
*bld
, const char *filename
)
791 git_tree_entry
*entry
= NULL
;
794 assert(bld
&& filename
);
796 pos
= git_strmap_lookup_index(bld
->map
, filename
);
797 if (git_strmap_valid_index(bld
->map
, pos
))
798 entry
= git_strmap_value_at(bld
->map
, pos
);
803 const git_tree_entry
*git_treebuilder_get(git_treebuilder
*bld
, const char *filename
)
805 return treebuilder_get(bld
, filename
);
808 int git_treebuilder_remove(git_treebuilder
*bld
, const char *filename
)
810 git_tree_entry
*entry
= treebuilder_get(bld
, filename
);
813 return tree_error("Failed to remove entry. File isn't in the tree", filename
);
815 git_strmap_delete(bld
->map
, filename
);
816 git_tree_entry_free(entry
);
821 int git_treebuilder_write(git_oid
*oid
, git_treebuilder
*bld
)
824 size_t i
, entrycount
;
825 git_buf tree
= GIT_BUF_INIT
;
827 git_tree_entry
*entry
;
832 entrycount
= git_strmap_num_entries(bld
->map
);
833 if (git_vector_init(&entries
, entrycount
, entry_sort_cmp
) < 0)
836 git_strmap_foreach_value(bld
->map
, entry
, {
837 if (git_vector_insert(&entries
, entry
) < 0)
841 git_vector_sort(&entries
);
843 /* Grow the buffer beforehand to an estimated size */
844 error
= git_buf_grow(&tree
, entrycount
* 72);
846 for (i
= 0; i
< entries
.length
&& !error
; ++i
) {
847 git_tree_entry
*entry
= git_vector_get(&entries
, i
);
849 git_buf_printf(&tree
, "%o ", entry
->attr
);
850 git_buf_put(&tree
, entry
->filename
, entry
->filename_len
+ 1);
851 git_buf_put(&tree
, (char *)entry
->oid
.id
, GIT_OID_RAWSZ
);
853 if (git_buf_oom(&tree
))
857 git_vector_free(&entries
);
860 !(error
= git_repository_odb__weakptr(&odb
, bld
->repo
)))
861 error
= git_odb_write(oid
, odb
, tree
.ptr
, tree
.size
, GIT_OBJ_TREE
);
867 void git_treebuilder_filter(
868 git_treebuilder
*bld
,
869 git_treebuilder_filter_cb filter
,
872 const char *filename
;
873 git_tree_entry
*entry
;
875 assert(bld
&& filter
);
877 git_strmap_foreach(bld
->map
, filename
, entry
, {
878 if (filter(entry
, payload
)) {
879 git_strmap_delete(bld
->map
, filename
);
880 git_tree_entry_free(entry
);
885 void git_treebuilder_clear(git_treebuilder
*bld
)
891 git_strmap_foreach_value(bld
->map
, e
, git_tree_entry_free(e
));
892 git_strmap_clear(bld
->map
);
895 void git_treebuilder_free(git_treebuilder
*bld
)
900 git_treebuilder_clear(bld
);
901 git_strmap_free(bld
->map
);
905 static size_t subpath_len(const char *path
)
907 const char *slash_pos
= strchr(path
, '/');
908 if (slash_pos
== NULL
)
911 return slash_pos
- path
;
914 int git_tree_entry_bypath(
915 git_tree_entry
**entry_out
,
916 const git_tree
*root
,
921 const git_tree_entry
*entry
;
924 /* Find how long is the current path component (i.e.
925 * the filename between two slashes */
926 filename_len
= subpath_len(path
);
928 if (filename_len
== 0) {
929 giterr_set(GITERR_TREE
, "Invalid tree path given");
930 return GIT_ENOTFOUND
;
933 entry
= entry_fromname(root
, path
, filename_len
);
936 giterr_set(GITERR_TREE
,
937 "the path '%.*s' does not exist in the given tree", filename_len
, path
);
938 return GIT_ENOTFOUND
;
941 switch (path
[filename_len
]) {
943 /* If there are more components in the path...
944 * then this entry *must* be a tree */
945 if (!git_tree_entry__is_tree(entry
)) {
946 giterr_set(GITERR_TREE
,
947 "the path '%.*s' exists but is not a tree", filename_len
, path
);
948 return GIT_ENOTFOUND
;
951 /* If there's only a slash left in the path, we
952 * return the current entry; otherwise, we keep
953 * walking down the path */
954 if (path
[filename_len
+ 1] != '\0')
958 /* If there are no more components in the path, return
960 return git_tree_entry_dup(entry_out
, entry
);
963 if (git_tree_lookup(&subtree
, root
->object
.repo
, &entry
->oid
) < 0)
966 error
= git_tree_entry_bypath(
969 path
+ filename_len
+ 1
972 git_tree_free(subtree
);
976 static int tree_walk(
977 const git_tree
*tree
,
978 git_treewalk_cb callback
,
985 const git_tree_entry
*entry
;
987 git_vector_foreach(&tree
->entries
, i
, entry
) {
989 error
= callback(path
->ptr
, entry
, payload
);
990 if (error
< 0) { /* negative value stops iteration */
991 giterr_set_after_callback_function(error
, "git_tree_walk");
994 if (error
> 0) { /* positive value skips this entry */
1000 if (git_tree_entry__is_tree(entry
)) {
1002 size_t path_len
= git_buf_len(path
);
1004 error
= git_tree_lookup(&subtree
, tree
->object
.repo
, &entry
->oid
);
1008 /* append the next entry to the path */
1009 git_buf_puts(path
, entry
->filename
);
1010 git_buf_putc(path
, '/');
1012 if (git_buf_oom(path
))
1015 error
= tree_walk(subtree
, callback
, path
, payload
, preorder
);
1017 git_tree_free(subtree
);
1021 git_buf_truncate(path
, path_len
);
1025 error
= callback(path
->ptr
, entry
, payload
);
1026 if (error
< 0) { /* negative value stops iteration */
1027 giterr_set_after_callback_function(error
, "git_tree_walk");
1038 const git_tree
*tree
,
1039 git_treewalk_mode mode
,
1040 git_treewalk_cb callback
,
1044 git_buf root_path
= GIT_BUF_INIT
;
1046 if (mode
!= GIT_TREEWALK_POST
&& mode
!= GIT_TREEWALK_PRE
) {
1047 giterr_set(GITERR_INVALID
, "Invalid walking mode for tree walk");
1052 tree
, callback
, &root_path
, payload
, (mode
== GIT_TREEWALK_PRE
));
1054 git_buf_free(&root_path
);