From 61c7b61e6fe2542deeb8d2aadbea90a8f5b3c9cd Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sun, 10 Mar 2013 22:38:53 -0700 Subject: Use correct case path in icase tree iterator If there are case-ambiguities in the path of a case insensitive tree iterator, it will now rewrite the entire path when it gives the path name to an entry, so a tree with "A/b/C/d.txt" and "a/B/c/E.txt" will give the true full paths (instead of case- folding them both to "A/B/C/d.txt" or "a/b/c/E.txt" or something like that. --- src/iterator.c | 95 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 30 deletions(-) (limited to 'src/iterator.c') diff --git a/src/iterator.c b/src/iterator.c index 84664c0f8..53ec6f61b 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -189,11 +189,30 @@ 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 const git_tree_entry *tree_iterator__get_tree_entry( + tree_iterator_frame *tf, const tree_iterator_entry *entry, size_t i) +{ + git_tree *tree; + + if (!entry) { + if (i >= tf->n_entries) + return NULL; + entry = &tf->entries[i]; + } + + tree = tf->parent->entries[entry->parent_entry_index].tree; + if (!tree) + return NULL; + + return git_tree_entry_byindex(tree, entry->parent_tree_index); +} + static char *tree_iterator__current_filename( tree_iterator *ti, const git_tree_entry *te) { @@ -210,39 +229,27 @@ static char *tree_iterator__current_filename( return ti->path.ptr; } -static int tree_iterator__create_top_frame(tree_iterator *ti, git_tree *tree) +static void tree_iterator__rewrite_filename(tree_iterator *ti) { - size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry); - tree_iterator_frame *top = git__calloc(sz, sizeof(char)); - GITERR_CHECK_ALLOC(top); + tree_iterator_frame *scan = ti->head; + size_t current = scan->current; + ssize_t strpos = ti->path.size; + const git_tree_entry *te; - 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; + while (scan && scan->parent) { + tree_iterator_entry *entry = &scan->entries[current]; - ti->head = ti->top = top; + te = tree_iterator__get_tree_entry(scan, entry, 0); + if (!te) + break; - return 0; -} + strpos -= te->filename_len; + memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len); + strpos -= 1; /* separator */ -static const git_tree_entry *tree_iterator__get_tree_entry( - tree_iterator_frame *tf, const tree_iterator_entry *entry, size_t i) -{ - git_tree *tree; - - if (!entry) { - if (i >= tf->n_entries) - return NULL; - entry = &tf->entries[i]; + current = entry->parent_entry_index; + scan = scan->parent; } - - tree = tf->parent->entries[entry->parent_entry_index].tree; - if (!tree) - return NULL; - - return git_tree_entry_byindex(tree, entry->parent_tree_index); } static int tree_iterator__entry_cmp(const void *a, const void *b, void *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, -- cgit v1.2.3