2 * Copyright (C) 2009-2011 the libgit2 contributors
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 #define DEFAULT_TREE_SIZE 16
15 #define MAX_FILEMODE 0777777
16 #define MAX_FILEMODE_BYTES 6
18 static int valid_attributes(const int attributes
)
20 return attributes
>= 0 && attributes
<= MAX_FILEMODE
;
23 static int valid_entry_name(const char *filename
)
25 return strlen(filename
) > 0 && strchr(filename
, '/') == NULL
;
28 static int entry_sort_cmp(const void *a
, const void *b
)
30 const git_tree_entry
*entry_a
= (const git_tree_entry
*)(a
);
31 const git_tree_entry
*entry_b
= (const git_tree_entry
*)(b
);
34 entry_a
->filename
, entry_a
->filename_len
, entry_is_tree(entry_a
),
35 entry_b
->filename
, entry_b
->filename_len
, entry_is_tree(entry_b
));
39 struct tree_key_search
{
44 static int homing_search_cmp(const void *key
, const void *array_member
)
46 const struct tree_key_search
*ksearch
= key
;
47 const git_tree_entry
*entry
= array_member
;
49 const size_t len1
= ksearch
->filename_len
;
50 const size_t len2
= entry
->filename_len
;
55 len1
< len2
? len1
: len2
60 * Search for an entry in a given tree.
62 * Note that this search is performed in two steps because
63 * of the way tree entries are sorted internally in git:
65 * Entries in a tree are not sorted alphabetically; two entries
66 * with the same root prefix will have different positions
67 * depending on whether they are folders (subtrees) or normal files.
69 * Consequently, it is not possible to find an entry on the tree
70 * with a binary search if you don't know whether the filename
71 * you're looking for is a folder or a normal file.
73 * To work around this, we first perform a homing binary search
74 * on the tree, using the minimal length root prefix of our filename.
75 * Once the comparisons for this homing search start becoming
76 * ambiguous because of folder vs file sorting, we look linearly
77 * around the area for our target file.
79 static int tree_key_search(git_vector
*entries
, const char *filename
)
81 struct tree_key_search ksearch
;
82 const git_tree_entry
*entry
;
86 ksearch
.filename
= filename
;
87 ksearch
.filename_len
= strlen(filename
);
89 /* Initial homing search; find an entry on the tree with
90 * the same prefix as the filename we're looking for */
91 homing
= git_vector_bsearch2(entries
, &homing_search_cmp
, &ksearch
);
95 /* We found a common prefix. Look forward as long as
96 * there are entries that share the common prefix */
97 for (i
= homing
; i
< (int)entries
->length
; ++i
) {
98 entry
= entries
->contents
[i
];
100 if (homing_search_cmp(&ksearch
, entry
) != 0)
103 if (strcmp(filename
, entry
->filename
) == 0)
107 /* If we haven't found our filename yet, look backwards
108 * too as long as we have entries with the same prefix */
109 for (i
= homing
- 1; i
>= 0; --i
) {
110 entry
= entries
->contents
[i
];
112 if (homing_search_cmp(&ksearch
, entry
) != 0)
115 if (strcmp(filename
, entry
->filename
) == 0)
119 /* The filename doesn't exist at all */
120 return GIT_ENOTFOUND
;
123 void git_tree__free(git_tree
*tree
)
127 for (i
= 0; i
< tree
->entries
.length
; ++i
) {
129 e
= git_vector_get(&tree
->entries
, i
);
131 git__free(e
->filename
);
135 git_vector_free(&tree
->entries
);
139 const git_oid
*git_tree_id(git_tree
*c
)
141 return git_object_id((git_object
*)c
);
144 unsigned int git_tree_entry_attributes(const git_tree_entry
*entry
)
149 const char *git_tree_entry_name(const git_tree_entry
*entry
)
152 return entry
->filename
;
155 const git_oid
*git_tree_entry_id(const git_tree_entry
*entry
)
161 git_otype
git_tree_entry_type(const git_tree_entry
*entry
)
165 if (S_ISGITLINK(entry
->attr
))
166 return GIT_OBJ_COMMIT
;
167 else if (S_ISDIR(entry
->attr
))
173 int git_tree_entry_2object(git_object
**object_out
, git_repository
*repo
, const git_tree_entry
*entry
)
175 assert(entry
&& object_out
);
176 return git_object_lookup(object_out
, repo
, &entry
->oid
, GIT_OBJ_ANY
);
179 const git_tree_entry
*git_tree_entry_byname(git_tree
*tree
, const char *filename
)
183 assert(tree
&& filename
);
185 idx
= tree_key_search(&tree
->entries
, filename
);
186 if (idx
== GIT_ENOTFOUND
)
189 return git_vector_get(&tree
->entries
, idx
);
192 const git_tree_entry
*git_tree_entry_byindex(git_tree
*tree
, unsigned int idx
)
195 return git_vector_get(&tree
->entries
, idx
);
198 unsigned int git_tree_entrycount(git_tree
*tree
)
201 return tree
->entries
.length
;
204 static int tree_parse_buffer(git_tree
*tree
, const char *buffer
, const char *buffer_end
)
206 int error
= GIT_SUCCESS
;
208 if (git_vector_init(&tree
->entries
, DEFAULT_TREE_SIZE
, entry_sort_cmp
) < GIT_SUCCESS
)
211 while (buffer
< buffer_end
) {
212 git_tree_entry
*entry
;
215 entry
= git__calloc(1, sizeof(git_tree_entry
));
221 if (git_vector_insert(&tree
->entries
, entry
) < GIT_SUCCESS
)
224 if (git__strtol32(&tmp
, buffer
, &buffer
, 8) < GIT_SUCCESS
||
225 !buffer
|| !valid_attributes(tmp
))
226 return git__throw(GIT_EOBJCORRUPTED
, "Failed to parse tree. Can't parse attributes");
230 if (*buffer
++ != ' ') {
231 error
= git__throw(GIT_EOBJCORRUPTED
, "Failed to parse tree. Object it corrupted");
235 if (memchr(buffer
, 0, buffer_end
- buffer
) == NULL
) {
236 error
= git__throw(GIT_EOBJCORRUPTED
, "Failed to parse tree. Object it corrupted");
240 entry
->filename
= git__strdup(buffer
);
241 entry
->filename_len
= strlen(buffer
);
243 while (buffer
< buffer_end
&& *buffer
!= 0)
248 git_oid_fromraw(&entry
->oid
, (const unsigned char *)buffer
);
249 buffer
+= GIT_OID_RAWSZ
;
252 return error
== GIT_SUCCESS
? GIT_SUCCESS
: git__rethrow(error
, "Failed to parse buffer");
255 int git_tree__parse(git_tree
*tree
, git_odb_object
*obj
)
258 return tree_parse_buffer(tree
, (char *)obj
->raw
.data
, (char *)obj
->raw
.data
+ obj
->raw
.len
);
261 static unsigned int find_next_dir(const char *dirname
, git_index
*index
, unsigned int start
)
263 unsigned int i
, entries
= git_index_entrycount(index
);
266 dirlen
= strlen(dirname
);
267 for (i
= start
; i
< entries
; ++i
) {
268 git_index_entry
*entry
= git_index_get(index
, i
);
269 if (strlen(entry
->path
) < dirlen
||
270 memcmp(entry
->path
, dirname
, dirlen
) ||
271 (dirlen
> 0 && entry
->path
[dirlen
] != '/')) {
279 static int append_entry(git_treebuilder
*bld
, const char *filename
, const git_oid
*id
, unsigned int attributes
)
281 git_tree_entry
*entry
;
283 if ((entry
= git__malloc(sizeof(git_tree_entry
))) == NULL
)
286 memset(entry
, 0x0, sizeof(git_tree_entry
));
287 entry
->filename
= git__strdup(filename
);
288 entry
->filename_len
= strlen(entry
->filename
);
290 git_oid_cpy(&entry
->oid
, id
);
291 entry
->attr
= attributes
;
293 if (git_vector_insert(&bld
->entries
, entry
) < 0)
299 static int write_tree(
301 git_repository
*repo
,
306 git_treebuilder
*bld
= NULL
;
308 unsigned int i
, entries
= git_index_entrycount(index
);
310 size_t dirname_len
= strlen(dirname
);
311 const git_tree_cache
*cache
;
313 cache
= git_tree_cache_get(index
->tree
, dirname
);
314 if (cache
!= NULL
&& cache
->entries
>= 0){
315 git_oid_cpy(oid
, &cache
->oid
);
316 return find_next_dir(dirname
, index
, start
);
319 error
= git_treebuilder_create(&bld
, NULL
);
325 * This loop is unfortunate, but necessary. The index doesn't have
326 * any directores, so we need to handle that manually, and we
327 * need to keep track of the current position.
329 for (i
= start
; i
< entries
; ++i
) {
330 git_index_entry
*entry
= git_index_get(index
, i
);
331 char *filename
, *next_slash
;
334 * If we've left our (sub)tree, exit the loop and return. The
335 * first check is an early out (and security for the
336 * third). The second check is a simple prefix comparison. The
337 * third check catches situations where there is a directory
338 * win32/sys and a file win32mmap.c. Without it, the following
339 * code believes there is a file win32/mmap.c
341 if (strlen(entry
->path
) < dirname_len
||
342 memcmp(entry
->path
, dirname
, dirname_len
) ||
343 (dirname_len
> 0 && entry
->path
[dirname_len
] != '/')) {
347 filename
= entry
->path
+ dirname_len
;
348 if (*filename
== '/')
350 next_slash
= strchr(filename
, '/');
354 char *subdir
, *last_comp
;
356 subdir
= git__strndup(entry
->path
, next_slash
- entry
->path
);
357 if (subdir
== NULL
) {
362 /* Write out the subtree */
363 written
= write_tree(&sub_oid
, repo
, index
, subdir
, i
);
365 error
= git__rethrow(written
, "Failed to write subtree %s", subdir
);
367 i
= written
- 1; /* -1 because of the loop increment */
371 * We need to figure out what we want toinsert
372 * into this tree. If we're traversing
373 * deps/zlib/, then we only want to write
374 * 'zlib' into the tree.
376 last_comp
= strrchr(subdir
, '/');
378 last_comp
++; /* Get rid of the '/' */
382 error
= append_entry(bld
, last_comp
, &sub_oid
, S_IFDIR
);
384 if (error
< GIT_SUCCESS
) {
385 error
= git__rethrow(error
, "Failed to insert dir");
389 error
= append_entry(bld
, filename
, &entry
->oid
, entry
->mode
);
390 if (error
< GIT_SUCCESS
) {
391 error
= git__rethrow(error
, "Failed to insert file");
396 error
= git_treebuilder_write(oid
, repo
, bld
);
397 if (error
< GIT_SUCCESS
)
398 error
= git__rethrow(error
, "Failed to write tree to db");
401 git_treebuilder_free(bld
);
403 if (error
< GIT_SUCCESS
)
409 int git_tree_create_fromindex(git_oid
*oid
, git_index
*index
)
411 git_repository
*repo
;
414 repo
= (git_repository
*)GIT_REFCOUNT_OWNER(index
);
417 return git__throw(GIT_EBAREINDEX
,
418 "Failed to create tree. "
419 "The index file is not backed up by an existing repository");
421 if (index
->tree
!= NULL
&& index
->tree
->entries
>= 0) {
422 git_oid_cpy(oid
, &index
->tree
->oid
);
426 /* The tree cache didn't help us */
427 error
= write_tree(oid
, repo
, index
, "", 0);
428 return (error
< GIT_SUCCESS
) ? git__rethrow(error
, "Failed to create tree") : GIT_SUCCESS
;
431 static void sort_entries(git_treebuilder
*bld
)
433 git_vector_sort(&bld
->entries
);
436 int git_treebuilder_create(git_treebuilder
**builder_p
, const git_tree
*source
)
438 git_treebuilder
*bld
;
439 unsigned int i
, source_entries
= DEFAULT_TREE_SIZE
;
443 bld
= git__calloc(1, sizeof(git_treebuilder
));
448 source_entries
= source
->entries
.length
;
450 if (git_vector_init(&bld
->entries
, source_entries
, entry_sort_cmp
) < GIT_SUCCESS
) {
455 if (source
!= NULL
) {
456 for (i
= 0; i
< source
->entries
.length
; ++i
) {
457 git_tree_entry
*entry_src
= source
->entries
.contents
[i
];
459 if (append_entry(bld
, entry_src
->filename
, &entry_src
->oid
, entry_src
->attr
) < 0) {
460 git_treebuilder_free(bld
);
470 int git_treebuilder_insert(git_tree_entry
**entry_out
, git_treebuilder
*bld
, const char *filename
, const git_oid
*id
, unsigned int attributes
)
472 git_tree_entry
*entry
;
475 assert(bld
&& id
&& filename
);
477 if (!valid_attributes(attributes
))
478 return git__throw(GIT_ERROR
, "Failed to insert entry. Invalid attributes");
480 if (!valid_entry_name(filename
))
481 return git__throw(GIT_ERROR
, "Failed to insert entry. Invalid name for a tree entry");
483 pos
= tree_key_search(&bld
->entries
, filename
);
486 entry
= git_vector_get(&bld
->entries
, pos
);
490 if ((entry
= git__malloc(sizeof(git_tree_entry
))) == NULL
)
493 memset(entry
, 0x0, sizeof(git_tree_entry
));
494 entry
->filename
= git__strdup(filename
);
495 entry
->filename_len
= strlen(entry
->filename
);
498 git_oid_cpy(&entry
->oid
, id
);
499 entry
->attr
= attributes
;
501 if (pos
== GIT_ENOTFOUND
) {
502 if (git_vector_insert(&bld
->entries
, entry
) < 0)
506 if (entry_out
!= NULL
)
512 static git_tree_entry
*treebuilder_get(git_treebuilder
*bld
, const char *filename
)
515 git_tree_entry
*entry
;
517 assert(bld
&& filename
);
519 idx
= tree_key_search(&bld
->entries
, filename
);
523 entry
= git_vector_get(&bld
->entries
, idx
);
530 const git_tree_entry
*git_treebuilder_get(git_treebuilder
*bld
, const char *filename
)
532 return treebuilder_get(bld
, filename
);
535 int git_treebuilder_remove(git_treebuilder
*bld
, const char *filename
)
537 git_tree_entry
*remove_ptr
= treebuilder_get(bld
, filename
);
539 if (remove_ptr
== NULL
|| remove_ptr
->removed
)
540 return git__throw(GIT_ENOTFOUND
, "Failed to remove entry. File isn't in the tree");
542 remove_ptr
->removed
= 1;
546 int git_treebuilder_write(git_oid
*oid
, git_repository
*repo
, git_treebuilder
*bld
)
550 git_buf tree
= GIT_BUF_INIT
;
557 /* Grow the buffer beforehand to an estimated size */
558 git_buf_grow(&tree
, bld
->entries
.length
* 72);
560 for (i
= 0; i
< bld
->entries
.length
; ++i
) {
561 git_tree_entry
*entry
= bld
->entries
.contents
[i
];
566 git_buf_printf(&tree
, "%o ", entry
->attr
);
567 git_buf_put(&tree
, entry
->filename
, entry
->filename_len
+ 1);
568 git_buf_put(&tree
, (char *)entry
->oid
.id
, GIT_OID_RAWSZ
);
571 if ((error
= git_buf_lasterror(&tree
)) < GIT_SUCCESS
) {
573 return git__rethrow(error
, "Not enough memory to build the tree data");
576 error
= git_repository_odb__weakptr(&odb
, repo
);
577 if (error
< GIT_SUCCESS
) {
582 error
= git_odb_write(oid
, odb
, tree
.ptr
, tree
.size
, GIT_OBJ_TREE
);
585 return error
== GIT_SUCCESS
? GIT_SUCCESS
: git__rethrow(error
, "Failed to write tree");
588 void git_treebuilder_filter(git_treebuilder
*bld
, int (*filter
)(const git_tree_entry
*, void *), void *payload
)
592 assert(bld
&& filter
);
594 for (i
= 0; i
< bld
->entries
.length
; ++i
) {
595 git_tree_entry
*entry
= bld
->entries
.contents
[i
];
596 if (!entry
->removed
&& filter(entry
, payload
))
601 void git_treebuilder_clear(git_treebuilder
*bld
)
606 for (i
= 0; i
< bld
->entries
.length
; ++i
) {
607 git_tree_entry
*e
= bld
->entries
.contents
[i
];
608 git__free(e
->filename
);
612 git_vector_clear(&bld
->entries
);
615 void git_treebuilder_free(git_treebuilder
*bld
)
617 git_treebuilder_clear(bld
);
618 git_vector_free(&bld
->entries
);
622 static int tree_frompath(
623 git_tree
**parent_out
,
625 git_buf
*treeentry_path
,
628 char *slash_pos
= NULL
;
629 const git_tree_entry
* entry
;
630 int error
= GIT_SUCCESS
;
633 if (!*(treeentry_path
->ptr
+ offset
))
634 return git__rethrow(GIT_EINVALIDPATH
,
635 "Invalid relative path to a tree entry '%s'.", treeentry_path
->ptr
);
637 slash_pos
= (char *)strchr(treeentry_path
->ptr
+ offset
, '/');
639 if (slash_pos
== NULL
)
640 return git_tree_lookup(
643 git_object_id((const git_object
*)root
)
646 if (slash_pos
== treeentry_path
->ptr
+ offset
)
647 return git__rethrow(GIT_EINVALIDPATH
,
648 "Invalid relative path to a tree entry '%s'.", treeentry_path
->ptr
);
652 entry
= git_tree_entry_byname(root
, treeentry_path
->ptr
+ offset
);
654 if (slash_pos
!= NULL
)
658 return git__rethrow(GIT_ENOTFOUND
,
659 "No tree entry can be found from "
660 "the given tree and relative path '%s'.", treeentry_path
->ptr
);
663 error
= git_tree_lookup(&subtree
, root
->object
.repo
, &entry
->oid
);
664 if (error
< GIT_SUCCESS
)
667 error
= tree_frompath(
671 (slash_pos
- treeentry_path
->ptr
) + 1
674 git_tree_free(subtree
);
678 int git_tree_get_subtree(
681 const char *subtree_path
)
684 git_buf buffer
= GIT_BUF_INIT
;
686 assert(subtree
&& root
&& subtree_path
);
688 if ((error
= git_buf_sets(&buffer
, subtree_path
)) == GIT_SUCCESS
)
689 error
= tree_frompath(subtree
, root
, &buffer
, 0);
691 git_buf_free(&buffer
);
696 static int tree_walk_post(
698 git_treewalk_cb callback
,
702 int error
= GIT_SUCCESS
;
705 for (i
= 0; i
< tree
->entries
.length
; ++i
) {
706 git_tree_entry
*entry
= tree
->entries
.contents
[i
];
708 if (callback(path
->ptr
, entry
, payload
) < 0)
711 if (entry_is_tree(entry
)) {
713 size_t path_len
= path
->size
;
715 if ((error
= git_tree_lookup(
716 &subtree
, tree
->object
.repo
, &entry
->oid
)) < 0)
719 /* append the next entry to the path */
720 git_buf_puts(path
, entry
->filename
);
721 git_buf_putc(path
, '/');
722 if ((error
= git_buf_lasterror(path
)) < GIT_SUCCESS
)
725 error
= tree_walk_post(subtree
, callback
, path
, payload
);
726 if (error
< GIT_SUCCESS
)
729 git_buf_truncate(path
, path_len
);
730 git_tree_free(subtree
);
737 int git_tree_walk(git_tree
*tree
, git_treewalk_cb callback
, int mode
, void *payload
)
739 int error
= GIT_SUCCESS
;
740 git_buf root_path
= GIT_BUF_INIT
;
743 case GIT_TREEWALK_POST
:
744 error
= tree_walk_post(tree
, callback
, &root_path
, payload
);
747 case GIT_TREEWALK_PRE
:
748 error
= git__throw(GIT_ENOTIMPLEMENTED
,
749 "Preorder tree walking is still not implemented");
753 error
= git__throw(GIT_EINVALIDARGS
,
754 "Invalid walking mode for tree walk");
758 git_buf_free(&root_path
);
763 static int tree_entry_cmp(const git_tree_entry
*a
, const git_tree_entry
*b
)
767 ret
= a
->attr
- b
->attr
;
771 return git_oid_cmp(&a
->oid
, &b
->oid
);
774 static void mark_del(git_tree_diff_data
*diff
, git_tree_entry
*entry
)
776 diff
->old_attr
= entry
->attr
;
777 git_oid_cpy(&diff
->old_oid
, &entry
->oid
);
778 diff
->path
= entry
->filename
;
779 diff
->status
|= GIT_STATUS_DELETED
;
782 static void mark_add(git_tree_diff_data
*diff
, git_tree_entry
*entry
)
784 diff
->new_attr
= entry
->attr
;
785 git_oid_cpy(&diff
->new_oid
, &entry
->oid
);
786 diff
->path
= entry
->filename
;
787 diff
->status
|= GIT_STATUS_ADDED
;
790 static int signal_additions(git_tree
*tree
, int start
, int end
, git_tree_diff_cb cb
, void *data
)
792 git_tree_diff_data diff
;
793 git_tree_entry
*entry
;
797 end
= git_tree_entrycount(tree
);
799 for (i
= start
; i
< end
; ++i
) {
800 memset(&diff
, 0x0, sizeof(git_tree_diff_data
));
801 entry
= git_vector_get(&tree
->entries
, i
);
802 mark_add(&diff
, entry
);
804 error
= cb(&diff
, data
);
805 if (error
< GIT_SUCCESS
)
812 static int signal_addition(git_tree_entry
*entry
, git_tree_diff_cb cb
, void *data
)
814 git_tree_diff_data diff
;
816 memset(&diff
, 0x0, sizeof(git_tree_diff_data
));
818 mark_add(&diff
, entry
);
820 return cb(&diff
, data
);
823 static int signal_deletions(git_tree
*tree
, int start
, int end
, git_tree_diff_cb cb
, void *data
)
825 git_tree_diff_data diff
;
826 git_tree_entry
*entry
;
830 end
= git_tree_entrycount(tree
);
832 for (i
= start
; i
< end
; ++i
) {
833 memset(&diff
, 0x0, sizeof(git_tree_diff_data
));
834 entry
= git_vector_get(&tree
->entries
, i
);
835 mark_del(&diff
, entry
);
837 error
= cb(&diff
, data
);
838 if (error
< GIT_SUCCESS
)
845 static int signal_deletion(git_tree_entry
*entry
, git_tree_diff_cb cb
, void *data
)
847 git_tree_diff_data diff
;
849 memset(&diff
, 0x0, sizeof(git_tree_diff_data
));
851 mark_del(&diff
, entry
);
853 return cb(&diff
, data
);
856 static int signal_modification(git_tree_entry
*a
, git_tree_entry
*b
,
857 git_tree_diff_cb cb
, void *data
)
859 git_tree_diff_data diff
;
861 memset(&diff
, 0x0, sizeof(git_tree_diff_data
));
866 return cb(&diff
, data
);
869 int git_tree_diff(git_tree
*a
, git_tree
*b
, git_tree_diff_cb cb
, void *data
)
871 unsigned int i_a
= 0, i_b
= 0; /* Counters for trees a and b */
872 git_tree_entry
*entry_a
= NULL
, *entry_b
= NULL
;
873 git_tree_diff_data diff
;
874 int error
= GIT_SUCCESS
, cmp
;
877 entry_a
= a
== NULL
? NULL
: git_vector_get(&a
->entries
, i_a
);
878 entry_b
= b
== NULL
? NULL
: git_vector_get(&b
->entries
, i_b
);
880 if (!entry_a
&& !entry_b
)
883 memset(&diff
, 0x0, sizeof(git_tree_diff_data
));
886 * We've run out of tree on one side so the rest of the
887 * entries on the tree with remaining entries are all
888 * deletions or additions.
890 if (entry_a
&& !entry_b
)
891 return signal_deletions(a
, i_a
, -1, cb
, data
);
892 if (!entry_a
&& entry_b
)
893 return signal_additions(b
, i_b
, -1, cb
, data
);
896 * Both trees are sorted with git's almost-alphabetical
897 * sorting, so a comparison value < 0 means the entry was
898 * deleted in the right tree. > 0 means the entry was added.
900 cmp
= entry_sort_cmp(entry_a
, entry_b
);
906 /* If everything's the same, jump to next pair */
907 if (!tree_entry_cmp(entry_a
, entry_b
))
910 /* If they're not both dirs or both files, it's add + del */
911 if (S_ISDIR(entry_a
->attr
) != S_ISDIR(entry_b
->attr
)) {
912 if ((error
= signal_addition(entry_a
, cb
, data
)) < 0)
914 if ((error
= signal_deletion(entry_b
, cb
, data
)) < 0)
918 /* Otherwise consider it a modification */
919 if ((error
= signal_modification(entry_a
, entry_b
, cb
, data
)) < 0)
922 } else if (cmp
< 0) {
924 if ((error
= signal_deletion(entry_a
, cb
, data
)) < 0)
926 } else if (cmp
> 0) {
928 if ((error
= signal_addition(entry_b
, cb
, data
)) < 0)
937 struct diff_index_cbdata
{
944 static int cmp_tentry_ientry(git_tree_entry
*tentry
, git_index_entry
*ientry
)
948 cmp
= tentry
->attr
- ientry
->mode
;
952 return git_oid_cmp(&tentry
->oid
, &ientry
->oid
);
955 static void make_tentry(git_tree_entry
*tentry
, git_index_entry
*ientry
)
959 memset(tentry
, 0x0, sizeof(git_tree_entry
));
960 tentry
->attr
= ientry
->mode
;
962 last_slash
= strrchr(ientry
->path
, '/');
966 last_slash
= ientry
->path
;
967 tentry
->filename
= last_slash
;
969 git_oid_cpy(&tentry
->oid
, &ientry
->oid
);
970 tentry
->filename_len
= strlen(tentry
->filename
);
973 static int diff_index_cb(const char *root
, git_tree_entry
*tentry
, void *data
)
975 struct diff_index_cbdata
*cbdata
= (struct diff_index_cbdata
*) data
;
976 git_index_entry
*ientry
= git_index_get(cbdata
->index
, cbdata
->i
);
977 git_tree_entry fake_entry
;
978 git_buf fn_buf
= GIT_BUF_INIT
;
979 int cmp
, error
= GIT_SUCCESS
;
981 if (entry_is_tree(tentry
))
984 git_buf_puts(&fn_buf
, root
);
985 git_buf_puts(&fn_buf
, tentry
->filename
);
988 error
= signal_deletion(tentry
, cbdata
->cb
, cbdata
->data
);
989 git_buf_free(&fn_buf
);
993 /* Like with 'git diff-index', the index is the right side*/
994 cmp
= strcmp(git_buf_cstr(&fn_buf
), ientry
->path
);
995 git_buf_free(&fn_buf
);
998 if (!cmp_tentry_ientry(tentry
, ientry
))
1001 make_tentry(&fake_entry
, ientry
);
1002 if ((error
= signal_modification(tentry
, &fake_entry
, cbdata
->cb
, cbdata
->data
)) < 0)
1004 } else if (cmp
< 0) {
1006 memcpy(&fake_entry
, tentry
, sizeof(git_tree_entry
));
1007 if ((error
= signal_deletion(tentry
, cbdata
->cb
, cbdata
->data
)) < 0)
1012 make_tentry(&fake_entry
, ientry
);
1013 if ((error
= signal_addition(&fake_entry
, cbdata
->cb
, cbdata
->data
)) < 0)
1016 * The index has an addition. This means that we need to use
1017 * the next entry in the index without advancing the tree
1018 * walker, so call ourselves with the same tree state.
1020 if ((error
= diff_index_cb(root
, tentry
, data
)) < 0)
1028 int git_tree_diff_index_recursive(git_tree
*tree
, git_index
*index
, git_tree_diff_cb cb
, void *data
)
1030 struct diff_index_cbdata cbdata
;
1031 git_buf dummy_path
= GIT_BUF_INIT
;
1033 cbdata
.index
= index
;
1038 return tree_walk_post(tree
, diff_index_cb
, &dummy_path
, &cbdata
);