diff --git a/src/iterator.c b/src/iterator.c index 84664c0f8..53ec6f61b 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -189,44 +189,12 @@ typedef struct { tree_iterator_frame *head, *top; git_index_entry entry; git_buf path; + int path_ambiguities; bool path_has_filename; int (*strcomp)(const char *a, const char *b); int (*strncomp)(const char *a, const char *b, size_t sz); } tree_iterator; -static char *tree_iterator__current_filename( - tree_iterator *ti, const git_tree_entry *te) -{ - if (!ti->path_has_filename) { - if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0) - return NULL; - - if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0) - return NULL; - - ti->path_has_filename = true; - } - - return ti->path.ptr; -} - -static int tree_iterator__create_top_frame(tree_iterator *ti, git_tree *tree) -{ - size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry); - tree_iterator_frame *top = git__calloc(sz, sizeof(char)); - GITERR_CHECK_ALLOC(top); - - top->n_entries = 1; - top->next = 1; - top->start = ti->base.start; - top->startlen = top->start ? strlen(top->start) : 0; - top->entries[0].tree = tree; - - ti->head = ti->top = top; - - return 0; -} - static const git_tree_entry *tree_iterator__get_tree_entry( tree_iterator_frame *tf, const tree_iterator_entry *entry, size_t i) { @@ -245,6 +213,45 @@ static const git_tree_entry *tree_iterator__get_tree_entry( return git_tree_entry_byindex(tree, entry->parent_tree_index); } +static char *tree_iterator__current_filename( + tree_iterator *ti, const git_tree_entry *te) +{ + if (!ti->path_has_filename) { + if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0) + return NULL; + + if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0) + return NULL; + + ti->path_has_filename = true; + } + + return ti->path.ptr; +} + +static void tree_iterator__rewrite_filename(tree_iterator *ti) +{ + tree_iterator_frame *scan = ti->head; + size_t current = scan->current; + ssize_t strpos = ti->path.size; + const git_tree_entry *te; + + while (scan && scan->parent) { + tree_iterator_entry *entry = &scan->entries[current]; + + te = tree_iterator__get_tree_entry(scan, entry, 0); + if (!te) + break; + + strpos -= te->filename_len; + memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len); + strpos -= 1; /* separator */ + + current = entry->parent_entry_index; + scan = scan->parent; + } +} + static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) { tree_iterator_frame *tf = p; @@ -290,6 +297,9 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) last_te = te; } + if (tf->next > tf->current + 1) + ti->path_ambiguities++; + if (last_te && !tree_iterator__current_filename(ti, last_te)) return -1; @@ -376,8 +386,12 @@ static int tree_iterator__push_frame(tree_iterator *ti) return 0; } -static bool tree_iterator__move_to_next(tree_iterator_frame *tf) +static bool tree_iterator__move_to_next( + tree_iterator *ti, tree_iterator_frame *tf) { + if (tf->next > tf->current + 1) + ti->path_ambiguities--; + while (tf->current < tf->next) { if (tf->parent && tf->entries[tf->current].tree) { git_tree_free(tf->entries[tf->current].tree); @@ -396,7 +410,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti) if (!tf->parent) return false; - tree_iterator__move_to_next(tf); + tree_iterator__move_to_next(ti, tf); ti->head = tf->parent; ti->head->child = NULL; @@ -427,6 +441,9 @@ static int tree_iterator__current( if (ti->entry.path == NULL) return -1; + if (ti->path_ambiguities > 0) + tree_iterator__rewrite_filename(ti); + if (iterator__past_end(ti, ti->entry.path)) { while (tree_iterator__pop_frame(ti)) /* pop to top */; ti->head->current = ti->head->n_entries; @@ -478,7 +495,7 @@ static int tree_iterator__advance( } /* scan forward and up, advancing in frame or popping frame when done */ - while (!tree_iterator__move_to_next(tf) && tree_iterator__pop_frame(ti)) + while (!tree_iterator__move_to_next(ti, tf) && tree_iterator__pop_frame(ti)) tf = ti->head; /* find next and load trees */ @@ -510,6 +527,7 @@ static int tree_iterator__reset( if (iterator__reset_range(self, start, end) < 0) return -1; git_buf_clear(&ti->path); + ti->path_ambiguities = 0; return tree_iterator__push_frame(ti); /* re-expand top tree */ } @@ -537,6 +555,23 @@ static void tree_iterator__free(git_iterator *self) git_buf_free(&ti->path); } +static int tree_iterator__create_top_frame(tree_iterator *ti, git_tree *tree) +{ + size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry); + tree_iterator_frame *top = git__calloc(sz, sizeof(char)); + GITERR_CHECK_ALLOC(top); + + top->n_entries = 1; + top->next = 1; + top->start = ti->base.start; + top->startlen = top->start ? strlen(top->start) : 0; + top->entries[0].tree = tree; + + ti->head = ti->top = top; + + return 0; +} + int git_iterator_for_tree( git_iterator **iter, git_tree *tree, diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 63613e903..804bc8324 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -456,7 +456,7 @@ void test_repo_iterator__tree_case_conflicts(void) const char *expect_cs[] = { "A/1.file", "A/3.file", "a/2.file", "a/4.file" }; const char *expect_ci[] = { - "a/1.file", "a/2.file", "a/3.file", "a/4.file" }; + "A/1.file", "a/2.file", "A/3.file", "a/4.file" }; g_repo = cl_git_sandbox_init("icase"); @@ -479,7 +479,7 @@ void test_repo_iterator__tree_case_conflicts(void) cl_git_pass(git_iterator_for_tree( &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); - expect_iterator_items(i, 4, expect_ci, -4, expect_ci); + expect_iterator_items(i, 4, expect_ci, 4, expect_ci); git_iterator_free(i); git_tree_free(tree);