summaryrefslogtreecommitdiff
path: root/src/iterator.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/iterator.c')
-rw-r--r--src/iterator.c214
1 files changed, 145 insertions, 69 deletions
diff --git a/src/iterator.c b/src/iterator.c
index ff08c1ce0..4360b99ad 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -46,6 +46,9 @@
#define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
#define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
+#define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
+#define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
+
#define iterator__end(I) ((git_iterator *)(I))->end
#define iterator__past_end(I,PATH) \
(iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
@@ -68,6 +71,8 @@ static int iterator__reset_range(
GITERR_CHECK_ALLOC(iter->end);
}
+ iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
+
return 0;
}
@@ -109,7 +114,7 @@ static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
{
GIT_UNUSED(i);
iterator__clear_entry(e);
- return 0;
+ return GIT_ITEROVER;
}
static int empty_iterator__seek(git_iterator *i, const char *p)
@@ -193,6 +198,7 @@ typedef struct {
git_buf path;
int path_ambiguities;
bool path_has_filename;
+ bool entry_is_current;
int (*strncomp)(const char *a, const char *b, size_t sz);
} tree_iterator;
@@ -266,9 +272,28 @@ static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
((tree_iterator *)p)->strncomp);
}
+static bool tree_iterator__move_to_next(
+ tree_iterator *ti, tree_iterator_frame *tf)
+{
+ if (tf->next > tf->current + 1)
+ ti->path_ambiguities--;
+
+ if (!tf->up) { /* at root */
+ tf->current = tf->next;
+ return false;
+ }
+
+ for (; tf->current < tf->next; tf->current++) {
+ git_tree_free(tf->entries[tf->current]->tree);
+ tf->entries[tf->current]->tree = NULL;
+ }
+
+ return (tf->current < tf->n_entries);
+}
+
static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
{
- int error;
+ int error = 0;
const git_tree_entry *te, *last = NULL;
tf->next = tf->current;
@@ -279,18 +304,23 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
break;
- /* load trees for items in [current,next) range */
- if (git_tree_entry__is_tree(te) &&
- (error = git_tree_lookup(
- &tf->entries[tf->next]->tree, ti->base.repo, &te->oid)) < 0)
- return error;
+ /* try to load trees for items in [current,next) range */
+ if (!error && git_tree_entry__is_tree(te))
+ error = git_tree_lookup(
+ &tf->entries[tf->next]->tree, ti->base.repo, &te->oid);
}
if (tf->next > tf->current + 1)
ti->path_ambiguities++;
+ /* if a tree lookup failed, advance over this span and return failure */
+ if (error < 0) {
+ tree_iterator__move_to_next(ti, tf);
+ return error;
+ }
+
if (last && !tree_iterator__current_filename(ti, last))
- return -1;
+ return -1; /* must have been allocation failure */
return 0;
}
@@ -308,7 +338,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
size_t i, n_entries = 0;
if (head->current >= head->n_entries || !head->entries[head->current]->tree)
- return 0;
+ return GIT_ITEROVER;
for (i = head->current; i < head->next; ++i)
n_entries += git_tree_entrycount(head->entries[i]->tree);
@@ -359,7 +389,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
}
}
- ti->path_has_filename = false;
+ ti->path_has_filename = ti->entry_is_current = false;
if ((error = tree_iterator__set_next(ti, tf)) < 0)
return error;
@@ -371,25 +401,6 @@ static int tree_iterator__push_frame(tree_iterator *ti)
return 0;
}
-static bool tree_iterator__move_to_next(
- tree_iterator *ti, tree_iterator_frame *tf)
-{
- if (tf->next > tf->current + 1)
- ti->path_ambiguities--;
-
- if (!tf->up) { /* at root */
- tf->current = tf->next;
- return false;
- }
-
- for (; tf->current < tf->next; tf->current++) {
- git_tree_free(tf->entries[tf->current]->tree);
- tf->entries[tf->current]->tree = NULL;
- }
-
- return (tf->current < tf->n_entries);
-}
-
static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
{
tree_iterator_frame *tf = ti->head;
@@ -412,7 +423,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
return true;
}
-static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
+static void tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
{
while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
@@ -421,22 +432,18 @@ static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
ti->path_ambiguities = 0;
git_buf_clear(&ti->path);
}
-
- return 0;
}
-static int tree_iterator__current(
- const git_index_entry **entry, git_iterator *self)
+static int tree_iterator__update_entry(tree_iterator *ti)
{
- tree_iterator *ti = (tree_iterator *)self;
- tree_iterator_frame *tf = ti->head;
- const git_tree_entry *te;
+ tree_iterator_frame *tf;
+ const git_tree_entry *te;
- iterator__clear_entry(entry);
+ if (ti->entry_is_current)
+ return 0;
- if (tf->current >= tf->n_entries)
- return 0;
- te = tf->entries[tf->current]->te;
+ tf = ti->head;
+ te = tf->entries[tf->current]->te;
ti->entry.mode = te->attr;
git_oid_cpy(&ti->entry.oid, &te->oid);
@@ -447,12 +454,36 @@ static int tree_iterator__current(
if (ti->path_ambiguities > 0)
tree_iterator__rewrite_filename(ti);
- if (iterator__past_end(ti, ti->entry.path))
- return tree_iterator__pop_all(ti, true, false);
+ if (iterator__past_end(ti, ti->entry.path)) {
+ tree_iterator__pop_all(ti, true, false);
+ return GIT_ITEROVER;
+ }
+
+ ti->entry_is_current = true;
+
+ return 0;
+}
+
+static int tree_iterator__current(
+ const git_index_entry **entry, git_iterator *self)
+{
+ int error;
+ tree_iterator *ti = (tree_iterator *)self;
+ tree_iterator_frame *tf = ti->head;
+
+ iterator__clear_entry(entry);
+
+ if (tf->current >= tf->n_entries)
+ return GIT_ITEROVER;
+
+ if ((error = tree_iterator__update_entry(ti)) < 0)
+ return error;
if (entry)
*entry = &ti->entry;
+ ti->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
+
return 0;
}
@@ -464,8 +495,10 @@ static int tree_iterator__advance_into(
iterator__clear_entry(entry);
- if (tree_iterator__at_tree(ti) &&
- !(error = tree_iterator__push_frame(ti)))
+ if (tree_iterator__at_tree(ti))
+ error = tree_iterator__push_frame(ti);
+
+ if (!error && entry)
error = tree_iterator__current(entry, self);
return error;
@@ -480,8 +513,11 @@ static int tree_iterator__advance(
iterator__clear_entry(entry);
- if (tf->current > tf->n_entries)
- return 0;
+ if (tf->current >= tf->n_entries)
+ return GIT_ITEROVER;
+
+ if (!iterator__has_been_accessed(ti))
+ return tree_iterator__current(entry, self);
if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
tree_iterator__at_tree(ti))
@@ -489,7 +525,7 @@ static int tree_iterator__advance(
if (ti->path_has_filename) {
git_buf_rtruncate_at_char(&ti->path, '/');
- ti->path_has_filename = false;
+ ti->path_has_filename = ti->entry_is_current = false;
}
/* scan forward and up, advancing in frame or popping frame when done */
@@ -699,7 +735,9 @@ static int index_iterator__current(
if (entry)
*entry = ie;
- return 0;
+ ii->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
+
+ return (ie != NULL) ? 0 : GIT_ITEROVER;
}
static int index_iterator__at_end(git_iterator *self)
@@ -715,6 +753,9 @@ static int index_iterator__advance(
size_t entrycount = git_index_entrycount(ii->index);
const git_index_entry *ie;
+ if (!iterator__has_been_accessed(ii))
+ return index_iterator__current(entry, self);
+
if (index_iterator__at_tree(ii)) {
if (iterator__do_autoexpand(ii)) {
ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
@@ -906,6 +947,8 @@ static void fs_iterator__pop_frame(
}
static int fs_iterator__update_entry(fs_iterator *fi);
+static int fs_iterator__advance_over(
+ const git_index_entry **entry, git_iterator *self);
static int fs_iterator__entry_cmp(const void *i, const void *item)
{
@@ -945,7 +988,13 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
fi->base.start, fi->base.end, &ff->entries);
- if (error < 0 || ff->entries.length == 0) {
+ if (error < 0) {
+ fs_iterator__free_frame(ff);
+ fs_iterator__advance_over(NULL, (git_iterator *)fi);
+ return error;
+ }
+
+ if (ff->entries.length == 0) {
fs_iterator__free_frame(ff);
return GIT_ENOTFOUND;
}
@@ -966,9 +1015,14 @@ static int fs_iterator__current(
const git_index_entry **entry, git_iterator *self)
{
fs_iterator *fi = (fs_iterator *)self;
+ const git_index_entry *fe = (fi->entry.path == NULL) ? NULL : &fi->entry;
+
if (entry)
- *entry = (fi->entry.path == NULL) ? NULL : &fi->entry;
- return 0;
+ *entry = fe;
+
+ fi->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
+
+ return (fe != NULL) ? 0 : GIT_ITEROVER;
}
static int fs_iterator__at_end(git_iterator *self)
@@ -998,6 +1052,9 @@ static int fs_iterator__advance_into(
if (!error && entry)
error = fs_iterator__current(entry, iter);
+ if (!error && !fi->entry.path)
+ error = GIT_ITEROVER;
+
return error;
}
@@ -1035,6 +1092,9 @@ static int fs_iterator__advance(
{
fs_iterator *fi = (fs_iterator *)self;
+ if (!iterator__has_been_accessed(fi))
+ return fs_iterator__current(entry, self);
+
/* given include_trees & autoexpand, we might have to go into a tree */
if (iterator__do_autoexpand(fi) &&
fi->entry.path != NULL &&
@@ -1063,18 +1123,23 @@ static int fs_iterator__seek(git_iterator *self, const char *prefix)
static int fs_iterator__reset(
git_iterator *self, const char *start, const char *end)
{
+ int error;
fs_iterator *fi = (fs_iterator *)self;
while (fi->stack != NULL && fi->stack->next != NULL)
fs_iterator__pop_frame(fi, fi->stack, false);
fi->depth = 0;
- if (iterator__reset_range(self, start, end) < 0)
- return -1;
+ if ((error = iterator__reset_range(self, start, end)) < 0)
+ return error;
fs_iterator__seek_frame_start(fi, fi->stack);
- return fs_iterator__update_entry(fi);
+ error = fs_iterator__update_entry(fi);
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ return error;
}
static void fs_iterator__free(git_iterator *self)
@@ -1089,18 +1154,23 @@ static void fs_iterator__free(git_iterator *self)
static int fs_iterator__update_entry(fs_iterator *fi)
{
- git_path_with_stat *ps =
- git_vector_get(&fi->stack->entries, fi->stack->index);
+ git_path_with_stat *ps;
- git_buf_truncate(&fi->path, fi->root_len);
memset(&fi->entry, 0, sizeof(fi->entry));
+ if (!fi->stack)
+ return GIT_ITEROVER;
+
+ ps = git_vector_get(&fi->stack->entries, fi->stack->index);
if (!ps)
- return 0;
+ return GIT_ITEROVER;
+
+ git_buf_truncate(&fi->path, fi->root_len);
if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
return -1;
+
if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
- return 0;
+ return GIT_ITEROVER;
fi->entry.path = ps->path;
git_index_entry__init_from_stat(&fi->entry, &ps->st);
@@ -1114,8 +1184,13 @@ static int fs_iterator__update_entry(fs_iterator *fi)
return fs_iterator__advance_over(NULL, (git_iterator *)fi);
/* if this is a tree and trees aren't included, then skip */
- if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi))
- return git_iterator_advance(NULL, (git_iterator *)fi);
+ if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi)) {
+ int error = fs_iterator__advance_into(NULL, (git_iterator *)fi);
+ if (error != GIT_ENOTFOUND)
+ return error;
+ giterr_clear();
+ return fs_iterator__advance_over(NULL, (git_iterator *)fi);
+ }
return 0;
}
@@ -1131,13 +1206,14 @@ static int fs_iterator__initialize(
}
fi->root_len = fi->path.size;
- if ((error = fs_iterator__expand_dir(fi)) == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
- if (error) {
- git_iterator_free((git_iterator *)fi);
- fi = NULL;
+ if ((error = fs_iterator__expand_dir(fi)) < 0) {
+ if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
+ giterr_clear();
+ error = 0;
+ } else {
+ git_iterator_free((git_iterator *)fi);
+ fi = NULL;
+ }
}
*out = (git_iterator *)fi;