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;
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 tf
= git__calloc(sizeof(tree_iterator_frame
) +
348 n_entries
* sizeof(tree_iterator_entry
*), 1);
349 GITERR_CHECK_ALLOC(tf
);
351 tf
->n_entries
= n_entries
;
357 for (i
= head
->current
, n_entries
= 0; i
< head
->next
; ++i
) {
358 git_tree
*tree
= head
->entries
[i
]->tree
;
359 size_t j
, max_j
= git_tree_entrycount(tree
);
361 for (j
= 0; j
< max_j
; ++j
) {
362 tree_iterator_entry
*entry
= git_pool_malloc(&ti
->pool
, 1);
363 GITERR_CHECK_ALLOC(entry
);
365 entry
->parent
= head
->entries
[i
];
366 entry
->te
= git_tree_entry_byindex(tree
, j
);
369 tf
->entries
[n_entries
++] = entry
;
373 /* if ignore_case, sort entries case insensitively */
374 if (iterator__ignore_case(ti
))
376 (void **)tf
->entries
, tf
->n_entries
, tree_iterator__ci_cmp
, tf
);
378 /* pick tf->current based on "start" (or start at zero) */
379 if (head
->startlen
> 0) {
380 git__bsearch_r((void **)tf
->entries
, tf
->n_entries
, head
,
381 tree_iterator__search_cmp
, ti
, &tf
->current
);
383 while (tf
->current
&&
384 !tree_iterator__search_cmp(head
, tf
->entries
[tf
->current
-1], ti
))
387 if ((tf
->start
= strchr(head
->start
, '/')) != NULL
) {
389 tf
->startlen
= strlen(tf
->start
);
393 ti
->path_has_filename
= ti
->entry_is_current
= false;
395 if ((error
= tree_iterator__set_next(ti
, tf
)) < 0)
398 /* autoexpand as needed */
399 if (!iterator__include_trees(ti
) && tree_iterator__at_tree(ti
))
400 return tree_iterator__push_frame(ti
);
405 static bool tree_iterator__pop_frame(tree_iterator
*ti
, bool final
)
407 tree_iterator_frame
*tf
= ti
->head
;
413 ti
->head
->down
= NULL
;
415 tree_iterator__move_to_next(ti
, tf
);
417 if (!final
) { /* if final, don't bother to clean up */
418 git_pool_free_array(&ti
->pool
, tf
->n_entries
, (void **)tf
->entries
);
419 git_buf_rtruncate_at_char(&ti
->path
, '/');
427 static void tree_iterator__pop_all(tree_iterator
*ti
, bool to_end
, bool final
)
429 while (tree_iterator__pop_frame(ti
, final
)) /* pop to root */;
432 ti
->head
->current
= to_end
? ti
->head
->n_entries
: 0;
433 ti
->path_ambiguities
= 0;
434 git_buf_clear(&ti
->path
);
438 static int tree_iterator__update_entry(tree_iterator
*ti
)
440 tree_iterator_frame
*tf
;
441 const git_tree_entry
*te
;
443 if (ti
->entry_is_current
)
447 te
= tf
->entries
[tf
->current
]->te
;
449 ti
->entry
.mode
= te
->attr
;
450 git_oid_cpy(&ti
->entry
.id
, &te
->oid
);
452 ti
->entry
.path
= tree_iterator__current_filename(ti
, te
);
453 GITERR_CHECK_ALLOC(ti
->entry
.path
);
455 if (ti
->path_ambiguities
> 0)
456 tree_iterator__rewrite_filename(ti
);
458 if (iterator__past_end(ti
, ti
->entry
.path
)) {
459 tree_iterator__pop_all(ti
, true, false);
463 ti
->entry_is_current
= true;
468 static int tree_iterator__current(
469 const git_index_entry
**entry
, git_iterator
*self
)
472 tree_iterator
*ti
= (tree_iterator
*)self
;
473 tree_iterator_frame
*tf
= ti
->head
;
475 iterator__clear_entry(entry
);
477 if (tf
->current
>= tf
->n_entries
)
480 if ((error
= tree_iterator__update_entry(ti
)) < 0)
486 ti
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
491 static int tree_iterator__advance_into(
492 const git_index_entry
**entry
, git_iterator
*self
)
495 tree_iterator
*ti
= (tree_iterator
*)self
;
497 iterator__clear_entry(entry
);
499 if (tree_iterator__at_tree(ti
))
500 error
= tree_iterator__push_frame(ti
);
503 error
= tree_iterator__current(entry
, self
);
508 static int tree_iterator__advance(
509 const git_index_entry
**entry
, git_iterator
*self
)
512 tree_iterator
*ti
= (tree_iterator
*)self
;
513 tree_iterator_frame
*tf
= ti
->head
;
515 iterator__clear_entry(entry
);
517 if (tf
->current
>= tf
->n_entries
)
520 if (!iterator__has_been_accessed(ti
))
521 return tree_iterator__current(entry
, self
);
523 if (iterator__do_autoexpand(ti
) && iterator__include_trees(ti
) &&
524 tree_iterator__at_tree(ti
))
525 return tree_iterator__advance_into(entry
, self
);
527 if (ti
->path_has_filename
) {
528 git_buf_rtruncate_at_char(&ti
->path
, '/');
529 ti
->path_has_filename
= ti
->entry_is_current
= false;
532 /* scan forward and up, advancing in frame or popping frame when done */
533 while (!tree_iterator__move_to_next(ti
, tf
) &&
534 tree_iterator__pop_frame(ti
, false))
537 /* find next and load trees */
538 if ((error
= tree_iterator__set_next(ti
, tf
)) < 0)
541 /* deal with include_trees / auto_expand as needed */
542 if (!iterator__include_trees(ti
) && tree_iterator__at_tree(ti
))
543 return tree_iterator__advance_into(entry
, self
);
545 return tree_iterator__current(entry
, self
);
548 static int tree_iterator__seek(git_iterator
*self
, const char *prefix
)
550 GIT_UNUSED(self
); GIT_UNUSED(prefix
);
554 static int tree_iterator__reset(
555 git_iterator
*self
, const char *start
, const char *end
)
557 tree_iterator
*ti
= (tree_iterator
*)self
;
559 tree_iterator__pop_all(ti
, false, false);
561 if (iterator__reset_range(self
, start
, end
) < 0)
564 return tree_iterator__push_frame(ti
); /* re-expand root tree */
567 static int tree_iterator__at_end(git_iterator
*self
)
569 tree_iterator
*ti
= (tree_iterator
*)self
;
570 return (ti
->head
->current
>= ti
->head
->n_entries
);
573 static void tree_iterator__free(git_iterator
*self
)
575 tree_iterator
*ti
= (tree_iterator
*)self
;
577 tree_iterator__pop_all(ti
, true, false);
579 git_tree_free(ti
->head
->entries
[0]->tree
);
581 git_pool_clear(&ti
->pool
);
582 git_buf_free(&ti
->path
);
585 static int tree_iterator__create_root_frame(tree_iterator
*ti
, git_tree
*tree
)
587 size_t sz
= sizeof(tree_iterator_frame
) + sizeof(tree_iterator_entry
);
588 tree_iterator_frame
*root
= git__calloc(sz
, sizeof(char));
589 GITERR_CHECK_ALLOC(root
);
593 root
->start
= ti
->base
.start
;
594 root
->startlen
= root
->start
? strlen(root
->start
) : 0;
595 root
->entries
[0] = git_pool_mallocz(&ti
->pool
, 1);
596 GITERR_CHECK_ALLOC(root
->entries
[0]);
597 root
->entries
[0]->tree
= tree
;
599 ti
->head
= ti
->root
= root
;
604 int git_iterator_for_tree(
607 git_iterator_flag_t flags
,
615 return git_iterator_for_nothing(iter
, flags
, start
, end
);
617 if ((error
= git_object_dup((git_object
**)&tree
, (git_object
*)tree
)) < 0)
620 ti
= git__calloc(1, sizeof(tree_iterator
));
621 GITERR_CHECK_ALLOC(ti
);
623 ITERATOR_BASE_INIT(ti
, tree
, TREE
, git_tree_owner(tree
));
625 if ((error
= iterator__update_ignore_case((git_iterator
*)ti
, flags
)) < 0)
627 ti
->strncomp
= iterator__ignore_case(ti
) ? git__strncasecmp
: git__strncmp
;
629 if ((error
= git_pool_init(&ti
->pool
, sizeof(tree_iterator_entry
),0)) < 0 ||
630 (error
= tree_iterator__create_root_frame(ti
, tree
)) < 0 ||
631 (error
= tree_iterator__push_frame(ti
)) < 0) /* expand root now */
634 *iter
= (git_iterator
*)ti
;
638 git_iterator_free((git_iterator
*)ti
);
645 git_iterator_callbacks cb
;
648 git_vector_cmp entry_srch
;
650 /* when not in autoexpand mode, use these to represent "tree" state */
653 char restore_terminator
;
654 git_index_entry tree_entry
;
657 static const git_index_entry
*index_iterator__index_entry(index_iterator
*ii
)
659 const git_index_entry
*ie
= git_vector_get(&ii
->entries
, ii
->current
);
661 if (ie
!= NULL
&& iterator__past_end(ii
, ie
->path
)) {
662 ii
->current
= git_vector_length(&ii
->entries
);
669 static const git_index_entry
*index_iterator__skip_conflicts(index_iterator
*ii
)
671 const git_index_entry
*ie
;
673 while ((ie
= index_iterator__index_entry(ii
)) != NULL
&&
674 git_index_entry_stage(ie
) != 0)
680 static void index_iterator__next_prefix_tree(index_iterator
*ii
)
684 if (!iterator__include_trees(ii
))
687 slash
= strchr(&ii
->partial
.ptr
[ii
->partial_pos
], '/');
690 ii
->partial_pos
= (slash
- ii
->partial
.ptr
) + 1;
691 ii
->restore_terminator
= ii
->partial
.ptr
[ii
->partial_pos
];
692 ii
->partial
.ptr
[ii
->partial_pos
] = '\0';
694 ii
->partial_pos
= ii
->partial
.size
;
697 if (index_iterator__index_entry(ii
) == NULL
)
698 ii
->partial_pos
= ii
->partial
.size
;
701 static int index_iterator__first_prefix_tree(index_iterator
*ii
)
703 const git_index_entry
*ie
= index_iterator__skip_conflicts(ii
);
704 const char *scan
, *prior
, *slash
;
706 if (!ie
|| !iterator__include_trees(ii
))
709 /* find longest common prefix with prior index entry */
710 for (scan
= slash
= ie
->path
, prior
= ii
->partial
.ptr
;
711 *scan
&& *scan
== *prior
; ++scan
, ++prior
)
715 if (git_buf_sets(&ii
->partial
, ie
->path
) < 0)
718 ii
->partial_pos
= (slash
- ie
->path
) + 1;
719 index_iterator__next_prefix_tree(ii
);
724 #define index_iterator__at_tree(I) \
725 (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
727 static int index_iterator__current(
728 const git_index_entry
**entry
, git_iterator
*self
)
730 index_iterator
*ii
= (index_iterator
*)self
;
731 const git_index_entry
*ie
= git_vector_get(&ii
->entries
, ii
->current
);
733 if (ie
!= NULL
&& index_iterator__at_tree(ii
)) {
734 ii
->tree_entry
.path
= ii
->partial
.ptr
;
735 ie
= &ii
->tree_entry
;
741 ii
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
743 return (ie
!= NULL
) ? 0 : GIT_ITEROVER
;
746 static int index_iterator__at_end(git_iterator
*self
)
748 index_iterator
*ii
= (index_iterator
*)self
;
749 return (ii
->current
>= git_vector_length(&ii
->entries
));
752 static int index_iterator__advance(
753 const git_index_entry
**entry
, git_iterator
*self
)
755 index_iterator
*ii
= (index_iterator
*)self
;
756 size_t entrycount
= git_vector_length(&ii
->entries
);
757 const git_index_entry
*ie
;
759 if (!iterator__has_been_accessed(ii
))
760 return index_iterator__current(entry
, self
);
762 if (index_iterator__at_tree(ii
)) {
763 if (iterator__do_autoexpand(ii
)) {
764 ii
->partial
.ptr
[ii
->partial_pos
] = ii
->restore_terminator
;
765 index_iterator__next_prefix_tree(ii
);
767 /* advance to sibling tree (i.e. find entry with new prefix) */
768 while (ii
->current
< entrycount
) {
771 if (!(ie
= git_vector_get(&ii
->entries
, ii
->current
)) ||
772 ii
->base
.prefixcomp(ie
->path
, ii
->partial
.ptr
) != 0)
776 if (index_iterator__first_prefix_tree(ii
) < 0)
780 if (ii
->current
< entrycount
)
783 if (index_iterator__first_prefix_tree(ii
) < 0)
787 return index_iterator__current(entry
, self
);
790 static int index_iterator__advance_into(
791 const git_index_entry
**entry
, git_iterator
*self
)
793 index_iterator
*ii
= (index_iterator
*)self
;
794 const git_index_entry
*ie
= git_vector_get(&ii
->entries
, ii
->current
);
796 if (ie
!= NULL
&& index_iterator__at_tree(ii
)) {
797 if (ii
->restore_terminator
)
798 ii
->partial
.ptr
[ii
->partial_pos
] = ii
->restore_terminator
;
799 index_iterator__next_prefix_tree(ii
);
802 return index_iterator__current(entry
, self
);
805 static int index_iterator__seek(git_iterator
*self
, const char *prefix
)
807 GIT_UNUSED(self
); GIT_UNUSED(prefix
);
811 static int index_iterator__reset(
812 git_iterator
*self
, const char *start
, const char *end
)
814 index_iterator
*ii
= (index_iterator
*)self
;
815 const git_index_entry
*ie
;
817 if (iterator__reset_range(self
, start
, end
) < 0)
823 git_index_snapshot_find(
824 &ii
->current
, &ii
->entries
, ii
->entry_srch
, ii
->base
.start
, 0, 0);
826 if ((ie
= index_iterator__skip_conflicts(ii
)) == NULL
)
829 if (git_buf_sets(&ii
->partial
, ie
->path
) < 0)
834 if (ii
->base
.start
) {
835 size_t startlen
= strlen(ii
->base
.start
);
837 ii
->partial_pos
= (startlen
> ii
->partial
.size
) ?
838 ii
->partial
.size
: startlen
;
841 index_iterator__next_prefix_tree(ii
);
846 static void index_iterator__free(git_iterator
*self
)
848 index_iterator
*ii
= (index_iterator
*)self
;
849 git_index_snapshot_release(&ii
->entries
, ii
->index
);
851 git_buf_free(&ii
->partial
);
854 int git_iterator_for_index(
857 git_iterator_flag_t flags
,
862 index_iterator
*ii
= git__calloc(1, sizeof(index_iterator
));
863 GITERR_CHECK_ALLOC(ii
);
865 if ((error
= git_index_snapshot_new(&ii
->entries
, index
)) < 0) {
871 ITERATOR_BASE_INIT(ii
, index
, INDEX
, git_index_owner(index
));
873 if ((error
= iterator__update_ignore_case((git_iterator
*)ii
, flags
)) < 0) {
874 git_iterator_free((git_iterator
*)ii
);
878 ii
->entry_srch
= iterator__ignore_case(ii
) ?
879 git_index_entry_isrch
: git_index_entry_srch
;
881 git_vector_set_cmp(&ii
->entries
, iterator__ignore_case(ii
) ?
882 git_index_entry_icmp
: git_index_entry_cmp
);
883 git_vector_sort(&ii
->entries
);
885 git_buf_init(&ii
->partial
, 0);
886 ii
->tree_entry
.mode
= GIT_FILEMODE_TREE
;
888 index_iterator__reset((git_iterator
*)ii
, NULL
, NULL
);
890 *iter
= (git_iterator
*)ii
;
895 typedef struct fs_iterator_frame fs_iterator_frame
;
896 struct fs_iterator_frame
{
897 fs_iterator_frame
*next
;
903 typedef struct fs_iterator fs_iterator
;
906 git_iterator_callbacks cb
;
907 fs_iterator_frame
*stack
;
908 git_index_entry entry
;
911 uint32_t dirload_flags
;
914 int (*enter_dir_cb
)(fs_iterator
*self
);
915 int (*leave_dir_cb
)(fs_iterator
*self
);
916 int (*update_entry_cb
)(fs_iterator
*self
);
919 #define FS_MAX_DEPTH 100
921 static fs_iterator_frame
*fs_iterator__alloc_frame(fs_iterator
*fi
)
923 fs_iterator_frame
*ff
= git__calloc(1, sizeof(fs_iterator_frame
));
924 git_vector_cmp entry_compare
= CASESELECT(
925 iterator__ignore_case(fi
),
926 git_path_with_stat_cmp_icase
, git_path_with_stat_cmp
);
928 if (ff
&& git_vector_init(&ff
->entries
, 0, entry_compare
) < 0) {
936 static void fs_iterator__free_frame(fs_iterator_frame
*ff
)
938 git_vector_free_deep(&ff
->entries
);
942 static void fs_iterator__pop_frame(
943 fs_iterator
*fi
, fs_iterator_frame
*ff
, bool pop_last
)
945 if (fi
&& fi
->stack
== ff
) {
946 if (!ff
->next
&& !pop_last
) {
947 memset(&fi
->entry
, 0, sizeof(fi
->entry
));
951 if (fi
->leave_dir_cb
)
952 (void)fi
->leave_dir_cb(fi
);
954 fi
->stack
= ff
->next
;
958 fs_iterator__free_frame(ff
);
961 static int fs_iterator__update_entry(fs_iterator
*fi
);
962 static int fs_iterator__advance_over(
963 const git_index_entry
**entry
, git_iterator
*self
);
965 static int fs_iterator__entry_cmp(const void *i
, const void *item
)
967 const fs_iterator
*fi
= (const fs_iterator
*)i
;
968 const git_path_with_stat
*ps
= item
;
969 return fi
->base
.prefixcomp(fi
->base
.start
, ps
->path
);
972 static void fs_iterator__seek_frame_start(
973 fs_iterator
*fi
, fs_iterator_frame
*ff
)
980 &ff
->index
, &ff
->entries
, fs_iterator__entry_cmp
, fi
);
985 static int fs_iterator__expand_dir(fs_iterator
*fi
)
988 fs_iterator_frame
*ff
;
990 if (fi
->depth
> FS_MAX_DEPTH
) {
991 giterr_set(GITERR_REPOSITORY
,
992 "Directory nesting is too deep (%d)", fi
->depth
);
996 ff
= fs_iterator__alloc_frame(fi
);
997 GITERR_CHECK_ALLOC(ff
);
999 error
= git_path_dirload_with_stat(
1000 fi
->path
.ptr
, fi
->root_len
, fi
->dirload_flags
,
1001 fi
->base
.start
, fi
->base
.end
, &ff
->entries
);
1004 git_error_state last_error
= { 0 };
1005 giterr_capture(&last_error
, error
);
1007 /* these callbacks may clear the error message */
1008 fs_iterator__free_frame(ff
);
1009 fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1010 /* next time return value we skipped to */
1011 fi
->base
.flags
&= ~GIT_ITERATOR_FIRST_ACCESS
;
1013 return giterr_restore(&last_error
);
1016 if (ff
->entries
.length
== 0) {
1017 fs_iterator__free_frame(ff
);
1018 return GIT_ENOTFOUND
;
1020 fi
->base
.stat_calls
+= ff
->entries
.length
;
1022 fs_iterator__seek_frame_start(fi
, ff
);
1024 ff
->next
= fi
->stack
;
1028 if (fi
->enter_dir_cb
&& (error
= fi
->enter_dir_cb(fi
)) < 0)
1031 return fs_iterator__update_entry(fi
);
1034 static int fs_iterator__current(
1035 const git_index_entry
**entry
, git_iterator
*self
)
1037 fs_iterator
*fi
= (fs_iterator
*)self
;
1038 const git_index_entry
*fe
= (fi
->entry
.path
== NULL
) ? NULL
: &fi
->entry
;
1043 fi
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
1045 return (fe
!= NULL
) ? 0 : GIT_ITEROVER
;
1048 static int fs_iterator__at_end(git_iterator
*self
)
1050 return (((fs_iterator
*)self
)->entry
.path
== NULL
);
1053 static int fs_iterator__advance_into(
1054 const git_index_entry
**entry
, git_iterator
*iter
)
1057 fs_iterator
*fi
= (fs_iterator
*)iter
;
1059 iterator__clear_entry(entry
);
1061 /* Allow you to explicitly advance into a commit/submodule (as well as a
1062 * tree) to avoid cases where an entry is mislabeled as a submodule in
1063 * the working directory. The fs iterator will never have COMMMIT
1064 * entries on it's own, but a wrapper might add them.
1066 if (fi
->entry
.path
!= NULL
&&
1067 (fi
->entry
.mode
== GIT_FILEMODE_TREE
||
1068 fi
->entry
.mode
== GIT_FILEMODE_COMMIT
))
1069 /* returns GIT_ENOTFOUND if the directory is empty */
1070 error
= fs_iterator__expand_dir(fi
);
1072 if (!error
&& entry
)
1073 error
= fs_iterator__current(entry
, iter
);
1075 if (!error
&& !fi
->entry
.path
)
1076 error
= GIT_ITEROVER
;
1081 static int fs_iterator__advance_over(
1082 const git_index_entry
**entry
, git_iterator
*self
)
1085 fs_iterator
*fi
= (fs_iterator
*)self
;
1086 fs_iterator_frame
*ff
;
1087 git_path_with_stat
*next
;
1092 while (fi
->entry
.path
!= NULL
) {
1094 next
= git_vector_get(&ff
->entries
, ++ff
->index
);
1099 fs_iterator__pop_frame(fi
, ff
, false);
1102 error
= fs_iterator__update_entry(fi
);
1104 if (!error
&& entry
!= NULL
)
1105 error
= fs_iterator__current(entry
, self
);
1110 static int fs_iterator__advance(
1111 const git_index_entry
**entry
, git_iterator
*self
)
1113 fs_iterator
*fi
= (fs_iterator
*)self
;
1115 if (!iterator__has_been_accessed(fi
))
1116 return fs_iterator__current(entry
, self
);
1118 /* given include_trees & autoexpand, we might have to go into a tree */
1119 if (iterator__do_autoexpand(fi
) &&
1120 fi
->entry
.path
!= NULL
&&
1121 fi
->entry
.mode
== GIT_FILEMODE_TREE
)
1123 int error
= fs_iterator__advance_into(entry
, self
);
1124 if (error
!= GIT_ENOTFOUND
)
1126 /* continue silently past empty directories if autoexpanding */
1130 return fs_iterator__advance_over(entry
, self
);
1133 static int fs_iterator__seek(git_iterator
*self
, const char *prefix
)
1137 /* pop stack until matching prefix */
1138 /* find prefix item in current frame */
1139 /* push subdirectories as deep as possible while matching */
1143 static int fs_iterator__reset(
1144 git_iterator
*self
, const char *start
, const char *end
)
1147 fs_iterator
*fi
= (fs_iterator
*)self
;
1149 while (fi
->stack
!= NULL
&& fi
->stack
->next
!= NULL
)
1150 fs_iterator__pop_frame(fi
, fi
->stack
, false);
1153 if ((error
= iterator__reset_range(self
, start
, end
)) < 0)
1156 fs_iterator__seek_frame_start(fi
, fi
->stack
);
1158 error
= fs_iterator__update_entry(fi
);
1159 if (error
== GIT_ITEROVER
)
1165 static void fs_iterator__free(git_iterator
*self
)
1167 fs_iterator
*fi
= (fs_iterator
*)self
;
1169 while (fi
->stack
!= NULL
)
1170 fs_iterator__pop_frame(fi
, fi
->stack
, true);
1172 git_buf_free(&fi
->path
);
1175 static int fs_iterator__update_entry(fs_iterator
*fi
)
1177 git_path_with_stat
*ps
;
1179 memset(&fi
->entry
, 0, sizeof(fi
->entry
));
1182 return GIT_ITEROVER
;
1184 ps
= git_vector_get(&fi
->stack
->entries
, fi
->stack
->index
);
1186 return GIT_ITEROVER
;
1188 git_buf_truncate(&fi
->path
, fi
->root_len
);
1189 if (git_buf_put(&fi
->path
, ps
->path
, ps
->path_len
) < 0)
1192 if (iterator__past_end(fi
, fi
->path
.ptr
+ fi
->root_len
))
1193 return GIT_ITEROVER
;
1195 fi
->entry
.path
= ps
->path
;
1196 git_index_entry__init_from_stat(&fi
->entry
, &ps
->st
, true);
1198 /* need different mode here to keep directories during iteration */
1199 fi
->entry
.mode
= git_futils_canonical_mode(ps
->st
.st_mode
);
1201 /* allow wrapper to check/update the entry (can force skip) */
1202 if (fi
->update_entry_cb
&&
1203 fi
->update_entry_cb(fi
) == GIT_ENOTFOUND
)
1204 return fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1206 /* if this is a tree and trees aren't included, then skip */
1207 if (fi
->entry
.mode
== GIT_FILEMODE_TREE
&& !iterator__include_trees(fi
)) {
1208 int error
= fs_iterator__advance_into(NULL
, (git_iterator
*)fi
);
1209 if (error
!= GIT_ENOTFOUND
)
1212 return fs_iterator__advance_over(NULL
, (git_iterator
*)fi
);
1218 static int fs_iterator__initialize(
1219 git_iterator
**out
, fs_iterator
*fi
, const char *root
)
1223 if (git_buf_sets(&fi
->path
, root
) < 0 || git_path_to_dir(&fi
->path
) < 0) {
1227 fi
->root_len
= fi
->path
.size
;
1230 (iterator__ignore_case(fi
) ? GIT_PATH_DIR_IGNORE_CASE
: 0) |
1231 (iterator__flag(fi
, PRECOMPOSE_UNICODE
) ?
1232 GIT_PATH_DIR_PRECOMPOSE_UNICODE
: 0);
1234 if ((error
= fs_iterator__expand_dir(fi
)) < 0) {
1235 if (error
== GIT_ENOTFOUND
|| error
== GIT_ITEROVER
) {
1239 git_iterator_free((git_iterator
*)fi
);
1244 *out
= (git_iterator
*)fi
;
1248 int git_iterator_for_filesystem(
1251 git_iterator_flag_t flags
,
1255 fs_iterator
*fi
= git__calloc(1, sizeof(fs_iterator
));
1256 GITERR_CHECK_ALLOC(fi
);
1258 ITERATOR_BASE_INIT(fi
, fs
, FS
, NULL
);
1260 if ((flags
& GIT_ITERATOR_IGNORE_CASE
) != 0)
1261 fi
->base
.flags
|= GIT_ITERATOR_IGNORE_CASE
;
1263 return fs_iterator__initialize(out
, fi
, root
);
1269 git_ignores ignores
;
1273 GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf
*path
)
1277 if (!path
|| (len
= path
->size
) < 4)
1280 if (path
->ptr
[len
- 1] == '/')
1283 if (tolower(path
->ptr
[len
- 1]) != 't' ||
1284 tolower(path
->ptr
[len
- 2]) != 'i' ||
1285 tolower(path
->ptr
[len
- 3]) != 'g' ||
1286 tolower(path
->ptr
[len
- 4]) != '.')
1289 return (len
== 4 || path
->ptr
[len
- 5] == '/');
1292 static int workdir_iterator__enter_dir(fs_iterator
*fi
)
1294 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1295 fs_iterator_frame
*ff
= fi
->stack
;
1297 git_path_with_stat
*entry
;
1298 bool found_submodules
= false;
1300 /* check if this directory is ignored */
1301 if (git_ignore__lookup(
1302 &ff
->is_ignored
, &wi
->ignores
, fi
->path
.ptr
+ fi
->root_len
) < 0) {
1304 ff
->is_ignored
= GIT_IGNORE_NOTFOUND
;
1307 /* if this is not the top level directory... */
1308 if (ff
->next
!= NULL
) {
1309 ssize_t slash_pos
= git_buf_rfind_next(&fi
->path
, '/');
1311 /* inherit ignored from parent if no rule specified */
1312 if (ff
->is_ignored
<= GIT_IGNORE_NOTFOUND
)
1313 ff
->is_ignored
= ff
->next
->is_ignored
;
1315 /* push new ignores for files in this directory */
1316 (void)git_ignore__push_dir(&wi
->ignores
, &fi
->path
.ptr
[slash_pos
+ 1]);
1319 /* convert submodules to GITLINK and remove trailing slashes */
1320 git_vector_foreach(&ff
->entries
, pos
, entry
) {
1321 if (!S_ISDIR(entry
->st
.st_mode
) || !strcmp(GIT_DIR
, entry
->path
))
1324 if (git_submodule__is_submodule(fi
->base
.repo
, entry
->path
)) {
1325 entry
->st
.st_mode
= GIT_FILEMODE_COMMIT
;
1327 entry
->path
[entry
->path_len
] = '\0';
1328 found_submodules
= true;
1332 /* if we renamed submodules, re-sort and re-seek to start */
1333 if (found_submodules
) {
1334 git_vector_set_sorted(&ff
->entries
, 0);
1335 git_vector_sort(&ff
->entries
);
1336 fs_iterator__seek_frame_start(fi
, ff
);
1342 static int workdir_iterator__leave_dir(fs_iterator
*fi
)
1344 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1345 git_ignore__pop_dir(&wi
->ignores
);
1349 static int workdir_iterator__update_entry(fs_iterator
*fi
)
1351 workdir_iterator
*wi
= (workdir_iterator
*)fi
;
1353 /* skip over .git entries */
1354 if (workdir_path_is_dotgit(&fi
->path
))
1355 return GIT_ENOTFOUND
;
1357 /* reset is_ignored since we haven't checked yet */
1358 wi
->is_ignored
= GIT_IGNORE_UNCHECKED
;
1363 static void workdir_iterator__free(git_iterator
*self
)
1365 workdir_iterator
*wi
= (workdir_iterator
*)self
;
1366 fs_iterator__free(self
);
1367 git_ignore__free(&wi
->ignores
);
1370 int git_iterator_for_workdir_ext(
1372 git_repository
*repo
,
1373 const char *repo_workdir
,
1374 git_iterator_flag_t flags
,
1378 int error
, precompose
= 0;
1379 workdir_iterator
*wi
;
1381 if (!repo_workdir
) {
1382 if (git_repository__ensure_not_bare(repo
, "scan working directory") < 0)
1383 return GIT_EBAREREPO
;
1384 repo_workdir
= git_repository_workdir(repo
);
1387 /* initialize as an fs iterator then do overrides */
1388 wi
= git__calloc(1, sizeof(workdir_iterator
));
1389 GITERR_CHECK_ALLOC(wi
);
1390 ITERATOR_BASE_INIT((&wi
->fi
), fs
, FS
, repo
);
1392 wi
->fi
.base
.type
= GIT_ITERATOR_TYPE_WORKDIR
;
1393 wi
->fi
.cb
.free
= workdir_iterator__free
;
1394 wi
->fi
.enter_dir_cb
= workdir_iterator__enter_dir
;
1395 wi
->fi
.leave_dir_cb
= workdir_iterator__leave_dir
;
1396 wi
->fi
.update_entry_cb
= workdir_iterator__update_entry
;
1398 if ((error
= iterator__update_ignore_case((git_iterator
*)wi
, flags
)) < 0 ||
1399 (error
= git_ignore__for_path(repo
, ".gitignore", &wi
->ignores
)) < 0)
1401 git_iterator_free((git_iterator
*)wi
);
1405 /* try to look up precompose and set flag if appropriate */
1406 if (git_repository__cvar(&precompose
, repo
, GIT_CVAR_PRECOMPOSE
) < 0)
1408 else if (precompose
)
1409 wi
->fi
.base
.flags
|= GIT_ITERATOR_PRECOMPOSE_UNICODE
;
1411 return fs_iterator__initialize(out
, &wi
->fi
, repo_workdir
);
1415 void git_iterator_free(git_iterator
*iter
)
1420 iter
->cb
->free(iter
);
1422 git__free(iter
->start
);
1423 git__free(iter
->end
);
1425 memset(iter
, 0, sizeof(*iter
));
1430 int git_iterator_set_ignore_case(git_iterator
*iter
, bool ignore_case
)
1432 bool desire_ignore_case
= (ignore_case
!= 0);
1434 if (iterator__ignore_case(iter
) == desire_ignore_case
)
1437 if (iter
->type
== GIT_ITERATOR_TYPE_EMPTY
) {
1438 if (desire_ignore_case
)
1439 iter
->flags
|= GIT_ITERATOR_IGNORE_CASE
;
1441 iter
->flags
&= ~GIT_ITERATOR_IGNORE_CASE
;
1443 giterr_set(GITERR_INVALID
,
1444 "Cannot currently set ignore case on non-empty iterators");
1451 git_index
*git_iterator_get_index(git_iterator
*iter
)
1453 if (iter
->type
== GIT_ITERATOR_TYPE_INDEX
)
1454 return ((index_iterator
*)iter
)->index
;
1458 int git_iterator_current_tree_entry(
1459 const git_tree_entry
**tree_entry
, git_iterator
*iter
)
1461 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
)
1464 tree_iterator_frame
*tf
= ((tree_iterator
*)iter
)->head
;
1465 *tree_entry
= (tf
->current
< tf
->n_entries
) ?
1466 tf
->entries
[tf
->current
]->te
: NULL
;
1472 int git_iterator_current_parent_tree(
1473 const git_tree
**tree_ptr
,
1475 const char *parent_path
)
1477 tree_iterator
*ti
= (tree_iterator
*)iter
;
1478 tree_iterator_frame
*tf
;
1479 const char *scan
= parent_path
;
1480 const git_tree_entry
*te
;
1484 if (iter
->type
!= GIT_ITERATOR_TYPE_TREE
)
1487 for (tf
= ti
->root
; *scan
; ) {
1488 if (!(tf
= tf
->down
) ||
1489 tf
->current
>= tf
->n_entries
||
1490 !(te
= tf
->entries
[tf
->current
]->te
) ||
1491 ti
->strncomp(scan
, te
->filename
, te
->filename_len
) != 0)
1494 scan
+= te
->filename_len
;
1499 *tree_ptr
= tf
->entries
[tf
->current
]->tree
;
1503 static void workdir_iterator_update_is_ignored(workdir_iterator
*wi
)
1505 if (git_ignore__lookup(
1506 &wi
->is_ignored
, &wi
->ignores
, wi
->fi
.entry
.path
) < 0) {
1508 wi
->is_ignored
= GIT_IGNORE_NOTFOUND
;
1511 /* use ignore from containing frame stack */
1512 if (wi
->is_ignored
<= GIT_IGNORE_NOTFOUND
)
1513 wi
->is_ignored
= wi
->fi
.stack
->is_ignored
;
1516 bool git_iterator_current_is_ignored(git_iterator
*iter
)
1518 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1520 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1523 if (wi
->is_ignored
!= GIT_IGNORE_UNCHECKED
)
1524 return (bool)(wi
->is_ignored
== GIT_IGNORE_TRUE
);
1526 workdir_iterator_update_is_ignored(wi
);
1528 return (bool)(wi
->is_ignored
== GIT_IGNORE_TRUE
);
1531 bool git_iterator_current_tree_is_ignored(git_iterator
*iter
)
1533 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1535 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1538 return (bool)(wi
->fi
.stack
->is_ignored
== GIT_IGNORE_TRUE
);
1541 int git_iterator_cmp(git_iterator
*iter
, const char *path_prefix
)
1543 const git_index_entry
*entry
;
1545 /* a "done" iterator is after every prefix */
1546 if (git_iterator_current(&entry
, iter
) < 0 || entry
== NULL
)
1549 /* a NULL prefix is after any valid iterator */
1553 return iter
->prefixcomp(entry
->path
, path_prefix
);
1556 int git_iterator_current_workdir_path(git_buf
**path
, git_iterator
*iter
)
1558 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1560 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
|| !wi
->fi
.entry
.path
)
1563 *path
= &wi
->fi
.path
;
1568 int git_iterator_advance_over_with_status(
1569 const git_index_entry
**entryptr
,
1570 git_iterator_status_t
*status
,
1574 workdir_iterator
*wi
= (workdir_iterator
*)iter
;
1576 const git_index_entry
*entry
;
1578 *status
= GIT_ITERATOR_STATUS_NORMAL
;
1580 if (iter
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1581 return git_iterator_advance(entryptr
, iter
);
1582 if ((error
= git_iterator_current(&entry
, iter
)) < 0)
1585 if (!S_ISDIR(entry
->mode
)) {
1586 workdir_iterator_update_is_ignored(wi
);
1587 if (wi
->is_ignored
== GIT_IGNORE_TRUE
)
1588 *status
= GIT_ITERATOR_STATUS_IGNORED
;
1589 return git_iterator_advance(entryptr
, iter
);
1592 *status
= GIT_ITERATOR_STATUS_EMPTY
;
1594 base
= git__strdup(entry
->path
);
1595 GITERR_CHECK_ALLOC(base
);
1597 /* scan inside directory looking for a non-ignored item */
1598 while (entry
&& !iter
->prefixcomp(entry
->path
, base
)) {
1599 workdir_iterator_update_is_ignored(wi
);
1601 /* if we found an explicitly ignored item, then update from
1604 if (wi
->is_ignored
== GIT_IGNORE_TRUE
)
1605 *status
= GIT_ITERATOR_STATUS_IGNORED
;
1606 else if (S_ISDIR(entry
->mode
)) {
1607 error
= git_iterator_advance_into(&entry
, iter
);
1611 else if (error
== GIT_ENOTFOUND
) {
1613 wi
->is_ignored
= GIT_IGNORE_TRUE
; /* mark empty dirs ignored */
1615 break; /* real error, stop here */
1617 /* we found a non-ignored item, treat parent as untracked */
1618 *status
= GIT_ITERATOR_STATUS_NORMAL
;
1622 if ((error
= git_iterator_advance(&entry
, iter
)) < 0)
1626 /* wrap up scan back to base directory */
1627 while (entry
&& !iter
->prefixcomp(entry
->path
, base
))
1628 if ((error
= git_iterator_advance(&entry
, iter
)) < 0)