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 #define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
14 #define GIT_ITERATOR_HONOR_IGNORES (1 << 16)
15 #define GIT_ITERATOR_IGNORE_DOT_GIT (1 << 17)
17 #define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
18 #define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
19 #define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
20 #define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
21 #define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
22 #define iterator__include_conflicts(I) iterator__flag(I,INCLUDE_CONFLICTS)
23 #define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
24 #define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES)
25 #define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT)
26 #define iterator__descend_symlinks(I) iterator__flag(I,DESCEND_SYMLINKS)
29 static void iterator_set_ignore_case(git_iterator
*iter
, bool ignore_case
)
32 iter
->flags
|= GIT_ITERATOR_IGNORE_CASE
;
34 iter
->flags
&= ~GIT_ITERATOR_IGNORE_CASE
;
36 iter
->strcomp
= ignore_case
? git__strcasecmp
: git__strcmp
;
37 iter
->strncomp
= ignore_case
? git__strncasecmp
: git__strncmp
;
38 iter
->prefixcomp
= ignore_case
? git__prefixcmp_icase
: git__prefixcmp
;
39 iter
->entry_srch
= ignore_case
? git_index_entry_isrch
: git_index_entry_srch
;
41 git_vector_set_cmp(&iter
->pathlist
, (git_vector_cmp
)iter
->strcomp
);
44 static int iterator_range_init(
45 git_iterator
*iter
, const char *start
, const char *end
)
47 if (start
&& *start
) {
48 iter
->start
= git__strdup(start
);
49 GIT_ERROR_CHECK_ALLOC(iter
->start
);
51 iter
->start_len
= strlen(iter
->start
);
55 iter
->end
= git__strdup(end
);
56 GIT_ERROR_CHECK_ALLOC(iter
->end
);
58 iter
->end_len
= strlen(iter
->end
);
61 iter
->started
= (iter
->start
== NULL
);
67 static void iterator_range_free(git_iterator
*iter
)
70 git__free(iter
->start
);
82 static int iterator_reset_range(
83 git_iterator
*iter
, const char *start
, const char *end
)
85 iterator_range_free(iter
);
86 return iterator_range_init(iter
, start
, end
);
89 static int iterator_pathlist_init(git_iterator
*iter
, git_strarray
*pathlist
)
93 if (git_vector_init(&iter
->pathlist
, pathlist
->count
, NULL
) < 0)
96 for (i
= 0; i
< pathlist
->count
; i
++) {
97 if (!pathlist
->strings
[i
])
100 if (git_vector_insert(&iter
->pathlist
, pathlist
->strings
[i
]) < 0)
107 static int iterator_init_common(
109 git_repository
*repo
,
111 git_iterator_options
*given_opts
)
113 static git_iterator_options default_opts
= GIT_ITERATOR_OPTIONS_INIT
;
114 git_iterator_options
*options
= given_opts
? given_opts
: &default_opts
;
121 iter
->flags
= options
->flags
;
123 if ((iter
->flags
& GIT_ITERATOR_IGNORE_CASE
) != 0) {
125 } else if ((iter
->flags
& GIT_ITERATOR_DONT_IGNORE_CASE
) != 0) {
130 if ((error
= git_repository_index__weakptr(&index
, iter
->repo
)) < 0)
133 ignore_case
= !!index
->ignore_case
;
135 if (ignore_case
== 1)
136 iter
->flags
|= GIT_ITERATOR_IGNORE_CASE
;
138 iter
->flags
|= GIT_ITERATOR_DONT_IGNORE_CASE
;
143 /* try to look up precompose and set flag if appropriate */
145 (iter
->flags
& GIT_ITERATOR_PRECOMPOSE_UNICODE
) == 0 &&
146 (iter
->flags
& GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE
) == 0) {
148 if (git_repository__cvar(&precompose
, repo
, GIT_CVAR_PRECOMPOSE
) < 0)
151 iter
->flags
|= GIT_ITERATOR_PRECOMPOSE_UNICODE
;
154 if ((iter
->flags
& GIT_ITERATOR_DONT_AUTOEXPAND
))
155 iter
->flags
|= GIT_ITERATOR_INCLUDE_TREES
;
157 if ((error
= iterator_range_init(iter
, options
->start
, options
->end
)) < 0 ||
158 (error
= iterator_pathlist_init(iter
, &options
->pathlist
)) < 0)
161 iterator_set_ignore_case(iter
, ignore_case
);
165 static void iterator_clear(git_iterator
*iter
)
167 iter
->started
= false;
169 iter
->stat_calls
= 0;
170 iter
->pathlist_walk_idx
= 0;
171 iter
->flags
&= ~GIT_ITERATOR_FIRST_ACCESS
;
174 GIT_INLINE(bool) iterator_has_started(
175 git_iterator
*iter
, const char *path
, bool is_submodule
)
179 if (iter
->start
== NULL
|| iter
->started
== true)
182 /* the starting path is generally a prefix - we have started once we
183 * are prefixed by this path
185 iter
->started
= (iter
->prefixcomp(path
, iter
->start
) >= 0);
190 path_len
= strlen(path
);
192 /* if, however, we are a submodule, then we support `start` being
193 * suffixed with a `/` for crazy legacy reasons. match `submod`
194 * with a start path of `submod/`.
196 if (is_submodule
&& iter
->start_len
&& path_len
== iter
->start_len
- 1 &&
197 iter
->start
[iter
->start_len
-1] == '/')
200 /* if, however, our current path is a directory, and our starting path
201 * is _beneath_ that directory, then recurse into the directory (even
202 * though we have not yet "started")
204 if (path_len
> 0 && path
[path_len
-1] == '/' &&
205 iter
->strncomp(path
, iter
->start
, path_len
) == 0)
211 GIT_INLINE(bool) iterator_has_ended(git_iterator
*iter
, const char *path
)
213 if (iter
->end
== NULL
)
215 else if (iter
->ended
)
218 iter
->ended
= (iter
->prefixcomp(path
, iter
->end
) > 0);
222 /* walker for the index and tree iterator that allows it to walk the sorted
223 * pathlist entries alongside sorted iterator entries.
225 static bool iterator_pathlist_next_is(git_iterator
*iter
, const char *path
)
228 size_t path_len
, p_len
, cmp_len
, i
;
231 if (iter
->pathlist
.length
== 0)
234 git_vector_sort(&iter
->pathlist
);
236 path_len
= strlen(path
);
238 /* for comparison, drop the trailing slash on the current '/' */
239 if (path_len
&& path
[path_len
-1] == '/')
242 for (i
= iter
->pathlist_walk_idx
; i
< iter
->pathlist
.length
; i
++) {
243 p
= iter
->pathlist
.contents
[i
];
246 if (p_len
&& p
[p_len
-1] == '/')
249 cmp_len
= min(path_len
, p_len
);
251 /* see if the pathlist entry is a prefix of this path */
252 cmp
= iter
->strncomp(p
, path
, cmp_len
);
254 /* prefix match - see if there's an exact match, or if we were
255 * given a path that matches the directory
258 /* if this pathlist entry is not suffixed with a '/' then
259 * it matches a path that is a file or a directory.
260 * (eg, pathlist = "foo" and path is "foo" or "foo/" or
263 if (p
[cmp_len
] == '\0' &&
264 (path
[cmp_len
] == '\0' || path
[cmp_len
] == '/'))
267 /* if this pathlist entry _is_ suffixed with a '/' then
268 * it matches only paths that are directories.
269 * (eg, pathlist = "foo/" and path is "foo/" or "foo/something")
271 if (p
[cmp_len
] == '/' && path
[cmp_len
] == '/')
275 /* this pathlist entry sorts before the given path, try the next */
277 iter
->pathlist_walk_idx
++;
281 /* this pathlist sorts after the given path, no match. */
291 ITERATOR_PATHLIST_NONE
= 0,
292 ITERATOR_PATHLIST_IS_FILE
= 1,
293 ITERATOR_PATHLIST_IS_DIR
= 2,
294 ITERATOR_PATHLIST_IS_PARENT
= 3,
295 ITERATOR_PATHLIST_FULL
= 4,
296 } iterator_pathlist_search_t
;
298 static iterator_pathlist_search_t
iterator_pathlist_search(
299 git_iterator
*iter
, const char *path
, size_t path_len
)
305 if (iter
->pathlist
.length
== 0)
306 return ITERATOR_PATHLIST_FULL
;
308 git_vector_sort(&iter
->pathlist
);
310 error
= git_vector_bsearch2(&idx
, &iter
->pathlist
,
311 (git_vector_cmp
)iter
->strcomp
, path
);
313 /* the given path was found in the pathlist. since the pathlist only
314 * matches directories when they're suffixed with a '/', analyze the
315 * path string to determine whether it's a directory or not.
318 if (path_len
&& path
[path_len
-1] == '/')
319 return ITERATOR_PATHLIST_IS_DIR
;
321 return ITERATOR_PATHLIST_IS_FILE
;
324 /* at this point, the path we're examining may be a directory (though we
325 * don't know that yet, since we're avoiding a stat unless it's necessary)
326 * so walk the pathlist looking for the given path with a '/' after it,
328 while ((p
= git_vector_get(&iter
->pathlist
, idx
)) != NULL
) {
329 if (iter
->prefixcomp(p
, path
) != 0)
332 /* an exact match would have been matched by the bsearch above */
335 /* is this a literal directory entry (eg `foo/`) or a file beneath */
336 if (p
[path_len
] == '/') {
337 return (p
[path_len
+1] == '\0') ?
338 ITERATOR_PATHLIST_IS_DIR
:
339 ITERATOR_PATHLIST_IS_PARENT
;
342 if (p
[path_len
] > '/')
348 return ITERATOR_PATHLIST_NONE
;
353 static int empty_iterator_noop(const git_index_entry
**e
, git_iterator
*i
)
363 static int empty_iterator_advance_over(
364 const git_index_entry
**e
,
365 git_iterator_status_t
*s
,
368 *s
= GIT_ITERATOR_STATUS_EMPTY
;
369 return empty_iterator_noop(e
, i
);
372 static int empty_iterator_reset(git_iterator
*i
)
378 static void empty_iterator_free(git_iterator
*i
)
385 git_iterator_callbacks cb
;
388 int git_iterator_for_nothing(
390 git_iterator_options
*options
)
392 empty_iterator
*iter
;
394 static git_iterator_callbacks callbacks
= {
398 empty_iterator_advance_over
,
399 empty_iterator_reset
,
405 iter
= git__calloc(1, sizeof(empty_iterator
));
406 GIT_ERROR_CHECK_ALLOC(iter
);
408 iter
->base
.type
= GIT_ITERATOR_TYPE_EMPTY
;
409 iter
->base
.cb
= &callbacks
;
410 iter
->base
.flags
= options
->flags
;
419 git_tree_entry
*tree_entry
;
420 const char *parent_path
;
421 } tree_iterator_entry
;
426 /* path to this particular frame (folder) */
429 /* a sorted list of the entries for this frame (folder), these are
430 * actually pointers to the iterator's entry pool.
433 tree_iterator_entry
*current
;
437 /* on case insensitive iterations, we also have an array of other
438 * paths that were case insensitively equal to this one, and their
439 * tree objects. we have coalesced the tree entries into this frame.
440 * a child `tree_iterator_entry` will contain a pointer to its actual
443 git_vector similar_trees
;
444 git_array_t(git_buf
) similar_paths
;
445 } tree_iterator_frame
;
450 git_array_t(tree_iterator_frame
) frames
;
452 git_index_entry entry
;
455 /* a pool of entries to reduce the number of allocations */
459 GIT_INLINE(tree_iterator_frame
*) tree_iterator_parent_frame(
462 return iter
->frames
.size
> 1 ?
463 &iter
->frames
.ptr
[iter
->frames
.size
-2] : NULL
;
466 GIT_INLINE(tree_iterator_frame
*) tree_iterator_current_frame(
469 return iter
->frames
.size
? &iter
->frames
.ptr
[iter
->frames
.size
-1] : NULL
;
472 GIT_INLINE(int) tree_entry_cmp(
473 const git_tree_entry
*a
, const git_tree_entry
*b
, bool icase
)
476 a
->filename
, a
->filename_len
, a
->attr
== GIT_FILEMODE_TREE
,
477 b
->filename
, b
->filename_len
, b
->attr
== GIT_FILEMODE_TREE
,
478 icase
? git__strncasecmp
: git__strncmp
);
481 GIT_INLINE(int) tree_iterator_entry_cmp_icase(
482 const void *ptr_a
, const void *ptr_b
)
484 const tree_iterator_entry
*a
= (const tree_iterator_entry
*)ptr_a
;
485 const tree_iterator_entry
*b
= (const tree_iterator_entry
*)ptr_b
;
487 return tree_entry_cmp(a
->tree_entry
, b
->tree_entry
, true);
490 static int tree_iterator_entry_sort_icase(const void *ptr_a
, const void *ptr_b
)
492 const tree_iterator_entry
*a
= (const tree_iterator_entry
*)ptr_a
;
493 const tree_iterator_entry
*b
= (const tree_iterator_entry
*)ptr_b
;
495 int c
= tree_entry_cmp(a
->tree_entry
, b
->tree_entry
, true);
497 /* stabilize the sort order for filenames that are (case insensitively)
498 * the same by examining the parent path (case sensitively) before
499 * falling back to a case sensitive sort of the filename.
501 if (!c
&& a
->parent_path
!= b
->parent_path
)
502 c
= git__strcmp(a
->parent_path
, b
->parent_path
);
505 c
= tree_entry_cmp(a
->tree_entry
, b
->tree_entry
, false);
510 static int tree_iterator_compute_path(
512 tree_iterator_entry
*entry
)
516 if (entry
->parent_path
)
517 git_buf_joinpath(out
, entry
->parent_path
, entry
->tree_entry
->filename
);
519 git_buf_puts(out
, entry
->tree_entry
->filename
);
521 if (git_tree_entry__is_tree(entry
->tree_entry
))
522 git_buf_putc(out
, '/');
524 if (git_buf_oom(out
))
530 static int tree_iterator_frame_init(
533 tree_iterator_entry
*frame_entry
)
535 tree_iterator_frame
*new_frame
= NULL
;
536 tree_iterator_entry
*new_entry
;
537 git_tree
*dup
= NULL
;
538 git_tree_entry
*tree_entry
;
543 new_frame
= git_array_alloc(iter
->frames
);
544 GIT_ERROR_CHECK_ALLOC(new_frame
);
546 memset(new_frame
, 0, sizeof(tree_iterator_frame
));
548 if ((error
= git_tree_dup(&dup
, tree
)) < 0)
551 memset(new_frame
, 0x0, sizeof(tree_iterator_frame
));
552 new_frame
->tree
= dup
;
555 (error
= tree_iterator_compute_path(&new_frame
->path
, frame_entry
)) < 0)
558 cmp
= iterator__ignore_case(&iter
->base
) ?
559 tree_iterator_entry_sort_icase
: NULL
;
561 if ((error
= git_vector_init(
562 &new_frame
->entries
, dup
->entries
.size
, cmp
)) < 0)
565 git_array_foreach(dup
->entries
, i
, tree_entry
) {
566 new_entry
= git_pool_malloc(&iter
->entry_pool
, 1);
567 GIT_ERROR_CHECK_ALLOC(new_entry
);
569 new_entry
->tree_entry
= tree_entry
;
570 new_entry
->parent_path
= new_frame
->path
.ptr
;
572 if ((error
= git_vector_insert(&new_frame
->entries
, new_entry
)) < 0)
576 git_vector_set_sorted(&new_frame
->entries
,
577 !iterator__ignore_case(&iter
->base
));
582 git_array_pop(iter
->frames
);
588 GIT_INLINE(tree_iterator_entry
*) tree_iterator_current_entry(
589 tree_iterator_frame
*frame
)
591 return frame
->current
;
594 GIT_INLINE(int) tree_iterator_frame_push_neighbors(
596 tree_iterator_frame
*parent_frame
,
597 tree_iterator_frame
*frame
,
598 const char *filename
)
600 tree_iterator_entry
*entry
, *new_entry
;
601 git_tree
*tree
= NULL
;
602 git_tree_entry
*tree_entry
;
607 while (parent_frame
->next_idx
< parent_frame
->entries
.length
) {
608 entry
= parent_frame
->entries
.contents
[parent_frame
->next_idx
];
610 if (strcasecmp(filename
, entry
->tree_entry
->filename
) != 0)
613 if ((error
= git_tree_lookup(&tree
,
614 iter
->base
.repo
, entry
->tree_entry
->oid
)) < 0)
617 if (git_vector_insert(&parent_frame
->similar_trees
, tree
) < 0)
620 path
= git_array_alloc(parent_frame
->similar_paths
);
621 GIT_ERROR_CHECK_ALLOC(path
);
623 memset(path
, 0, sizeof(git_buf
));
625 if ((error
= tree_iterator_compute_path(path
, entry
)) < 0)
628 GIT_ERROR_CHECK_ALLOC_ADD(&new_size
,
629 frame
->entries
.length
, tree
->entries
.size
);
630 git_vector_size_hint(&frame
->entries
, new_size
);
632 git_array_foreach(tree
->entries
, i
, tree_entry
) {
633 new_entry
= git_pool_malloc(&iter
->entry_pool
, 1);
634 GIT_ERROR_CHECK_ALLOC(new_entry
);
636 new_entry
->tree_entry
= tree_entry
;
637 new_entry
->parent_path
= path
->ptr
;
639 if ((error
= git_vector_insert(&frame
->entries
, new_entry
)) < 0)
646 parent_frame
->next_idx
++;
652 GIT_INLINE(int) tree_iterator_frame_push(
653 tree_iterator
*iter
, tree_iterator_entry
*entry
)
655 tree_iterator_frame
*parent_frame
, *frame
;
656 git_tree
*tree
= NULL
;
659 if ((error
= git_tree_lookup(&tree
,
660 iter
->base
.repo
, entry
->tree_entry
->oid
)) < 0 ||
661 (error
= tree_iterator_frame_init(iter
, tree
, entry
)) < 0)
664 parent_frame
= tree_iterator_parent_frame(iter
);
665 frame
= tree_iterator_current_frame(iter
);
667 /* if we're case insensitive, then we may have another directory that
668 * is (case insensitively) equal to this one. coalesce those children
671 if (iterator__ignore_case(&iter
->base
))
672 error
= tree_iterator_frame_push_neighbors(iter
,
673 parent_frame
, frame
, entry
->tree_entry
->filename
);
680 static void tree_iterator_frame_pop(tree_iterator
*iter
)
682 tree_iterator_frame
*frame
;
687 assert(iter
->frames
.size
);
689 frame
= git_array_pop(iter
->frames
);
691 git_vector_free(&frame
->entries
);
692 git_tree_free(frame
->tree
);
695 buf
= git_array_pop(frame
->similar_paths
);
696 git_buf_dispose(buf
);
697 } while (buf
!= NULL
);
699 git_array_clear(frame
->similar_paths
);
701 git_vector_foreach(&frame
->similar_trees
, i
, tree
)
704 git_vector_free(&frame
->similar_trees
);
706 git_buf_dispose(&frame
->path
);
709 static int tree_iterator_current(
710 const git_index_entry
**out
, git_iterator
*i
)
712 tree_iterator
*iter
= (tree_iterator
*)i
;
714 if (!iterator__has_been_accessed(i
))
715 return iter
->base
.cb
->advance(out
, i
);
717 if (!iter
->frames
.size
) {
726 static void tree_iterator_set_current(
728 tree_iterator_frame
*frame
,
729 tree_iterator_entry
*entry
)
731 git_tree_entry
*tree_entry
= entry
->tree_entry
;
733 frame
->current
= entry
;
735 memset(&iter
->entry
, 0x0, sizeof(git_index_entry
));
737 iter
->entry
.mode
= tree_entry
->attr
;
738 iter
->entry
.path
= iter
->entry_path
.ptr
;
739 git_oid_cpy(&iter
->entry
.id
, tree_entry
->oid
);
742 static int tree_iterator_advance(const git_index_entry
**out
, git_iterator
*i
)
744 tree_iterator
*iter
= (tree_iterator
*)i
;
747 iter
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
749 /* examine tree entries until we find the next one to return */
751 tree_iterator_entry
*prev_entry
, *entry
;
752 tree_iterator_frame
*frame
;
755 if ((frame
= tree_iterator_current_frame(iter
)) == NULL
) {
756 error
= GIT_ITEROVER
;
760 /* no more entries in this frame. pop the frame out */
761 if (frame
->next_idx
== frame
->entries
.length
) {
762 tree_iterator_frame_pop(iter
);
766 /* we may have coalesced the contents of case-insensitively same-named
767 * directories, so do the sort now.
769 if (frame
->next_idx
== 0 && !git_vector_is_sorted(&frame
->entries
))
770 git_vector_sort(&frame
->entries
);
772 /* we have more entries in the current frame, that's our next entry */
773 prev_entry
= tree_iterator_current_entry(frame
);
774 entry
= frame
->entries
.contents
[frame
->next_idx
];
777 /* we can have collisions when iterating case insensitively. (eg,
778 * 'A/a' and 'a/A'). squash this one if it's already been seen.
780 if (iterator__ignore_case(&iter
->base
) &&
782 tree_iterator_entry_cmp_icase(prev_entry
, entry
) == 0)
785 if ((error
= tree_iterator_compute_path(&iter
->entry_path
, entry
)) < 0)
788 /* if this path is before our start, advance over this entry */
789 if (!iterator_has_started(&iter
->base
, iter
->entry_path
.ptr
, false))
792 /* if this path is after our end, stop */
793 if (iterator_has_ended(&iter
->base
, iter
->entry_path
.ptr
)) {
794 error
= GIT_ITEROVER
;
798 /* if we have a list of paths we're interested in, examine it */
799 if (!iterator_pathlist_next_is(&iter
->base
, iter
->entry_path
.ptr
))
802 is_tree
= git_tree_entry__is_tree(entry
->tree_entry
);
804 /* if we are *not* including trees then advance over this entry */
805 if (is_tree
&& !iterator__include_trees(iter
)) {
807 /* if we've found a tree (and are not returning it to the caller)
808 * and we are autoexpanding, then we want to return the first
809 * child. push the new directory and advance.
811 if (iterator__do_autoexpand(iter
)) {
812 if ((error
= tree_iterator_frame_push(iter
, entry
)) < 0)
819 tree_iterator_set_current(iter
, frame
, entry
);
821 /* if we are autoexpanding, then push this as a new frame, so that
822 * the next call to `advance` will dive into this directory.
824 if (is_tree
&& iterator__do_autoexpand(iter
))
825 error
= tree_iterator_frame_push(iter
, entry
);
831 *out
= (error
== 0) ? &iter
->entry
: NULL
;
836 static int tree_iterator_advance_into(
837 const git_index_entry
**out
, git_iterator
*i
)
839 tree_iterator
*iter
= (tree_iterator
*)i
;
840 tree_iterator_frame
*frame
;
841 tree_iterator_entry
*prev_entry
;
847 if ((frame
= tree_iterator_current_frame(iter
)) == NULL
)
850 /* get the last seen entry */
851 prev_entry
= tree_iterator_current_entry(frame
);
853 /* it's legal to call advance_into when auto-expand is on. in this case,
854 * we will have pushed a new (empty) frame on to the stack for this
855 * new directory. since it's empty, its current_entry should be null.
857 assert(iterator__do_autoexpand(i
) ^ (prev_entry
!= NULL
));
860 if (!git_tree_entry__is_tree(prev_entry
->tree_entry
))
863 if ((error
= tree_iterator_frame_push(iter
, prev_entry
)) < 0)
867 /* we've advanced into the directory in question, let advance
868 * find the first entry
870 return tree_iterator_advance(out
, i
);
873 static int tree_iterator_advance_over(
874 const git_index_entry
**out
,
875 git_iterator_status_t
*status
,
878 *status
= GIT_ITERATOR_STATUS_NORMAL
;
879 return git_iterator_advance(out
, i
);
882 static void tree_iterator_clear(tree_iterator
*iter
)
884 while (iter
->frames
.size
)
885 tree_iterator_frame_pop(iter
);
887 git_array_clear(iter
->frames
);
889 git_pool_clear(&iter
->entry_pool
);
890 git_buf_clear(&iter
->entry_path
);
892 iterator_clear(&iter
->base
);
895 static int tree_iterator_init(tree_iterator
*iter
)
899 git_pool_init(&iter
->entry_pool
, sizeof(tree_iterator_entry
));
901 if ((error
= tree_iterator_frame_init(iter
, iter
->root
, NULL
)) < 0)
904 iter
->base
.flags
&= ~GIT_ITERATOR_FIRST_ACCESS
;
909 static int tree_iterator_reset(git_iterator
*i
)
911 tree_iterator
*iter
= (tree_iterator
*)i
;
913 tree_iterator_clear(iter
);
914 return tree_iterator_init(iter
);
917 static void tree_iterator_free(git_iterator
*i
)
919 tree_iterator
*iter
= (tree_iterator
*)i
;
921 tree_iterator_clear(iter
);
923 git_tree_free(iter
->root
);
924 git_buf_dispose(&iter
->entry_path
);
927 int git_iterator_for_tree(
930 git_iterator_options
*options
)
935 static git_iterator_callbacks callbacks
= {
936 tree_iterator_current
,
937 tree_iterator_advance
,
938 tree_iterator_advance_into
,
939 tree_iterator_advance_over
,
947 return git_iterator_for_nothing(out
, options
);
949 iter
= git__calloc(1, sizeof(tree_iterator
));
950 GIT_ERROR_CHECK_ALLOC(iter
);
952 iter
->base
.type
= GIT_ITERATOR_TYPE_TREE
;
953 iter
->base
.cb
= &callbacks
;
955 if ((error
= iterator_init_common(&iter
->base
,
956 git_tree_owner(tree
), NULL
, options
)) < 0 ||
957 (error
= git_tree_dup(&iter
->root
, tree
)) < 0 ||
958 (error
= tree_iterator_init(iter
)) < 0)
965 git_iterator_free(&iter
->base
);
969 int git_iterator_current_tree_entry(
970 const git_tree_entry
**tree_entry
, git_iterator
*i
)
973 tree_iterator_frame
*frame
;
974 tree_iterator_entry
*entry
;
976 assert(i
->type
== GIT_ITERATOR_TYPE_TREE
);
978 iter
= (tree_iterator
*)i
;
980 frame
= tree_iterator_current_frame(iter
);
981 entry
= tree_iterator_current_entry(frame
);
983 *tree_entry
= entry
->tree_entry
;
987 int git_iterator_current_parent_tree(
988 const git_tree
**parent_tree
, git_iterator
*i
, size_t depth
)
991 tree_iterator_frame
*frame
;
993 assert(i
->type
== GIT_ITERATOR_TYPE_TREE
);
995 iter
= (tree_iterator
*)i
;
997 assert(depth
< iter
->frames
.size
);
998 frame
= &iter
->frames
.ptr
[iter
->frames
.size
-depth
-1];
1000 *parent_tree
= frame
->tree
;
1004 /* Filesystem iterator */
1009 iterator_pathlist_search_t match
;
1011 char path
[GIT_FLEX_ARRAY
];
1012 } filesystem_iterator_entry
;
1016 git_pool entry_pool
;
1021 } filesystem_iterator_frame
;
1028 unsigned int dirload_flags
;
1032 git_vector index_snapshot
;
1034 git_array_t(filesystem_iterator_frame
) frames
;
1035 git_ignores ignores
;
1037 /* info about the current entry */
1038 git_index_entry entry
;
1039 git_buf current_path
;
1040 int current_is_ignored
;
1042 /* temporary buffer for advance_over */
1044 } filesystem_iterator
;
1047 GIT_INLINE(filesystem_iterator_frame
*) filesystem_iterator_parent_frame(
1048 filesystem_iterator
*iter
)
1050 return iter
->frames
.size
> 1 ?
1051 &iter
->frames
.ptr
[iter
->frames
.size
-2] : NULL
;
1054 GIT_INLINE(filesystem_iterator_frame
*) filesystem_iterator_current_frame(
1055 filesystem_iterator
*iter
)
1057 return iter
->frames
.size
? &iter
->frames
.ptr
[iter
->frames
.size
-1] : NULL
;
1060 GIT_INLINE(filesystem_iterator_entry
*) filesystem_iterator_current_entry(
1061 filesystem_iterator_frame
*frame
)
1063 return frame
->next_idx
== 0 ?
1064 NULL
: frame
->entries
.contents
[frame
->next_idx
-1];
1067 static int filesystem_iterator_entry_cmp(const void *_a
, const void *_b
)
1069 const filesystem_iterator_entry
*a
= (const filesystem_iterator_entry
*)_a
;
1070 const filesystem_iterator_entry
*b
= (const filesystem_iterator_entry
*)_b
;
1072 return git__strcmp(a
->path
, b
->path
);
1075 static int filesystem_iterator_entry_cmp_icase(const void *_a
, const void *_b
)
1077 const filesystem_iterator_entry
*a
= (const filesystem_iterator_entry
*)_a
;
1078 const filesystem_iterator_entry
*b
= (const filesystem_iterator_entry
*)_b
;
1080 return git__strcasecmp(a
->path
, b
->path
);
1083 #define FILESYSTEM_MAX_DEPTH 100
1086 * Figure out if an entry is a submodule.
1088 * We consider it a submodule if the path is listed as a submodule in
1089 * either the tree or the index.
1091 static int filesystem_iterator_is_submodule(
1092 bool *out
, filesystem_iterator
*iter
, const char *path
, size_t path_len
)
1094 bool is_submodule
= false;
1099 /* first see if this path is a submodule in HEAD */
1101 git_tree_entry
*entry
;
1103 error
= git_tree_entry_bypath(&entry
, iter
->tree
, path
);
1105 if (error
< 0 && error
!= GIT_ENOTFOUND
)
1109 is_submodule
= (entry
->attr
== GIT_FILEMODE_COMMIT
);
1110 git_tree_entry_free(entry
);
1114 if (!is_submodule
&& iter
->base
.index
) {
1117 error
= git_index_snapshot_find(&pos
,
1118 &iter
->index_snapshot
, iter
->base
.entry_srch
, path
, path_len
, 0);
1120 if (error
< 0 && error
!= GIT_ENOTFOUND
)
1124 git_index_entry
*e
= git_vector_get(&iter
->index_snapshot
, pos
);
1125 is_submodule
= (e
->mode
== GIT_FILEMODE_COMMIT
);
1129 *out
= is_submodule
;
1133 static void filesystem_iterator_frame_push_ignores(
1134 filesystem_iterator
*iter
,
1135 filesystem_iterator_entry
*frame_entry
,
1136 filesystem_iterator_frame
*new_frame
)
1138 filesystem_iterator_frame
*previous_frame
;
1139 const char *path
= frame_entry
? frame_entry
->path
: "";
1141 if (!iterator__honor_ignores(&iter
->base
))
1144 if (git_ignore__lookup(&new_frame
->is_ignored
,
1145 &iter
->ignores
, path
, GIT_DIR_FLAG_TRUE
) < 0) {
1147 new_frame
->is_ignored
= GIT_IGNORE_NOTFOUND
;
1150 /* if this is not the top level directory... */
1152 const char *relative_path
;
1154 previous_frame
= filesystem_iterator_parent_frame(iter
);
1156 /* push new ignores for files in this directory */
1157 relative_path
= frame_entry
->path
+ previous_frame
->path_len
;
1159 /* inherit ignored from parent if no rule specified */
1160 if (new_frame
->is_ignored
<= GIT_IGNORE_NOTFOUND
)
1161 new_frame
->is_ignored
= previous_frame
->is_ignored
;
1163 git_ignore__push_dir(&iter
->ignores
, relative_path
);
1167 static void filesystem_iterator_frame_pop_ignores(
1168 filesystem_iterator
*iter
)
1170 if (iterator__honor_ignores(&iter
->base
))
1171 git_ignore__pop_dir(&iter
->ignores
);
1174 GIT_INLINE(bool) filesystem_iterator_examine_path(
1176 iterator_pathlist_search_t
*match_out
,
1177 filesystem_iterator
*iter
,
1178 filesystem_iterator_entry
*frame_entry
,
1183 iterator_pathlist_search_t match
= ITERATOR_PATHLIST_FULL
;
1185 *is_dir_out
= false;
1186 *match_out
= ITERATOR_PATHLIST_NONE
;
1188 if (iter
->base
.start_len
) {
1189 int cmp
= iter
->base
.strncomp(path
, iter
->base
.start
, path_len
);
1191 /* we haven't stat'ed `path` yet, so we don't yet know if it's a
1192 * directory or not. special case if the current path may be a
1193 * directory that matches the start prefix.
1196 if (iter
->base
.start
[path_len
] == '/')
1199 else if (iter
->base
.start
[path_len
] != '\0')
1207 if (iter
->base
.end_len
) {
1208 int cmp
= iter
->base
.strncomp(path
, iter
->base
.end
, iter
->base
.end_len
);
1214 /* if we have a pathlist that we're limiting to, examine this path now
1215 * to avoid a `stat` if we're not interested in the path.
1217 if (iter
->base
.pathlist
.length
) {
1218 /* if our parent was explicitly included, so too are we */
1219 if (frame_entry
&& frame_entry
->match
!= ITERATOR_PATHLIST_IS_PARENT
)
1220 match
= ITERATOR_PATHLIST_FULL
;
1222 match
= iterator_pathlist_search(&iter
->base
, path
, path_len
);
1224 if (match
== ITERATOR_PATHLIST_NONE
)
1227 /* Ensure that the pathlist entry lines up with what we expected */
1228 if (match
== ITERATOR_PATHLIST_IS_DIR
||
1229 match
== ITERATOR_PATHLIST_IS_PARENT
)
1233 *is_dir_out
= is_dir
;
1238 GIT_INLINE(bool) filesystem_iterator_is_dot_git(
1239 filesystem_iterator
*iter
, const char *path
, size_t path_len
)
1243 if (!iterator__ignore_dot_git(&iter
->base
))
1246 if ((len
= path_len
) < 4)
1249 if (path
[len
- 1] == '/')
1252 if (git__tolower(path
[len
- 1]) != 't' ||
1253 git__tolower(path
[len
- 2]) != 'i' ||
1254 git__tolower(path
[len
- 3]) != 'g' ||
1255 git__tolower(path
[len
- 4]) != '.')
1258 return (len
== 4 || path
[len
- 5] == '/');
1261 static int filesystem_iterator_entry_hash(
1262 filesystem_iterator
*iter
,
1263 filesystem_iterator_entry
*entry
)
1265 git_buf fullpath
= GIT_BUF_INIT
;
1268 if (S_ISDIR(entry
->st
.st_mode
)) {
1269 memset(&entry
->id
, 0, GIT_OID_RAWSZ
);
1273 if (iter
->base
.type
== GIT_ITERATOR_TYPE_WORKDIR
)
1274 return git_repository_hashfile(&entry
->id
,
1275 iter
->base
.repo
, entry
->path
, GIT_OBJECT_BLOB
, NULL
);
1277 if (!(error
= git_buf_joinpath(&fullpath
, iter
->root
, entry
->path
)))
1278 error
= git_odb_hashfile(&entry
->id
, fullpath
.ptr
, GIT_OBJECT_BLOB
);
1280 git_buf_dispose(&fullpath
);
1284 static int filesystem_iterator_entry_init(
1285 filesystem_iterator_entry
**out
,
1286 filesystem_iterator
*iter
,
1287 filesystem_iterator_frame
*frame
,
1290 struct stat
*statbuf
,
1291 iterator_pathlist_search_t pathlist_match
)
1293 filesystem_iterator_entry
*entry
;
1299 /* Make sure to append two bytes, one for the path's null
1300 * termination, one for a possible trailing '/' for folders.
1302 GIT_ERROR_CHECK_ALLOC_ADD(&entry_size
,
1303 sizeof(filesystem_iterator_entry
), path_len
);
1304 GIT_ERROR_CHECK_ALLOC_ADD(&entry_size
, entry_size
, 2);
1306 entry
= git_pool_malloc(&frame
->entry_pool
, entry_size
);
1307 GIT_ERROR_CHECK_ALLOC(entry
);
1309 entry
->path_len
= path_len
;
1310 entry
->match
= pathlist_match
;
1311 memcpy(entry
->path
, path
, path_len
);
1312 memcpy(&entry
->st
, statbuf
, sizeof(struct stat
));
1314 /* Suffix directory paths with a '/' */
1315 if (S_ISDIR(entry
->st
.st_mode
))
1316 entry
->path
[entry
->path_len
++] = '/';
1318 entry
->path
[entry
->path_len
] = '\0';
1320 if (iter
->base
.flags
& GIT_ITERATOR_INCLUDE_HASH
)
1321 error
= filesystem_iterator_entry_hash(iter
, entry
);
1329 static int filesystem_iterator_frame_push(
1330 filesystem_iterator
*iter
,
1331 filesystem_iterator_entry
*frame_entry
)
1333 filesystem_iterator_frame
*new_frame
= NULL
;
1334 git_path_diriter diriter
= GIT_PATH_DIRITER_INIT
;
1335 git_buf root
= GIT_BUF_INIT
;
1337 filesystem_iterator_entry
*entry
;
1338 struct stat statbuf
;
1342 if (iter
->frames
.size
== FILESYSTEM_MAX_DEPTH
) {
1343 git_error_set(GIT_ERROR_REPOSITORY
,
1344 "directory nesting too deep (%"PRIuZ
")", iter
->frames
.size
);
1348 new_frame
= git_array_alloc(iter
->frames
);
1349 GIT_ERROR_CHECK_ALLOC(new_frame
);
1351 memset(new_frame
, 0, sizeof(filesystem_iterator_frame
));
1354 git_buf_joinpath(&root
, iter
->root
, frame_entry
->path
);
1356 git_buf_puts(&root
, iter
->root
);
1358 if (git_buf_oom(&root
)) {
1363 new_frame
->path_len
= frame_entry
? frame_entry
->path_len
: 0;
1365 /* Any error here is equivalent to the dir not existing, skip over it */
1366 if ((error
= git_path_diriter_init(
1367 &diriter
, root
.ptr
, iter
->dirload_flags
)) < 0) {
1368 error
= GIT_ENOTFOUND
;
1372 if ((error
= git_vector_init(&new_frame
->entries
, 64,
1373 iterator__ignore_case(&iter
->base
) ?
1374 filesystem_iterator_entry_cmp_icase
:
1375 filesystem_iterator_entry_cmp
)) < 0)
1378 git_pool_init(&new_frame
->entry_pool
, 1);
1380 /* check if this directory is ignored */
1381 filesystem_iterator_frame_push_ignores(iter
, frame_entry
, new_frame
);
1383 while ((error
= git_path_diriter_next(&diriter
)) == 0) {
1384 iterator_pathlist_search_t pathlist_match
= ITERATOR_PATHLIST_FULL
;
1385 bool dir_expected
= false;
1387 if ((error
= git_path_diriter_fullpath(&path
, &path_len
, &diriter
)) < 0)
1390 assert(path_len
> iter
->root_len
);
1392 /* remove the prefix if requested */
1393 path
+= iter
->root_len
;
1394 path_len
-= iter
->root_len
;
1396 /* examine start / end and the pathlist to see if this path is in it.
1397 * note that since we haven't yet stat'ed the path, we cannot know
1398 * whether it's a directory yet or not, so this can give us an
1399 * expected type (S_IFDIR or S_IFREG) that we should examine)
1401 if (!filesystem_iterator_examine_path(&dir_expected
, &pathlist_match
,
1402 iter
, frame_entry
, path
, path_len
))
1405 /* TODO: don't need to stat if assume unchanged for this path and
1406 * we have an index, we can just copy the data out of it.
1409 if ((error
= git_path_diriter_stat(&statbuf
, &diriter
)) < 0) {
1410 /* file was removed between readdir and lstat */
1411 if (error
== GIT_ENOTFOUND
)
1414 /* treat the file as unreadable */
1415 memset(&statbuf
, 0, sizeof(statbuf
));
1416 statbuf
.st_mode
= GIT_FILEMODE_UNREADABLE
;
1421 iter
->base
.stat_calls
++;
1423 /* Ignore wacky things in the filesystem */
1424 if (!S_ISDIR(statbuf
.st_mode
) &&
1425 !S_ISREG(statbuf
.st_mode
) &&
1426 !S_ISLNK(statbuf
.st_mode
) &&
1427 statbuf
.st_mode
!= GIT_FILEMODE_UNREADABLE
)
1430 if (filesystem_iterator_is_dot_git(iter
, path
, path_len
))
1433 /* convert submodules to GITLINK and remove trailing slashes */
1434 if (S_ISDIR(statbuf
.st_mode
)) {
1435 bool submodule
= false;
1437 if ((error
= filesystem_iterator_is_submodule(&submodule
,
1438 iter
, path
, path_len
)) < 0)
1442 statbuf
.st_mode
= GIT_FILEMODE_COMMIT
;
1445 /* Ensure that the pathlist entry lines up with what we expected */
1446 else if (dir_expected
)
1449 if ((error
= filesystem_iterator_entry_init(&entry
,
1450 iter
, new_frame
, path
, path_len
, &statbuf
, pathlist_match
)) < 0)
1453 git_vector_insert(&new_frame
->entries
, entry
);
1456 if (error
== GIT_ITEROVER
)
1459 /* sort now that directory suffix is added */
1460 git_vector_sort(&new_frame
->entries
);
1464 git_array_pop(iter
->frames
);
1466 git_buf_dispose(&root
);
1467 git_path_diriter_free(&diriter
);
1471 GIT_INLINE(void) filesystem_iterator_frame_pop(filesystem_iterator
*iter
)
1473 filesystem_iterator_frame
*frame
;
1475 assert(iter
->frames
.size
);
1477 frame
= git_array_pop(iter
->frames
);
1478 filesystem_iterator_frame_pop_ignores(iter
);
1480 git_pool_clear(&frame
->entry_pool
);
1481 git_vector_free(&frame
->entries
);
1484 static void filesystem_iterator_set_current(
1485 filesystem_iterator
*iter
,
1486 filesystem_iterator_entry
*entry
)
1489 * Index entries are limited to 32 bit timestamps. We can safely
1490 * cast this since workdir times are only used in the cache; any
1491 * mismatch will cause a hash recomputation which is unfortunate
1492 * but affects only people who set their filetimes to 2038.
1493 * (Same with the file size.)
1495 iter
->entry
.ctime
.seconds
= (int32_t)entry
->st
.st_ctime
;
1496 iter
->entry
.mtime
.seconds
= (int32_t)entry
->st
.st_mtime
;
1498 #if defined(GIT_USE_NSEC)
1499 iter
->entry
.ctime
.nanoseconds
= entry
->st
.st_ctime_nsec
;
1500 iter
->entry
.mtime
.nanoseconds
= entry
->st
.st_mtime_nsec
;
1502 iter
->entry
.ctime
.nanoseconds
= 0;
1503 iter
->entry
.mtime
.nanoseconds
= 0;
1506 iter
->entry
.dev
= entry
->st
.st_dev
;
1507 iter
->entry
.ino
= entry
->st
.st_ino
;
1508 iter
->entry
.mode
= git_futils_canonical_mode(entry
->st
.st_mode
);
1509 iter
->entry
.uid
= entry
->st
.st_uid
;
1510 iter
->entry
.gid
= entry
->st
.st_gid
;
1511 iter
->entry
.file_size
= (uint32_t)entry
->st
.st_size
;
1513 if (iter
->base
.flags
& GIT_ITERATOR_INCLUDE_HASH
)
1514 git_oid_cpy(&iter
->entry
.id
, &entry
->id
);
1516 iter
->entry
.path
= entry
->path
;
1518 iter
->current_is_ignored
= GIT_IGNORE_UNCHECKED
;
1521 static int filesystem_iterator_current(
1522 const git_index_entry
**out
, git_iterator
*i
)
1524 filesystem_iterator
*iter
= (filesystem_iterator
*)i
;
1526 if (!iterator__has_been_accessed(i
))
1527 return iter
->base
.cb
->advance(out
, i
);
1529 if (!iter
->frames
.size
) {
1531 return GIT_ITEROVER
;
1534 *out
= &iter
->entry
;
1538 static int filesystem_iterator_is_dir(
1540 const filesystem_iterator
*iter
,
1541 const filesystem_iterator_entry
*entry
)
1544 git_buf fullpath
= GIT_BUF_INIT
;
1547 if (S_ISDIR(entry
->st
.st_mode
)) {
1552 if (!iterator__descend_symlinks(iter
) || !S_ISLNK(entry
->st
.st_mode
)) {
1557 if ((error
= git_buf_joinpath(&fullpath
, iter
->root
, entry
->path
)) < 0 ||
1558 (error
= p_stat(fullpath
.ptr
, &st
)) < 0)
1561 *is_dir
= S_ISDIR(st
.st_mode
);
1564 git_buf_dispose(&fullpath
);
1568 static int filesystem_iterator_advance(
1569 const git_index_entry
**out
, git_iterator
*i
)
1571 filesystem_iterator
*iter
= (filesystem_iterator
*)i
;
1575 iter
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
1577 /* examine filesystem entries until we find the next one to return */
1579 filesystem_iterator_frame
*frame
;
1580 filesystem_iterator_entry
*entry
;
1582 if ((frame
= filesystem_iterator_current_frame(iter
)) == NULL
) {
1583 error
= GIT_ITEROVER
;
1587 /* no more entries in this frame. pop the frame out */
1588 if (frame
->next_idx
== frame
->entries
.length
) {
1589 filesystem_iterator_frame_pop(iter
);
1593 /* we have more entries in the current frame, that's our next entry */
1594 entry
= frame
->entries
.contents
[frame
->next_idx
];
1597 if ((error
= filesystem_iterator_is_dir(&is_dir
, iter
, entry
)) < 0)
1601 if (iterator__do_autoexpand(iter
)) {
1602 error
= filesystem_iterator_frame_push(iter
, entry
);
1604 /* may get GIT_ENOTFOUND due to races or permission problems
1605 * that we want to quietly swallow
1607 if (error
== GIT_ENOTFOUND
)
1613 if (!iterator__include_trees(iter
))
1617 filesystem_iterator_set_current(iter
, entry
);
1622 *out
= (error
== 0) ? &iter
->entry
: NULL
;
1627 static int filesystem_iterator_advance_into(
1628 const git_index_entry
**out
, git_iterator
*i
)
1630 filesystem_iterator
*iter
= (filesystem_iterator
*)i
;
1631 filesystem_iterator_frame
*frame
;
1632 filesystem_iterator_entry
*prev_entry
;
1638 if ((frame
= filesystem_iterator_current_frame(iter
)) == NULL
)
1639 return GIT_ITEROVER
;
1641 /* get the last seen entry */
1642 prev_entry
= filesystem_iterator_current_entry(frame
);
1644 /* it's legal to call advance_into when auto-expand is on. in this case,
1645 * we will have pushed a new (empty) frame on to the stack for this
1646 * new directory. since it's empty, its current_entry should be null.
1648 assert(iterator__do_autoexpand(i
) ^ (prev_entry
!= NULL
));
1651 if (prev_entry
->st
.st_mode
!= GIT_FILEMODE_COMMIT
&&
1652 !S_ISDIR(prev_entry
->st
.st_mode
))
1655 if ((error
= filesystem_iterator_frame_push(iter
, prev_entry
)) < 0)
1659 /* we've advanced into the directory in question, let advance
1660 * find the first entry
1662 return filesystem_iterator_advance(out
, i
);
1665 int git_iterator_current_workdir_path(git_buf
**out
, git_iterator
*i
)
1667 filesystem_iterator
*iter
= (filesystem_iterator
*)i
;
1668 const git_index_entry
*entry
;
1670 if (i
->type
!= GIT_ITERATOR_TYPE_FS
&&
1671 i
->type
!= GIT_ITERATOR_TYPE_WORKDIR
) {
1676 git_buf_truncate(&iter
->current_path
, iter
->root_len
);
1678 if (git_iterator_current(&entry
, i
) < 0 ||
1679 git_buf_puts(&iter
->current_path
, entry
->path
) < 0)
1682 *out
= &iter
->current_path
;
1686 GIT_INLINE(git_dir_flag
) entry_dir_flag(git_index_entry
*entry
)
1688 #if defined(GIT_WIN32) && !defined(__MINGW32__)
1689 return (entry
&& entry
->mode
) ?
1690 (S_ISDIR(entry
->mode
) ? GIT_DIR_FLAG_TRUE
: GIT_DIR_FLAG_FALSE
) :
1691 GIT_DIR_FLAG_UNKNOWN
;
1694 return GIT_DIR_FLAG_UNKNOWN
;
1698 static void filesystem_iterator_update_ignored(filesystem_iterator
*iter
)
1700 filesystem_iterator_frame
*frame
;
1701 git_dir_flag dir_flag
= entry_dir_flag(&iter
->entry
);
1703 if (git_ignore__lookup(&iter
->current_is_ignored
,
1704 &iter
->ignores
, iter
->entry
.path
, dir_flag
) < 0) {
1706 iter
->current_is_ignored
= GIT_IGNORE_NOTFOUND
;
1709 /* use ignore from containing frame stack */
1710 if (iter
->current_is_ignored
<= GIT_IGNORE_NOTFOUND
) {
1711 frame
= filesystem_iterator_current_frame(iter
);
1712 iter
->current_is_ignored
= frame
->is_ignored
;
1716 GIT_INLINE(bool) filesystem_iterator_current_is_ignored(
1717 filesystem_iterator
*iter
)
1719 if (iter
->current_is_ignored
== GIT_IGNORE_UNCHECKED
)
1720 filesystem_iterator_update_ignored(iter
);
1722 return (iter
->current_is_ignored
== GIT_IGNORE_TRUE
);
1725 bool git_iterator_current_is_ignored(git_iterator
*i
)
1727 if (i
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1730 return filesystem_iterator_current_is_ignored((filesystem_iterator
*)i
);
1733 bool git_iterator_current_tree_is_ignored(git_iterator
*i
)
1735 filesystem_iterator
*iter
= (filesystem_iterator
*)i
;
1736 filesystem_iterator_frame
*frame
;
1738 if (i
->type
!= GIT_ITERATOR_TYPE_WORKDIR
)
1741 frame
= filesystem_iterator_current_frame(iter
);
1742 return (frame
->is_ignored
== GIT_IGNORE_TRUE
);
1745 static int filesystem_iterator_advance_over(
1746 const git_index_entry
**out
,
1747 git_iterator_status_t
*status
,
1750 filesystem_iterator
*iter
= (filesystem_iterator
*)i
;
1751 filesystem_iterator_frame
*current_frame
;
1752 filesystem_iterator_entry
*current_entry
;
1753 const git_index_entry
*entry
= NULL
;
1758 *status
= GIT_ITERATOR_STATUS_NORMAL
;
1760 assert(iterator__has_been_accessed(i
));
1762 current_frame
= filesystem_iterator_current_frame(iter
);
1763 assert(current_frame
);
1764 current_entry
= filesystem_iterator_current_entry(current_frame
);
1765 assert(current_entry
);
1767 if ((error
= git_iterator_current(&entry
, i
)) < 0)
1770 if (!S_ISDIR(entry
->mode
)) {
1771 if (filesystem_iterator_current_is_ignored(iter
))
1772 *status
= GIT_ITERATOR_STATUS_IGNORED
;
1774 return filesystem_iterator_advance(out
, i
);
1777 git_buf_clear(&iter
->tmp_buf
);
1778 if ((error
= git_buf_puts(&iter
->tmp_buf
, entry
->path
)) < 0)
1781 base
= iter
->tmp_buf
.ptr
;
1783 /* scan inside the directory looking for files. if we find nothing,
1784 * we will remain EMPTY. if we find any ignored item, upgrade EMPTY to
1785 * IGNORED. if we find a real actual item, upgrade all the way to NORMAL
1788 * however, if we're here looking for a pathlist item (but are not
1789 * actually in the pathlist ourselves) then start at FILTERED instead of
1790 * EMPTY. callers then know that this path was not something they asked
1793 *status
= current_entry
->match
== ITERATOR_PATHLIST_IS_PARENT
?
1794 GIT_ITERATOR_STATUS_FILTERED
: GIT_ITERATOR_STATUS_EMPTY
;
1796 while (entry
&& !iter
->base
.prefixcomp(entry
->path
, base
)) {
1797 if (filesystem_iterator_current_is_ignored(iter
)) {
1798 /* if we found an explicitly ignored item, then update from
1801 *status
= GIT_ITERATOR_STATUS_IGNORED
;
1802 } else if (S_ISDIR(entry
->mode
)) {
1803 error
= filesystem_iterator_advance_into(&entry
, i
);
1808 /* this directory disappeared, ignore it */
1809 else if (error
== GIT_ENOTFOUND
)
1812 /* a real error occurred */
1816 /* we found a non-ignored item, treat parent as untracked */
1817 *status
= GIT_ITERATOR_STATUS_NORMAL
;
1821 if ((error
= git_iterator_advance(&entry
, i
)) < 0)
1825 /* wrap up scan back to base directory */
1826 while (entry
&& !iter
->base
.prefixcomp(entry
->path
, base
)) {
1827 if ((error
= git_iterator_advance(&entry
, i
)) < 0)
1837 static void filesystem_iterator_clear(filesystem_iterator
*iter
)
1839 while (iter
->frames
.size
)
1840 filesystem_iterator_frame_pop(iter
);
1842 git_array_clear(iter
->frames
);
1843 git_ignore__free(&iter
->ignores
);
1845 git_buf_dispose(&iter
->tmp_buf
);
1847 iterator_clear(&iter
->base
);
1850 static int filesystem_iterator_init(filesystem_iterator
*iter
)
1854 if (iterator__honor_ignores(&iter
->base
) &&
1855 (error
= git_ignore__for_path(iter
->base
.repo
,
1856 ".gitignore", &iter
->ignores
)) < 0)
1859 if ((error
= filesystem_iterator_frame_push(iter
, NULL
)) < 0)
1862 iter
->base
.flags
&= ~GIT_ITERATOR_FIRST_ACCESS
;
1867 static int filesystem_iterator_reset(git_iterator
*i
)
1869 filesystem_iterator
*iter
= (filesystem_iterator
*)i
;
1871 filesystem_iterator_clear(iter
);
1872 return filesystem_iterator_init(iter
);
1875 static void filesystem_iterator_free(git_iterator
*i
)
1877 filesystem_iterator
*iter
= (filesystem_iterator
*)i
;
1878 git__free(iter
->root
);
1879 git_buf_dispose(&iter
->current_path
);
1880 git_tree_free(iter
->tree
);
1882 git_index_snapshot_release(&iter
->index_snapshot
, iter
->index
);
1883 filesystem_iterator_clear(iter
);
1886 static int iterator_for_filesystem(
1888 git_repository
*repo
,
1892 git_iterator_type_t type
,
1893 git_iterator_options
*options
)
1895 filesystem_iterator
*iter
;
1899 static git_iterator_callbacks callbacks
= {
1900 filesystem_iterator_current
,
1901 filesystem_iterator_advance
,
1902 filesystem_iterator_advance_into
,
1903 filesystem_iterator_advance_over
,
1904 filesystem_iterator_reset
,
1905 filesystem_iterator_free
1911 return git_iterator_for_nothing(out
, options
);
1913 iter
= git__calloc(1, sizeof(filesystem_iterator
));
1914 GIT_ERROR_CHECK_ALLOC(iter
);
1916 iter
->base
.type
= type
;
1917 iter
->base
.cb
= &callbacks
;
1919 root_len
= strlen(root
);
1921 iter
->root
= git__malloc(root_len
+2);
1922 GIT_ERROR_CHECK_ALLOC(iter
->root
);
1924 memcpy(iter
->root
, root
, root_len
);
1926 if (root_len
== 0 || root
[root_len
-1] != '/') {
1927 iter
->root
[root_len
] = '/';
1930 iter
->root
[root_len
] = '\0';
1931 iter
->root_len
= root_len
;
1933 if ((error
= git_buf_puts(&iter
->current_path
, iter
->root
)) < 0)
1936 if ((error
= iterator_init_common(&iter
->base
, repo
, index
, options
)) < 0)
1939 if (tree
&& (error
= git_tree_dup(&iter
->tree
, tree
)) < 0)
1943 (error
= git_index_snapshot_new(&iter
->index_snapshot
, index
)) < 0)
1946 iter
->index
= index
;
1947 iter
->dirload_flags
=
1948 (iterator__ignore_case(&iter
->base
) ? GIT_PATH_DIR_IGNORE_CASE
: 0) |
1949 (iterator__flag(&iter
->base
, PRECOMPOSE_UNICODE
) ?
1950 GIT_PATH_DIR_PRECOMPOSE_UNICODE
: 0);
1952 if ((error
= filesystem_iterator_init(iter
)) < 0)
1959 git_iterator_free(&iter
->base
);
1963 int git_iterator_for_filesystem(
1966 git_iterator_options
*options
)
1968 return iterator_for_filesystem(out
,
1969 NULL
, root
, NULL
, NULL
, GIT_ITERATOR_TYPE_FS
, options
);
1972 int git_iterator_for_workdir_ext(
1974 git_repository
*repo
,
1975 const char *repo_workdir
,
1978 git_iterator_options
*given_opts
)
1980 git_iterator_options options
= GIT_ITERATOR_OPTIONS_INIT
;
1982 if (!repo_workdir
) {
1983 if (git_repository__ensure_not_bare(repo
, "scan working directory") < 0)
1984 return GIT_EBAREREPO
;
1986 repo_workdir
= git_repository_workdir(repo
);
1989 /* upgrade to a workdir iterator, adding necessary internal flags */
1991 memcpy(&options
, given_opts
, sizeof(git_iterator_options
));
1993 options
.flags
|= GIT_ITERATOR_HONOR_IGNORES
|
1994 GIT_ITERATOR_IGNORE_DOT_GIT
;
1996 return iterator_for_filesystem(out
,
1997 repo
, repo_workdir
, index
, tree
, GIT_ITERATOR_TYPE_WORKDIR
, &options
);
2001 /* Index iterator */
2009 /* the pseudotree entry */
2010 git_index_entry tree_entry
;
2014 const git_index_entry
*entry
;
2017 static int index_iterator_current(
2018 const git_index_entry
**out
, git_iterator
*i
)
2020 index_iterator
*iter
= (index_iterator
*)i
;
2022 if (!iterator__has_been_accessed(i
))
2023 return iter
->base
.cb
->advance(out
, i
);
2025 if (iter
->entry
== NULL
) {
2027 return GIT_ITEROVER
;
2034 static bool index_iterator_create_pseudotree(
2035 const git_index_entry
**out
,
2036 index_iterator
*iter
,
2039 const char *prev_path
, *relative_path
, *dirsep
;
2042 prev_path
= iter
->entry
? iter
->entry
->path
: "";
2044 /* determine if the new path is in a different directory from the old */
2045 common_len
= git_path_common_dirlen(prev_path
, path
);
2046 relative_path
= path
+ common_len
;
2048 if ((dirsep
= strchr(relative_path
, '/')) == NULL
)
2051 git_buf_clear(&iter
->tree_buf
);
2052 git_buf_put(&iter
->tree_buf
, path
, (dirsep
- path
) + 1);
2054 iter
->tree_entry
.mode
= GIT_FILEMODE_TREE
;
2055 iter
->tree_entry
.path
= iter
->tree_buf
.ptr
;
2057 *out
= &iter
->tree_entry
;
2061 static int index_iterator_skip_pseudotree(index_iterator
*iter
)
2063 assert(iterator__has_been_accessed(&iter
->base
));
2064 assert(S_ISDIR(iter
->entry
->mode
));
2067 const git_index_entry
*next_entry
= NULL
;
2069 if (++iter
->next_idx
>= iter
->entries
.length
)
2070 return GIT_ITEROVER
;
2072 next_entry
= iter
->entries
.contents
[iter
->next_idx
];
2074 if (iter
->base
.strncomp(iter
->tree_buf
.ptr
, next_entry
->path
,
2075 iter
->tree_buf
.size
) != 0)
2079 iter
->skip_tree
= false;
2083 static int index_iterator_advance(
2084 const git_index_entry
**out
, git_iterator
*i
)
2086 index_iterator
*iter
= (index_iterator
*)i
;
2087 const git_index_entry
*entry
= NULL
;
2091 iter
->base
.flags
|= GIT_ITERATOR_FIRST_ACCESS
;
2094 if (iter
->next_idx
>= iter
->entries
.length
) {
2095 error
= GIT_ITEROVER
;
2099 /* we were not asked to expand this pseudotree. advance over it. */
2100 if (iter
->skip_tree
) {
2101 index_iterator_skip_pseudotree(iter
);
2105 entry
= iter
->entries
.contents
[iter
->next_idx
];
2106 is_submodule
= S_ISGITLINK(entry
->mode
);
2108 if (!iterator_has_started(&iter
->base
, entry
->path
, is_submodule
)) {
2113 if (iterator_has_ended(&iter
->base
, entry
->path
)) {
2114 error
= GIT_ITEROVER
;
2118 /* if we have a list of paths we're interested in, examine it */
2119 if (!iterator_pathlist_next_is(&iter
->base
, entry
->path
)) {
2124 /* if this is a conflict, skip it unless we're including conflicts */
2125 if (git_index_entry_is_conflict(entry
) &&
2126 !iterator__include_conflicts(&iter
->base
)) {
2131 /* we've found what will be our next _file_ entry. but if we are
2132 * returning trees entries, we may need to return a pseudotree
2133 * entry that will contain this. don't advance over this entry,
2134 * though, we still need to return it on the next `advance`.
2136 if (iterator__include_trees(&iter
->base
) &&
2137 index_iterator_create_pseudotree(&entry
, iter
, entry
->path
)) {
2139 /* Note whether this pseudo tree should be expanded or not */
2140 iter
->skip_tree
= iterator__dont_autoexpand(&iter
->base
);
2148 iter
->entry
= (error
== 0) ? entry
: NULL
;
2156 static int index_iterator_advance_into(
2157 const git_index_entry
**out
, git_iterator
*i
)
2159 index_iterator
*iter
= (index_iterator
*)i
;
2161 if (! S_ISDIR(iter
->tree_entry
.mode
)) {
2168 iter
->skip_tree
= false;
2169 return index_iterator_advance(out
, i
);
2172 static int index_iterator_advance_over(
2173 const git_index_entry
**out
,
2174 git_iterator_status_t
*status
,
2177 index_iterator
*iter
= (index_iterator
*)i
;
2178 const git_index_entry
*entry
;
2181 if ((error
= index_iterator_current(&entry
, i
)) < 0)
2184 if (S_ISDIR(entry
->mode
))
2185 index_iterator_skip_pseudotree(iter
);
2187 *status
= GIT_ITERATOR_STATUS_NORMAL
;
2188 return index_iterator_advance(out
, i
);
2191 static void index_iterator_clear(index_iterator
*iter
)
2193 iterator_clear(&iter
->base
);
2196 static int index_iterator_init(index_iterator
*iter
)
2198 iter
->base
.flags
&= ~GIT_ITERATOR_FIRST_ACCESS
;
2200 iter
->skip_tree
= false;
2204 static int index_iterator_reset(git_iterator
*i
)
2206 index_iterator
*iter
= (index_iterator
*)i
;
2208 index_iterator_clear(iter
);
2209 return index_iterator_init(iter
);
2212 static void index_iterator_free(git_iterator
*i
)
2214 index_iterator
*iter
= (index_iterator
*)i
;
2216 git_index_snapshot_release(&iter
->entries
, iter
->base
.index
);
2217 git_buf_dispose(&iter
->tree_buf
);
2220 int git_iterator_for_index(
2222 git_repository
*repo
,
2224 git_iterator_options
*options
)
2226 index_iterator
*iter
;
2229 static git_iterator_callbacks callbacks
= {
2230 index_iterator_current
,
2231 index_iterator_advance
,
2232 index_iterator_advance_into
,
2233 index_iterator_advance_over
,
2234 index_iterator_reset
,
2241 return git_iterator_for_nothing(out
, options
);
2243 iter
= git__calloc(1, sizeof(index_iterator
));
2244 GIT_ERROR_CHECK_ALLOC(iter
);
2246 iter
->base
.type
= GIT_ITERATOR_TYPE_INDEX
;
2247 iter
->base
.cb
= &callbacks
;
2249 if ((error
= iterator_init_common(&iter
->base
, repo
, index
, options
)) < 0 ||
2250 (error
= git_index_snapshot_new(&iter
->entries
, index
)) < 0 ||
2251 (error
= index_iterator_init(iter
)) < 0)
2254 git_vector_set_cmp(&iter
->entries
, iterator__ignore_case(&iter
->base
) ?
2255 git_index_entry_icmp
: git_index_entry_cmp
);
2256 git_vector_sort(&iter
->entries
);
2262 git_iterator_free(&iter
->base
);
2269 int git_iterator_reset_range(
2270 git_iterator
*i
, const char *start
, const char *end
)
2272 if (iterator_reset_range(i
, start
, end
) < 0)
2275 return i
->cb
->reset(i
);
2278 void git_iterator_set_ignore_case(git_iterator
*i
, bool ignore_case
)
2280 assert(!iterator__has_been_accessed(i
));
2281 iterator_set_ignore_case(i
, ignore_case
);
2284 void git_iterator_free(git_iterator
*iter
)
2289 iter
->cb
->free(iter
);
2291 git_vector_free(&iter
->pathlist
);
2292 git__free(iter
->start
);
2293 git__free(iter
->end
);
2295 memset(iter
, 0, sizeof(*iter
));
2300 int git_iterator_foreach(
2301 git_iterator
*iterator
,
2302 git_iterator_foreach_cb cb
,
2305 const git_index_entry
*iterator_item
;
2308 if ((error
= git_iterator_current(&iterator_item
, iterator
)) < 0)
2311 if ((error
= cb(iterator_item
, data
)) != 0)
2315 if ((error
= git_iterator_advance(&iterator_item
, iterator
)) < 0)
2318 if ((error
= cb(iterator_item
, data
)) != 0)
2323 if (error
== GIT_ITEROVER
)
2329 int git_iterator_walk(
2330 git_iterator
**iterators
,
2332 git_iterator_walk_cb cb
,
2335 const git_index_entry
**iterator_item
; /* next in each iterator */
2336 const git_index_entry
**cur_items
; /* current path in each iter */
2337 const git_index_entry
*first_match
;
2341 iterator_item
= git__calloc(cnt
, sizeof(git_index_entry
*));
2342 cur_items
= git__calloc(cnt
, sizeof(git_index_entry
*));
2344 GIT_ERROR_CHECK_ALLOC(iterator_item
);
2345 GIT_ERROR_CHECK_ALLOC(cur_items
);
2347 /* Set up the iterators */
2348 for (i
= 0; i
< cnt
; i
++) {
2349 error
= git_iterator_current(&iterator_item
[i
], iterators
[i
]);
2351 if (error
< 0 && error
!= GIT_ITEROVER
)
2356 for (i
= 0; i
< cnt
; i
++)
2357 cur_items
[i
] = NULL
;
2361 /* Find the next path(s) to consume from each iterator */
2362 for (i
= 0; i
< cnt
; i
++) {
2363 if (iterator_item
[i
] == NULL
)
2366 if (first_match
== NULL
) {
2367 first_match
= iterator_item
[i
];
2368 cur_items
[i
] = iterator_item
[i
];
2370 int path_diff
= git_index_entry_cmp(iterator_item
[i
], first_match
);
2372 if (path_diff
< 0) {
2373 /* Found an index entry that sorts before the one we're
2374 * looking at. Forget that we've seen the other and
2375 * look at the other iterators for this path.
2377 for (j
= 0; j
< i
; j
++)
2378 cur_items
[j
] = NULL
;
2380 first_match
= iterator_item
[i
];
2381 cur_items
[i
] = iterator_item
[i
];
2382 } else if (path_diff
== 0) {
2383 cur_items
[i
] = iterator_item
[i
];
2388 if (first_match
== NULL
)
2391 if ((error
= cb(cur_items
, data
)) != 0)
2394 /* Advance each iterator that participated */
2395 for (i
= 0; i
< cnt
; i
++) {
2396 if (cur_items
[i
] == NULL
)
2399 error
= git_iterator_advance(&iterator_item
[i
], iterators
[i
]);
2401 if (error
< 0 && error
!= GIT_ITEROVER
)
2407 git__free((git_index_entry
**)iterator_item
);
2408 git__free((git_index_entry
**)cur_items
);
2410 if (error
== GIT_ITEROVER
)