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.
13 #include "submodule.h"
16 #define ITERATOR_SET_CB(P,NAME_LC) do { \
17 (P)->cb.current = NAME_LC ## _iterator__current; \
18 (P)->cb.advance = NAME_LC ## _iterator__advance; \
19 (P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \
20 (P)->cb.seek = NAME_LC ## _iterator__seek; \
21 (P)->cb.reset = NAME_LC ## _iterator__reset; \
22 (P)->cb.at_end = NAME_LC ## _iterator__at_end; \
23 (P)->cb.free = NAME_LC ## _iterator__free; \
26 #define ITERATOR_CASE_FLAGS \
27 (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
29 #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
30 (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
31 (P)->base.cb = &(P)->cb; \
32 ITERATOR_SET_CB(P,NAME_LC); \
33 (P)->base.repo = (REPO); \
34 (P)->base.start = start ? git__strdup(start) : NULL; \
35 (P)->base.end = end ? git__strdup(end) : NULL; \
36 if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
37 git__free(P); return -1; } \
38 (P)->base.prefixcomp = git__prefixcmp; \
39 (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \
40 if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
41 (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
44 #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
45 #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
46 #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
47 #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
48 #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
50 #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
51 #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
53 #define iterator__end(I) ((git_iterator *)(I))->end
54 #define iterator__past_end(I,PATH) \
55 (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
58 static int iterator__reset_range(
59 git_iterator
*iter
, const char *start
, const char *end
)
63 git__free(iter
->start
);
64 iter
->start
= git__strdup(start
);
65 GITERR_CHECK_ALLOC(iter
->start
);
71 iter
->end
= git__strdup(end
);
72 GITERR_CHECK_ALLOC(iter
->end
);
75 iter
->flags
&= ~GIT_ITERATOR_FIRST_ACCESS
;
80 static int iterator__update_ignore_case(
82 git_iterator_flag_t flags
)
84 int error
= 0, ignore_case
= -1;
86 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
88 else if ((flags
& GIT_ITERATOR_DONT_IGNORE_CASE
) != 0)
93 if (!(error
= git_repository_index__weakptr(&index
, iter
->repo
)))
94 ignore_case
= (index
->ignore_case
!= false);
98 iter
->flags
= (iter
->flags
| GIT_ITERATOR_IGNORE_CASE
);
99 else if (ignore_case
== 0)
100 iter
->flags
= (iter
->flags
& ~GIT_ITERATOR_IGNORE_CASE
);
102 iter
->prefixcomp
= iterator__ignore_case(iter
) ?
103 git__prefixcmp_icase
: git__prefixcmp
;
108 GIT_INLINE(void) iterator__clear_entry(const git_index_entry
**entry
)
110 if (entry
) *entry
= NULL
;
114 static int empty_iterator__noop(const git_index_entry
**e
, git_iterator
*i
)
117 iterator__clear_entry(e
);
121 static int empty_iterator__seek(git_iterator
*i
, const char *p
)
123 GIT_UNUSED(i
); GIT_UNUSED(p
);
127 static int empty_iterator__reset(git_iterator
*i
, const char *s
, const char *e
)
129 GIT_UNUSED(i
); GIT_UNUSED(s
); GIT_UNUSED(e
);
133 static int empty_iterator__at_end(git_iterator
*i
)
139 static void empty_iterator__free(git_iterator
*i
)
146 git_iterator_callbacks cb
;
149 int git_iterator_for_nothing(
151 git_iterator_flag_t flags
,
155 empty_iterator
*i
= git__calloc(1, sizeof(empty_iterator
));
156 GITERR_CHECK_ALLOC(i
);
158 #define empty_iterator__current empty_iterator__noop
159 #define empty_iterator__advance empty_iterator__noop
160 #define empty_iterator__advance_into empty_iterator__noop
162 ITERATOR_BASE_INIT(i
, empty
, EMPTY
, NULL
);
164 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
165 i
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
167 *iter
= (git_iterator
*)i
;
172 typedef struct tree_iterator_entry tree_iterator_entry
;
173 struct tree_iterator_entry
{
174 tree_iterator_entry
*parent
;
175 const git_tree_entry
*te
;
179 typedef struct tree_iterator_frame tree_iterator_frame
;
180 struct tree_iterator_frame
{
181 tree_iterator_frame
*up
, *down
;
183 size_t n_entries
; /* items in this frame */
184 size_t current
; /* start of currently active range in frame */
185 size_t next
; /* start of next range in frame */
190 tree_iterator_entry
*entries
[GIT_FLEX_ARRAY
];
195 git_iterator_callbacks cb
;
196 tree_iterator_frame
*head
, *root
;
198 git_index_entry entry
;
200 int path_ambiguities
;
201 bool path_has_filename
;
202 bool entry_is_current
;
203 int (*strncomp
)(const char *a
, const char *b
, size_t sz
);
206 static char *tree_iterator__current_filename(
207 tree_iterator
*ti
, const git_tree_entry
*te
)
209 if (!ti
->path_has_filename
) {
210 if (git_buf_joinpath(&ti
->path
, ti
->path
.ptr
, te
->filename
) < 0)
213 if (git_tree_entry__is_tree(te
) && git_buf_putc(&ti
->path
, '/') < 0)
216 ti
->path_has_filename
= true;
222 static void tree_iterator__rewrite_filename(tree_iterator
*ti
)
224 tree_iterator_entry
*scan
= ti
->head
->entries
[ti
->head
->current
];
225 ssize_t strpos
= ti
->path
.size
;
226 const git_tree_entry
*te
;
228 if (strpos
&& ti
->path
.ptr
[strpos
- 1] == '/')
231 for (; scan
&& (te
= scan
->te
); scan
= scan
->parent
) {
232 strpos
-= te
->filename_len
;
233 memcpy(&ti
->path
.ptr
[strpos
], te
->filename
, te
->filename_len
);
234 strpos
-= 1; /* separator */
238 static int tree_iterator__te_cmp(
239 const git_tree_entry
*a
,
240 const git_tree_entry
*b
,
241 int (*compare
)(const char *, const char *, size_t))
244 a
->filename
, a
->filename_len
, a
->attr
== GIT_FILEMODE_TREE
,
245 b
->filename
, b
->filename_len
, b
->attr
== GIT_FILEMODE_TREE
,
249 static int tree_iterator__ci_cmp(const void *a
, const void *b
, void *p
)
251 const tree_iterator_entry
*ae
= a
, *be
= b
;
252 int cmp
= tree_iterator__te_cmp(ae
->te
, be
->te
, git__strncasecmp
);
255 /* stabilize sort order among equivalent names */
256 if (!ae
->parent
->te
|| !be
->parent
->te
)
257 cmp
= tree_iterator__te_cmp(ae
->te
, be
->te
, git__strncmp
);
259 cmp
= tree_iterator__ci_cmp(ae
->parent
, be
->parent
, p
);
265 static int tree_iterator__search_cmp(const void *key
, const void *val
, void *p
)
267 const tree_iterator_frame
*tf
= key
;
268 const git_tree_entry
*te
= ((tree_iterator_entry
*)val
)->te
;
271 tf
->start
, tf
->startlen
, false,
272 te
->filename
, te
->filename_len
, te
->attr
== GIT_FILEMODE_TREE
,
273 ((tree_iterator
*)p
)->strncomp
);
276 static bool tree_iterator__move_to_next(
277 tree_iterator
*ti
, tree_iterator_frame
*tf
)
279 if (tf
->next
> tf
->current
+ 1)
280 ti
->path_ambiguities
--;
282 if (!tf
->up
) { /* at root */
283 tf
->current
= tf
->next
;
287 for (; tf
->current
< tf
->next
; tf
->current
++) {
288 git_tree_free(tf
->entries
[tf
->current
]->tree
);
289 tf
->entries
[tf
->current
]->tree
= NULL
;
292 return (tf
->current
< tf
->n_entries
);
295 static int tree_iterator__set_next(tree_iterator
*ti
, tree_iterator_frame
*tf
)
298 const git_tree_entry
*te
, *last
= NULL
;
300 tf
->next
= tf
->current
;
302 for (; tf
->next
< tf
->n_entries
; tf
->next
++, last
= te
) {
303 te
= tf
->entries
[tf
->next
]->te
;
305 if (last
&& tree_iterator__te_cmp(last
, te
, ti
->strncomp
))
308 /* try to load trees for items in [current,next) range */
309 if (!error
&& git_tree_entry__is_tree(te
))
310 error
= git_tree_lookup(
311 &tf
->entries
[tf
->next
]->tree
, ti
->base
.repo
, &te
->oid
);
314 if (tf
->next
> tf
->current
+ 1)
315 ti
->path_ambiguities
++;
317 /* if a tree lookup failed, advance over this span and return failure */
319 tree_iterator__move_to_next(ti
, tf
);
323 if (last
&& !tree_iterator__current_filename(ti
, last
))
324 return -1; /* must have been allocation failure */
329 GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator
*ti
)
331 return (ti
->head
->current
< ti
->head
->n_entries
&&
332 ti
->head
->entries
[ti
->head
->current
]->tree
!= NULL
);
335 static int tree_iterator__push_frame(tree_iterator
*ti
)
338 tree_iterator_frame
*head
= ti
->head
, *tf
= NULL
;
339 size_t i
, n_entries
= 0, alloclen
;
341 if (head
->current
>= head
->n_entries
|| !head
->entries
[head
->current
]->tree
)
344 for (i
= head
->current
; i
< head
->next
; ++i
)
345 n_entries
+= git_tree_entrycount(head
->entries
[i
]->tree
);
347 GITERR_CHECK_ALLOC_MULTIPLY(&alloclen
, sizeof(tree_iterator_entry
*), n_entries
);
348 GITERR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, sizeof(tree_iterator_frame
));
350 tf
= git__calloc(1, alloclen
);
351 GITERR_CHECK_ALLOC(tf
);
353 tf
->n_entries
= n_entries
;
359 for (i
= head
->current
, n_entries
= 0; i
< head
->next
; ++i
) {
360 git_tree
*tree
= head
->entries
[i
]->tree
;
361 size_t j
, max_j
= git_tree_entrycount(tree
);
363 for (j
= 0; j
< max_j
; ++j
) {
364 tree_iterator_entry
*entry
= git_pool_malloc(&ti
->pool
, 1);
365 GITERR_CHECK_ALLOC(entry
);
367 entry
->parent
= head
->entries
[i
];
368 entry
->te
= git_tree_entry_byindex(tree
, j
);
371 tf
->entries
[n_entries
++] = entry
;
375 /* if ignore_case, sort entries case insensitively */
376 if (iterator__ignore_case(ti
))
378 (void **)tf
->entries
, tf
->n_entries
, tree_iterator__ci_cmp
, tf
);
380 /* pick tf->current based on "start" (or start at zero) */
381 if (head
->startlen
> 0) {
382 git__bsearch_r((void **)tf
->entries
, tf
->n_entries
, head
,
383 tree_iterator__search_cmp
, ti
, &tf
->current
);
385 while (tf
->current
&&
386 !tree_iterator__search_cmp(head
, tf
->entries
[tf
->current
-1], ti
))
389 if ((tf
->start
= strchr(head
->start
, '/')) != NULL
) {
391 tf
->startlen
= strlen(tf
->start
);
395 ti
->path_has_filename
= ti
->entry_is_current
= false;
397 if ((error
= tree_iterator__set_next(ti
, tf
)) < 0)
400 /* autoexpand as needed */
401 if (!iterator__include_trees(ti
) && tree_iterator__at_tree(ti
))
402 return tree_iterator__push_frame(ti
);
407 static bool tree_iterator__pop_frame(tree_iterator
*ti
, bool final
)
409 tree_iterator_frame
*tf
= ti
->head
;
415 ti
->head
->down
= NULL
;
417 tree_iterator__move_to_next(ti
, tf
);
419 if (!final
) { /* if final, don't bother to clean up */
420 git_pool_free_array(&ti
->pool
, tf
->n_entries
, (void **)tf
->entries
);
421 git_buf_rtruncate_at_char(&ti
->path
, '/');
429 static void tree_iterator__pop_all(tree_iterator
*ti
, bool to_end
, bool final
)
431 while (tree_iterator__pop_frame(ti
, final
)) /* pop to root */;
434 ti
->head
->current
= to_end
? ti
->head
->n_entries
: 0;
435 ti
->path_ambiguities
= 0;
436 git_buf_clear(&ti
->path
);
440 static int tree_iterator__update_entry(tree_iterator
*ti
)
442 tree_iterator_frame
*tf
;
443 const git_tree_entry
*te
;
445 if (ti
->entry_is_current
)
449 te
= tf
->entries
[tf
->current
]->te
;
451 ti
->entry
.mode
= te
->attr
;
452 git_oid_cpy(&ti
->entry
.id
, &te
->oid
);
454 ti
->entry
.path
= tree_iterator__current_filename(ti
, te
);
455 GITERR_CHECK_ALLOC(ti
->entry
.path
);
457 if (ti
->path_ambiguities
> 0)
458 tree_iterator__rewrite_filename(ti
);
460 if (iterator__past_end(ti
, ti
->entry
.path
)) {
461 tree_iterator__pop_all(ti
, true, false);
465 ti
->entry_is_current
= true;
470 static int tree_iterator__current(
471 const git_index_entry
**entry
, git_iterator
*self
)
474 tree_iterator
*ti
= (tree_iterator
*)self
;
475 tree_iterator_frame
*tf
= ti
->head
;
477 iterator__clear_entry(entry
);
479 if (tf
->current
>= tf
->n_entries
)
482 if ((error
= tree_iterator__update_entry(ti
)) < 0)
488 ti
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
493 static int tree_iterator__advance_into(
494 const git_index_entry
**entry
, git_iterator
*self
)
497 tree_iterator
*ti
= (tree_iterator
*)self
;
499 iterator__clear_entry(entry
);
501 if (tree_iterator__at_tree(ti
))
502 error
= tree_iterator__push_frame(ti
);
505 error
= tree_iterator__current(entry
, self
);
510 static int tree_iterator__advance(
511 const git_index_entry
**entry
, git_iterator
*self
)
514 tree_iterator
*ti
= (tree_iterator
*)self
;
515 tree_iterator_frame
*tf
= ti
->head
;
517 iterator__clear_entry(entry
);
519 if (tf
->current
>= tf
->n_entries
)
522 if (!iterator__has_been_accessed(ti
))
523 return tree_iterator__current(entry
, self
);
525 if (iterator__do_autoexpand(ti
) && iterator__include_trees(ti
) &&
526 tree_iterator__at_tree(ti
))
527 return tree_iterator__advance_into(entry
, self
);
529 if (ti
->path_has_filename
) {
530 git_buf_rtruncate_at_char(&ti
->path
, '/');
531 ti
->path_has_filename
= ti
->entry_is_current
= false;
534 /* scan forward and up, advancing in frame or popping frame when done */
535 while (!tree_iterator__move_to_next(ti
, tf
) &&
536 tree_iterator__pop_frame(ti
, false))
539 /* find next and load trees */
540 if ((error
= tree_iterator__set_next(ti
, tf
)) < 0)
543 /* deal with include_trees / auto_expand as needed */
544 if (!iterator__include_trees(ti
) && tree_iterator__at_tree(ti
))
545 return tree_iterator__advance_into(entry
, self
);
547 return tree_iterator__current(entry
, self
);
550 static int tree_iterator__seek(git_iterator
*self
, const char *prefix
)
552 GIT_UNUSED(self
); GIT_UNUSED(prefix
);
556 static int tree_iterator__reset(
557 git_iterator
*self
, const char *start
, const char *end
)
559 tree_iterator
*ti
= (tree_iterator
*)self
;
561 tree_iterator__pop_all(ti
, false, false);
563 if (iterator__reset_range(self
, start
, end
) < 0)
566 return tree_iterator__push_frame(ti
); /* re-expand root tree */
569 static int tree_iterator__at_end(git_iterator
*self
)
571 tree_iterator
*ti
= (tree_iterator
*)self
;
572 return (ti
->head
->current
>= ti
->head
->n_entries
);
575 static void tree_iterator__free(git_iterator
*self
)
577 tree_iterator
*ti
= (tree_iterator
*)self
;
579 tree_iterator__pop_all(ti
, true, false);
581 git_tree_free(ti
->head
->entries
[0]->tree
);
583 git_pool_clear(&ti
->pool
);
584 git_buf_free(&ti
->path
);
587 static int tree_iterator__create_root_frame(tree_iterator
*ti
, git_tree
*tree
)
589 size_t sz
= sizeof(tree_iterator_frame
) + sizeof(tree_iterator_entry
);
590 tree_iterator_frame
*root
= git__calloc(sz
, sizeof(char));
591 GITERR_CHECK_ALLOC(root
);
595 root
->start
= ti
->base
.start
;
596 root
->startlen
= root
->start
? strlen(root
->start
) : 0;
597 root
->entries
[0] = git_pool_mallocz(&ti
->pool
, 1);
598 GITERR_CHECK_ALLOC(root
->entries
[0]);
599 root
->entries
[0]->tree
= tree
;
601 ti
->head
= ti
->root
= root
;
606 int git_iterator_for_tree(
609 git_iterator_flag_t flags
,
617 return git_iterator_for_nothing(iter
, flags
, start
, end
);
619 if ((error
= git_object_dup((git_object
**)&tree
, (git_object
*)tree
)) < 0)
622 ti
= git__calloc(1, sizeof(tree_iterator
));
623 GITERR_CHECK_ALLOC(ti
);
625 ITERATOR_BASE_INIT(ti
, tree
, TREE
, git_tree_owner(tree
));
627 if ((error
= iterator__update_ignore_case((git_iterator
*)ti
, flags
)) < 0)
629 ti
->strncomp
= iterator__ignore_case(ti
) ? git__strncasecmp
: git__strncmp
;
631 if ((error
= git_pool_init(&ti
->pool
, sizeof(tree_iterator_entry
),0)) < 0 ||
632 (error
= tree_iterator__create_root_frame(ti
, tree
)) < 0 ||
633 (error
= tree_iterator__push_frame(ti
)) < 0) /* expand root now */
636 *iter
= (git_iterator
*)ti
;
640 git_iterator_free((git_iterator
*)ti
);
647 git_iterator_callbacks cb
;
650 git_vector_cmp entry_srch
;
652 /* when not in autoexpand mode, use these to represent "tree" state */
655 char restore_terminator
;
656 git_index_entry tree_entry
;
659 static const git_index_entry
*index_iterator__index_entry(index_iterator
*ii
)
661 const git_index_entry
*ie
= git_vector_get(&ii
->entries
, ii
->current
);
663 if (ie
!= NULL
&& iterator__past_end(ii
, ie
->path
)) {
664 ii
->current
= git_vector_length(&ii
->entries
);
671 static const git_index_entry
*index_iterator__skip_conflicts(index_iterator
*ii
)
673 const git_index_entry
*ie
;
675 while ((ie
= index_iterator__index_entry(ii
)) != NULL
&&
676 git_index_entry_stage(ie
) != 0)
682 static void index_iterator__next_prefix_tree(index_iterator
*ii
)
686 if (!iterator__include_trees(ii
))
689 slash
= strchr(&ii
->partial
.ptr
[ii
->partial_pos
], '/');
692 ii
->partial_pos
= (slash
- ii
->partial
.ptr
) + 1;
693 ii
->restore_terminator
= ii
->partial
.ptr
[ii
->partial_pos
];
694 ii
->partial
.ptr
[ii
->partial_pos
] = '\0';
696 ii
->partial_pos
= ii
->partial
.size
;
699 if (index_iterator__index_entry(ii
) == NULL
)
700 ii
->partial_pos
= ii
->partial
.size
;
703 static int index_iterator__first_prefix_tree(index_iterator
*ii
)
705 const git_index_entry
*ie
= index_iterator__skip_conflicts(ii
);
706 const char *scan
, *prior
, *slash
;
708 if (!ie
|| !iterator__include_trees(ii
))
711 /* find longest common prefix with prior index entry */
712 for (scan
= slash
= ie
->path
, prior
= ii
->partial
.ptr
;
713 *scan
&& *scan
== *prior
; ++scan
, ++prior
)
717 if (git_buf_sets(&ii
->partial
, ie
->path
) < 0)
720 ii
->partial_pos
= (slash
- ie
->path
) + 1;
721 index_iterator__next_prefix_tree(ii
);
726 #define index_iterator__at_tree(I) \
727 (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
729 static int index_iterator__current(
730 const git_index_entry
**entry
, git_iterator
*self
)
732 index_iterator
*ii
= (index_iterator
*)self
;
733 const git_index_entry
*ie
= git_vector_get(&ii
->entries
, ii
->current
);
735 if (ie
!= NULL
&& index_iterator__at_tree(ii
)) {
736 ii
->tree_entry
.path
= ii
->partial
.ptr
;
737 ie
= &ii
->tree_entry
;
743 ii
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
745 return (ie
!= NULL
) ? 0 : GIT_ITEROVER
;
748 static int index_iterator__at_end(git_iterator
*self
)
750 index_iterator
*ii
= (index_iterator
*)self
;
751 return (ii
->current
>= git_vector_length(&ii
->entries
));
754 static int index_iterator__advance(
755 const git_index_entry
**entry
, git_iterator
*self
)
757 index_iterator
*ii
= (index_iterator
*)self
;
758 size_t entrycount
= git_vector_length(&ii
->entries
);
759 const git_index_entry
*ie
;
761 if (!iterator__has_been_accessed(ii
))
762 return index_iterator__current(entry
, self
);
764 if (index_iterator__at_tree(ii
)) {
765 if (iterator__do_autoexpand(ii
)) {
766 ii
->partial
.ptr
[ii
->partial_pos
] = ii
->restore_terminator
;
767 index_iterator__next_prefix_tree(ii
);
769 /* advance to sibling tree (i.e. find entry with new prefix) */
770 while (ii
->current
< entrycount
) {
773 if (!(ie
= git_vector_get(&ii
->entries
, ii
->current
)) ||
774 ii
->base
.prefixcomp(ie
->path
, ii
->partial
.ptr
) != 0)
778 if (index_iterator__first_prefix_tree(ii
) < 0)
782 if (ii
->current
< entrycount
)
785 if (index_iterator__first_prefix_tree(ii
) < 0)
789 return index_iterator__current(entry
, self
);
792 static int index_iterator__advance_into(
793 const git_index_entry
**entry
, git_iterator
*self
)
795 index_iterator
*ii
= (index_iterator
*)self
;
796 const git_index_entry
*ie
= git_vector_get(&ii
->entries
, ii
->current
);
798 if (ie
!= NULL
&& index_iterator__at_tree(ii
)) {
799 if (ii
->restore_terminator
)
800 ii
->partial
.ptr
[ii
->partial_pos
] = ii
->restore_terminator
;
801 index_iterator__next_prefix_tree(ii
);
804 return index_iterator__current(entry
, self
);
807 static int index_iterator__seek(git_iterator
*self
, const char *prefix
)
809 GIT_UNUSED(self
); GIT_UNUSED(prefix
);
813 static int index_iterator__reset(
814 git_iterator
*self
, const char *start
, const char *end
)
816 index_iterator
*ii
= (index_iterator
*)self
;
817 const git_index_entry
*ie
;
819 if (iterator__reset_range(self
, start
, end
) < 0)
825 git_index_snapshot_find(
826 &ii
->current
, &ii
->entries
, ii
->entry_srch
, ii
->base
.start
, 0, 0);
828 if ((ie
= index_iterator__skip_conflicts(ii
)) == NULL
)
831 if (git_buf_sets(&ii
->partial
, ie
->path
) < 0)
836 if (ii
->base
.start
) {
837 size_t startlen
= strlen(ii
->base
.start
);
839 ii
->partial_pos
= (startlen
> ii
->partial
.size
) ?
840 ii
->partial
.size
: startlen
;
843 index_iterator__next_prefix_tree(ii
);
848 static void index_iterator__free(git_iterator
*self
)
850 index_iterator
*ii
= (index_iterator
*)self
;
851 git_index_snapshot_release(&ii
->entries
, ii
->index
);
853 git_buf_free(&ii
->partial
);
856 int git_iterator_for_index(
859 git_iterator_flag_t flags
,
864 index_iterator
*ii
= git__calloc(1, sizeof(index_iterator
));
865 GITERR_CHECK_ALLOC(ii
);
867 if ((error
= git_index_snapshot_new(&ii
->entries
, index
)) < 0) {
873 ITERATOR_BASE_INIT(ii
, index
, INDEX
, git_index_owner(index
));
875 if ((error
= iterator__update_ignore_case((git_iterator
*)ii
, flags
)) < 0) {
876 git_iterator_free((git_iterator
*)ii
);
880 ii
->entry_srch
= iterator__ignore_case(ii
) ?
881 git_index_entry_isrch
: git_index_entry_srch
;
883 git_vector_set_cmp(&ii
->entries
, iterator__ignore_case(ii
) ?
884 git_index_entry_icmp
: git_index_entry_cmp
);
885 git_vector_sort(&ii
->entries
);
887 git_buf_init(&ii
->partial
, 0);
888 ii
->tree_entry
.mode
= GIT_FILEMODE_TREE
;
890 index_iterator__reset((git_iterator
*)ii
, NULL
, NULL
);
892 *iter
= (git_iterator
*)ii
;
897 typedef struct fs_iterator_frame fs_iterator_frame
;
898 struct fs_iterator_frame
{
899 fs_iterator_frame
*next
;
905 typedef struct fs_iterator fs_iterator
;
908 git_iterator_callbacks cb
;
909 fs_iterator_frame
*stack
;
910 git_index_entry entry
;
913 uint32_t dirload_flags
;
916 int (*enter_dir_cb
)(fs_iterator
*self
);
917 int (*leave_dir_cb
)(fs_iterator
*self
);
918 int (*update_entry_cb
)(fs_iterator
*self
);
921 #define FS_MAX_DEPTH 100
923 static fs_iterator_frame
*fs_iterator__alloc_frame(fs_iterator
*fi
)
925 fs_iterator_frame
*ff
= git__calloc(1, sizeof(fs_iterator_frame
));
926 git_vector_cmp entry_compare
= CASESELECT(
927 iterator__ignore_case(fi
),
928 git_path_with_stat_cmp_icase
, git_path_with_stat_cmp
);
930 if (ff
&& git_vector_init(&ff
->entries
, 0, entry_compare
) < 0) {
938 static void fs_iterator__free_frame(fs_iterator_frame
*ff
)
940 git_vector_free_deep(&ff
->entries
);
944 static void fs_iterator__pop_frame(
945 fs_iterator
*fi
, fs_iterator_frame
*ff
, bool pop_last
)
947 if (fi
&& fi
->stack
== ff
) {
948 if (!ff
->next
&& !pop_last
) {
949 memset(&fi
->entry
, 0, sizeof(fi
->entry
));
953 if (fi
->leave_dir_cb
)
954 (void)fi
->leave_dir_cb(fi
);
956 fi
->stack
= ff
->next
;
960 fs_iterator__free_frame(ff
);
963 static int fs_iterator__update_entry(fs_iterator
*fi
);
964 static int fs_iterator__advance_over(
965 const git_index_entry
**entry
, git_iterator
*self
);
967 static int fs_iterator__entry_cmp(const void *i
, const void *item
)
969 const fs_iterator
*fi
= (const fs_iterator
*)i
;
970 const git_path_with_stat
*ps
= item
;
971 return fi
->base
.prefixcomp(fi
->base
.start
, ps
->path
);
974 static void fs_iterator__seek_frame_start(
975 fs_iterator
*fi
, fs_iterator_frame
*ff
)
982 &ff
->index
, &ff
->entries
, fs_iterator__entry_cmp
, fi
);
987 static int fs_iterator__expand_dir(fs_iterator
*fi
)
990 fs_iterator_frame
*ff
;
992 if (fi
->depth
> FS_MAX_DEPTH
) {
993 giterr_set(GITERR_REPOSITORY
,
994 "Directory nesting is too deep (%d)", fi
->depth
);
998 ff
= fs_iterator__alloc_frame(fi
);
999 GITERR_CHECK_ALLOC(ff
);
1001 error
= git_path_dirload_with_stat(
1002 fi
->path
.ptr
, fi
->root_len
, fi
->dirload_flags
,
1003 fi
->base
.start
, fi
->base
.end
, &ff
->entries
);
1006 git_error_state last_error
= { 0 };
1007 giterr_capture(&last_error
, error
);
1009 /* these callbacks may clear the error message */
1010 fs_iterator__free_frame(ff
);
1011 fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1012 /* next time return value we skipped to */
1013 fi
->base
.flags
&= ~GIT_ITERATOR_FIRST_ACCESS
;
1015 return giterr_restore(&last_error
);
1018 if (ff
->entries
.length
== 0) {
1019 fs_iterator__free_frame(ff
);
1020 return GIT_ENOTFOUND
;
1022 fi
->base
.stat_calls
+= ff
->entries
.length
;
1024 fs_iterator__seek_frame_start(fi
, ff
);
1026 ff
->next
= fi
->stack
;
1030 if (fi
->enter_dir_cb
&& (error
= fi
->enter_dir_cb(fi
)) < 0)
1033 return fs_iterator__update_entry(fi
);
1036 static int fs_iterator__current(
1037 const git_index_entry
**entry
, git_iterator
*self
)
1039 fs_iterator
*fi
= (fs_iterator
*)self
;
1040 const git_index_entry
*fe
= (fi
->entry
.path
== NULL
) ? NULL
: &fi
->entry
;
1045 fi
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
1047 return (fe
!= NULL
) ? 0 : GIT_ITEROVER
;
1050 static int fs_iterator__at_end(git_iterator
*self
)
1052 return (((fs_iterator
*)self
)->entry
.path
== NULL
);
1055 static int fs_iterator__advance_into(
1056 const git_index_entry
**entry
, git_iterator
*iter
)
1059 fs_iterator
*fi
= (fs_iterator
*)iter
;
1061 iterator__clear_entry(entry
);
1063 /* Allow you to explicitly advance into a commit/submodule (as well as a
1064 * tree) to avoid cases where an entry is mislabeled as a submodule in
1065 * the working directory. The fs iterator will never have COMMMIT
1066 * entries on it's own, but a wrapper might add them.
1068 if (fi
->entry
.path
!= NULL
&&
1069 (fi
->entry
.mode
== GIT_FILEMODE_TREE
||
1070 fi
->entry
.mode
== GIT_FILEMODE_COMMIT
))
1071 /* returns GIT_ENOTFOUND if the directory is empty */
1072 error
= fs_iterator__expand_dir(fi
);
1074 if (!error
&& entry
)
1075 error
= fs_iterator__current(entry
, iter
);
1077 if (!error
&& !fi
->entry
.path
)
1078 error
= GIT_ITEROVER
;
1083 static int fs_iterator__advance_over(
1084 const git_index_entry
**entry
, git_iterator
*self
)
1087 fs_iterator
*fi
= (fs_iterator
*)self
;
1088 fs_iterator_frame
*ff
;
1089 git_path_with_stat
*next
;
1094 while (fi
->entry
.path
!= NULL
) {
1096 next
= git_vector_get(&ff
->entries
, ++ff
->index
);
1101 fs_iterator__pop_frame(fi
, ff
, false);
1104 error
= fs_iterator__update_entry(fi
);
1106 if (!error
&& entry
!= NULL
)
1107 error
= fs_iterator__current(entry
, self
);
1112 static int fs_iterator__advance(
1113 const git_index_entry
**entry
, git_iterator
*self
)
1115 fs_iterator
*fi
= (fs_iterator
*)self
;
1117 if (!iterator__has_been_accessed(fi
))
1118 return fs_iterator__current(entry
, self
);
1120 /* given include_trees & autoexpand, we might have to go into a tree */
1121 if (iterator__do_autoexpand(fi
) &&
1122 fi
->entry
.path
!= NULL
&&
1123 fi
->entry
.mode
== GIT_FILEMODE_TREE
)
1125 int error
= fs_iterator__advance_into(entry
, self
);
1126 if (error
!= GIT_ENOTFOUND
)
1128 /* continue silently past empty directories if autoexpanding */
1132 return fs_iterator__advance_over(entry
, self
);
1135 static int fs_iterator__seek(git_iterator
*self
, const char *prefix
)
1139 /* pop stack until matching prefix */
1140 /* find prefix item in current frame */
1141 /* push subdirectories as deep as possible while matching */
1145 static int fs_iterator__reset(
1146 git_iterator
*self
, const char *start
, const char *end
)
1149 fs_iterator
*fi
= (fs_iterator
*)self
;
1151 while (fi
->stack
!= NULL
&& fi
->stack
->next
!= NULL
)
1152 fs_iterator__pop_frame(fi
, fi
->stack
, false);
1155 if ((error
= iterator__reset_range(self
, start
, end
)) < 0)
1158 fs_iterator__seek_frame_start(fi
, fi
->stack
);
1160 error
= fs_iterator__update_entry(fi
);
1161 if (error
== GIT_ITEROVER
)
1167 static void fs_iterator__free(git_iterator
*self
)
1169 fs_iterator
*fi
= (fs_iterator
*)self
;
1171 while (fi
->stack
!= NULL
)
1172 fs_iterator__pop_frame(fi
, fi
->stack
, true);
1174 git_buf_free(&fi
->path
);
1177 static int fs_iterator__update_entry(fs_iterator
*fi
)
1179 git_path_with_stat
*ps
;
1181 memset(&fi
->entry
, 0, sizeof(fi
->entry
));
1184 return GIT_ITEROVER
;
1186 ps
= git_vector_get(&fi
->stack
->entries
, fi
->stack
->index
);
1188 return GIT_ITEROVER
;
1190 git_buf_truncate(&fi
->path
, fi
->root_len
);
1191 if (git_buf_put(&fi
->path
, ps
->path
, ps
->path_len
) < 0)
1194 if (iterator__past_end(fi
, fi
->path
.ptr
+ fi
->root_len
))
1195 return GIT_ITEROVER
;
1197 fi
->entry
.path
= ps
->path
;
1198 git_index_entry__init_from_stat(&fi
->entry
, &ps
->st
, true);
1200 /* need different mode here to keep directories during iteration */
1201 fi
->entry
.mode
= git_futils_canonical_mode(ps
->st
.st_mode
);
1203 /* allow wrapper to check/update the entry (can force skip) */
1204 if (fi
->update_entry_cb
&&
1205 fi
->update_entry_cb(fi
) == GIT_ENOTFOUND
)
1206 return fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1208 /* if this is a tree and trees aren't included, then skip */
1209 if (fi
->entry
.mode
== GIT_FILEMODE_TREE
&& !iterator__include_trees(fi
)) {
1210 int error
= fs_iterator__advance_into(NULL
, (git_iterator
*)fi
);
1211 if (error
!= GIT_ENOTFOUND
)
1214 return fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1220 static int fs_iterator__initialize(
1221 git_iterator
**out
, fs_iterator
*fi
, const char *root
)
1225 if (git_buf_sets(&fi
->path
, root
) < 0 || git_path_to_dir(&fi
->path
) < 0) {
1229 fi
->root_len
= fi
->path
.size
;
1232 (iterator__ignore_case(fi
) ? GIT_PATH_DIR_IGNORE_CASE
: 0) |
1233 (iterator__flag(fi
, PRECOMPOSE_UNICODE
) ?
1234 GIT_PATH_DIR_PRECOMPOSE_UNICODE
: 0);
1236 if ((error
= fs_iterator__expand_dir(fi
)) < 0) {
1237 if (error
== GIT_ENOTFOUND
|| error
== GIT_ITEROVER
) {
1241 git_iterator_free((git_iterator
*)fi
);
1246 *out
= (git_iterator
*)fi
;
1250 int git_iterator_for_filesystem(
1253 git_iterator_flag_t flags
,
1257 fs_iterator
*fi
= git__calloc(1, sizeof(fs_iterator
));
1258 GITERR_CHECK_ALLOC(fi
);
1260 ITERATOR_BASE_INIT(fi
, fs
, FS
, NULL
);
1262 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
1263 fi
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
1265 return fs_iterator__initialize(out
, fi
, root
);
1271 git_ignores ignores
;
1275 * We may have a tree or the index+snapshot to compare against
1276 * when checking for submodules.
1280 git_vector index_snapshot
;
1281 git_vector_cmp entry_srch
;
1285 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf
*path
)
1289 if (!path
|| (len
= path
->size
) < 4)
1292 if (path
->ptr
[len
- 1] == '/')
1295 if (tolower(path
->ptr
[len
- 1]) != 't' ||
1296 tolower(path
->ptr
[len
- 2]) != 'i' ||
1297 tolower(path
->ptr
[len
- 3]) != 'g' ||
1298 tolower(path
->ptr
[len
- 4]) != '.')
1301 return (len
== 4 || path
->ptr
[len
- 5] == '/');
1305 * Figure out if an entry is a submodule.
1307 * We consider it a submodule if the path is listed as a submodule in
1308 * either the tree or the index.
1310 static int is_submodule(workdir_iterator
*wi
, git_path_with_stat
*ie
)
1312 int error
, is_submodule
= 0;
1317 /* remove the trailing slash for finding */
1318 ie
->path
[ie
->path_len
-1] = '\0';
1319 error
= git_tree_entry_bypath(&e
, wi
->tree
, ie
->path
);
1320 ie
->path
[ie
->path_len
-1] = '/';
1321 if (error
< 0 && error
!= GIT_ENOTFOUND
)
1324 is_submodule
= e
->attr
== GIT_FILEMODE_COMMIT
;
1325 git_tree_entry_free(e
);
1329 if (!is_submodule
&& wi
->index
) {
1333 error
= git_index_snapshot_find(&pos
, &wi
->index_snapshot
, wi
->entry_srch
, ie
->path
, ie
->path_len
-1, 0);
1334 if (error
< 0 && error
!= GIT_ENOTFOUND
)
1338 e
= git_vector_get(&wi
->index_snapshot
, pos
);
1340 is_submodule
= e
->mode
== GIT_FILEMODE_COMMIT
;
1344 return is_submodule
;
1347 static int workdir_iterator__enter_dir(fs_iterator
*fi
)
1349 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1350 fs_iterator_frame
*ff
= fi
->stack
;
1352 git_path_with_stat
*entry
;
1353 bool found_submodules
= false;
1355 /* check if this directory is ignored */
1356 if (git_ignore__lookup(
1357 &ff
->is_ignored
, &wi
->ignores
, fi
->path
.ptr
+ fi
->root_len
) < 0) {
1359 ff
->is_ignored
= GIT_IGNORE_NOTFOUND
;
1362 /* if this is not the top level directory... */
1363 if (ff
->next
!= NULL
) {
1364 ssize_t slash_pos
= git_buf_rfind_next(&fi
->path
, '/');
1366 /* inherit ignored from parent if no rule specified */
1367 if (ff
->is_ignored
<= GIT_IGNORE_NOTFOUND
)
1368 ff
->is_ignored
= ff
->next
->is_ignored
;
1370 /* push new ignores for files in this directory */
1371 (void)git_ignore__push_dir(&wi
->ignores
, &fi
->path
.ptr
[slash_pos
+ 1]);
1374 /* convert submodules to GITLINK and remove trailing slashes */
1375 git_vector_foreach(&ff
->entries
, pos
, entry
) {
1376 if (!S_ISDIR(entry
->st
.st_mode
) || !strcmp(GIT_DIR
, entry
->path
))
1379 if (is_submodule(wi
, entry
)) {
1380 entry
->st
.st_mode
= GIT_FILEMODE_COMMIT
;
1382 entry
->path
[entry
->path_len
] = '\0';
1383 found_submodules
= true;
1387 /* if we renamed submodules, re-sort and re-seek to start */
1388 if (found_submodules
) {
1389 git_vector_set_sorted(&ff
->entries
, 0);
1390 git_vector_sort(&ff
->entries
);
1391 fs_iterator__seek_frame_start(fi
, ff
);
1397 static int workdir_iterator__leave_dir(fs_iterator
*fi
)
1399 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1400 git_ignore__pop_dir(&wi
->ignores
);
1404 static int workdir_iterator__update_entry(fs_iterator
*fi
)
1406 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1408 /* skip over .git entries */
1409 if (workdir_path_is_dotgit(&fi
->path
))
1410 return GIT_ENOTFOUND
;
1412 /* reset is_ignored since we haven't checked yet */
1413 wi
->is_ignored
= GIT_IGNORE_UNCHECKED
;
1418 static void workdir_iterator__free(git_iterator
*self
)
1420 workdir_iterator
*wi
= (workdir_iterator
*)self
;
1422 git_index_snapshot_release(&wi
->index_snapshot
, wi
->index
);
1423 git_tree_free(wi
->tree
);
1424 fs_iterator__free(self
);
1425 git_ignore__free(&wi
->ignores
);
1428 int git_iterator_for_workdir_ext(
1430 git_repository
*repo
,
1431 const char *repo_workdir
,
1434 git_iterator_flag_t flags
,
1438 int error
, precompose
= 0;
1439 workdir_iterator
*wi
;
1441 if (!repo_workdir
) {
1442 if (git_repository__ensure_not_bare(repo
, "scan working directory") < 0)
1443 return GIT_EBAREREPO
;
1444 repo_workdir
= git_repository_workdir(repo
);
1447 /* initialize as an fs iterator then do overrides */
1448 wi
= git__calloc(1, sizeof(workdir_iterator
));
1449 GITERR_CHECK_ALLOC(wi
);
1450 ITERATOR_BASE_INIT((&wi
->fi
), fs
, FS
, repo
);
1452 wi
->fi
.base
.type
= GIT_ITERATOR_TYPE_WORKDIR
;
1453 wi
->fi
.cb
.free
= workdir_iterator__free
;
1454 wi
->fi
.enter_dir_cb
= workdir_iterator__enter_dir
;
1455 wi
->fi
.leave_dir_cb
= workdir_iterator__leave_dir
;
1456 wi
->fi
.update_entry_cb
= workdir_iterator__update_entry
;
1458 if ((error
= iterator__update_ignore_case((git_iterator
*)wi
, flags
)) < 0 ||
1459 (error
= git_ignore__for_path(repo
, ".gitignore", &wi
->ignores
)) < 0)
1461 git_iterator_free((git_iterator
*)wi
);
1465 if (tree
&& (error
= git_object_dup((git_object
**)&wi
->tree
, (git_object
*)tree
)) < 0)
1469 if (index
&& (error
= git_index_snapshot_new(&wi
->index_snapshot
, index
)) < 0) {
1470 git_iterator_free((git_iterator
*)wi
);
1473 wi
->entry_srch
= iterator__ignore_case(wi
) ?
1474 git_index_entry_isrch
: git_index_entry_srch
;
1477 /* try to look up precompose and set flag if appropriate */
1478 if (git_repository__cvar(&precompose
, repo
, GIT_CVAR_PRECOMPOSE
) < 0)
1480 else if (precompose
)
1481 wi
->fi
.base
.flags
|= GIT_ITERATOR_PRECOMPOSE_UNICODE
;
1483 return fs_iterator__initialize(out
, &wi
->fi
, repo_workdir
);
1487 void git_iterator_free(git_iterator
*iter
)
1492 iter
->cb
->free(iter
);
1494 git__free(iter
->start
);
1495 git__free(iter
->end
);
1497 memset(iter
, 0, sizeof(*iter
));
1502 int git_iterator_set_ignore_case(git_iterator
*iter
, bool ignore_case
)
1504 bool desire_ignore_case
= (ignore_case
!= 0);
1506 if (iterator__ignore_case(iter
) == desire_ignore_case
)
1509 if (iter
->type
== GIT_ITERATOR_TYPE_EMPTY
) {
1510 if (desire_ignore_case
)
1511 iter
->flags
|= GIT_ITERATOR_IGNORE_CASE
;
1513 iter
->flags
&= ~GIT_ITERATOR_IGNORE_CASE
;
1515 giterr_set(GITERR_INVALID
,
1516 "Cannot currently set ignore case on non-empty iterators");
1523 git_index
*git_iterator_get_index(git_iterator
*iter
)
1525 if (iter
->type
== GIT_ITERATOR_TYPE_INDEX
)
1526 return ((index_iterator
*)iter
)->index
;
1530 int git_iterator_current_tree_entry(
1531 const git_tree_entry
**tree_entry
, git_iterator
*iter
)
1533 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
)
1536 tree_iterator_frame
*tf
= ((tree_iterator
*)iter
)->head
;
1537 *tree_entry
= (tf
->current
< tf
->n_entries
) ?
1538 tf
->entries
[tf
->current
]->te
: NULL
;
1544 int git_iterator_current_parent_tree(
1545 const git_tree
**tree_ptr
,
1547 const char *parent_path
)
1549 tree_iterator
*ti
= (tree_iterator
*)iter
;
1550 tree_iterator_frame
*tf
;
1551 const char *scan
= parent_path
;
1552 const git_tree_entry
*te
;
1556 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
)
1559 for (tf
= ti
->root
; *scan
; ) {
1560 if (!(tf
= tf
->down
) ||
1561 tf
->current
>= tf
->n_entries
||
1562 !(te
= tf
->entries
[tf
->current
]->te
) ||
1563 ti
->strncomp(scan
, te
->filename
, te
->filename_len
) != 0)
1566 scan
+= te
->filename_len
;
1571 *tree_ptr
= tf
->entries
[tf
->current
]->tree
;
1575 static void workdir_iterator_update_is_ignored(workdir_iterator
*wi
)
1577 if (git_ignore__lookup(
1578 &wi
->is_ignored
, &wi
->ignores
, wi
->fi
.entry
.path
) < 0) {
1580 wi
->is_ignored
= GIT_IGNORE_NOTFOUND
;
1583 /* use ignore from containing frame stack */
1584 if (wi
->is_ignored
<= GIT_IGNORE_NOTFOUND
)
1585 wi
->is_ignored
= wi
->fi
.stack
->is_ignored
;
1588 bool git_iterator_current_is_ignored(git_iterator
*iter
)
1590 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1592 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1595 if (wi
->is_ignored
!= GIT_IGNORE_UNCHECKED
)
1596 return (bool)(wi
->is_ignored
== GIT_IGNORE_TRUE
);
1598 workdir_iterator_update_is_ignored(wi
);
1600 return (bool)(wi
->is_ignored
== GIT_IGNORE_TRUE
);
1603 bool git_iterator_current_tree_is_ignored(git_iterator
*iter
)
1605 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1607 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1610 return (bool)(wi
->fi
.stack
->is_ignored
== GIT_IGNORE_TRUE
);
1613 int git_iterator_cmp(git_iterator
*iter
, const char *path_prefix
)
1615 const git_index_entry
*entry
;
1617 /* a "done" iterator is after every prefix */
1618 if (git_iterator_current(&entry
, iter
) < 0 || entry
== NULL
)
1621 /* a NULL prefix is after any valid iterator */
1625 return iter
->prefixcomp(entry
->path
, path_prefix
);
1628 int git_iterator_current_workdir_path(git_buf
**path
, git_iterator
*iter
)
1630 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1632 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
|| !wi
->fi
.entry
.path
)
1635 *path
= &wi
->fi
.path
;
1640 int git_iterator_advance_over_with_status(
1641 const git_index_entry
**entryptr
,
1642 git_iterator_status_t
*status
,
1646 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1648 const git_index_entry
*entry
;
1650 *status
= GIT_ITERATOR_STATUS_NORMAL
;
1652 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1653 return git_iterator_advance(entryptr
, iter
);
1654 if ((error
= git_iterator_current(&entry
, iter
)) < 0)
1657 if (!S_ISDIR(entry
->mode
)) {
1658 workdir_iterator_update_is_ignored(wi
);
1659 if (wi
->is_ignored
== GIT_IGNORE_TRUE
)
1660 *status
= GIT_ITERATOR_STATUS_IGNORED
;
1661 return git_iterator_advance(entryptr
, iter
);
1664 *status
= GIT_ITERATOR_STATUS_EMPTY
;
1666 base
= git__strdup(entry
->path
);
1667 GITERR_CHECK_ALLOC(base
);
1669 /* scan inside directory looking for a non-ignored item */
1670 while (entry
&& !iter
->prefixcomp(entry
->path
, base
)) {
1671 workdir_iterator_update_is_ignored(wi
);
1673 /* if we found an explicitly ignored item, then update from
1676 if (wi
->is_ignored
== GIT_IGNORE_TRUE
)
1677 *status
= GIT_ITERATOR_STATUS_IGNORED
;
1678 else if (S_ISDIR(entry
->mode
)) {
1679 error
= git_iterator_advance_into(&entry
, iter
);
1683 else if (error
== GIT_ENOTFOUND
) {
1685 wi
->is_ignored
= GIT_IGNORE_TRUE
; /* mark empty dirs ignored */
1687 break; /* real error, stop here */
1689 /* we found a non-ignored item, treat parent as untracked */
1690 *status
= GIT_ITERATOR_STATUS_NORMAL
;
1694 if ((error
= git_iterator_advance(&entry
, iter
)) < 0)
1698 /* wrap up scan back to base directory */
1699 while (entry
&& !iter
->prefixcomp(entry
->path
, base
))
1700 if ((error
= git_iterator_advance(&entry
, iter
)) < 0)