mirror of
https://git.proxmox.com/git/libgit2
synced 2025-06-23 03:19:03 +00:00
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.
This commit is contained in:
parent
a03beb7ba6
commit
61c7b61e6f
107
src/iterator.c
107
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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user