Commit aec4f663 by Russell Belfer

Fix tree iterator advance using wrong name compare

Tree iterator advance was moving forward without taking the
filemode of the entries into account, equating "a" and "a/".
This makes the tree entry comparison code more easily reusable
and fixes the problem.
parent 92028ea5
...@@ -193,26 +193,31 @@ typedef struct { ...@@ -193,26 +193,31 @@ typedef struct {
git_buf path; git_buf path;
int path_ambiguities; int path_ambiguities;
bool path_has_filename; bool path_has_filename;
int (*strcomp)(const char *a, const char *b);
int (*strncomp)(const char *a, const char *b, size_t sz); int (*strncomp)(const char *a, const char *b, size_t sz);
} tree_iterator; } tree_iterator;
static const git_tree_entry *tree_iterator__get_tree_entry( static const git_tree_entry *tree_iterator__tree_entry(
tree_iterator_frame *tf, const tree_iterator_entry *entry, size_t i) tree_iterator_frame *tf, const tree_iterator_entry *entry)
{
git_tree *tree = tf->parent->entries[entry->parent_entry_index].tree;
if (!tree)
return NULL;
return git_tree_entry_byindex(tree, entry->parent_tree_index);
}
static const git_tree_entry *tree_iterator__tree_entry_by_index(
tree_iterator_frame *tf, size_t i)
{ {
git_tree *tree; git_tree *tree;
if (!entry) { if (i >= tf->n_entries)
if (i >= tf->n_entries) return NULL;
return NULL;
entry = &tf->entries[i];
}
tree = tf->parent->entries[entry->parent_entry_index].tree; tree = tf->parent->entries[tf->entries[i].parent_entry_index].tree;
if (!tree) if (!tree)
return NULL; return NULL;
return git_tree_entry_byindex(tree, entry->parent_tree_index); return git_tree_entry_byindex(tree, tf->entries[i].parent_tree_index);
} }
static char *tree_iterator__current_filename( static char *tree_iterator__current_filename(
...@@ -244,8 +249,7 @@ static void tree_iterator__rewrite_filename(tree_iterator *ti) ...@@ -244,8 +249,7 @@ static void tree_iterator__rewrite_filename(tree_iterator *ti)
while (scan && scan->parent) { while (scan && scan->parent) {
tree_iterator_entry *entry = &scan->entries[current]; tree_iterator_entry *entry = &scan->entries[current];
te = tree_iterator__get_tree_entry(scan, entry, 0); if (!(te = tree_iterator__tree_entry(scan, entry)))
if (!te)
break; break;
strpos -= te->filename_len; strpos -= te->filename_len;
...@@ -257,21 +261,20 @@ static void tree_iterator__rewrite_filename(tree_iterator *ti) ...@@ -257,21 +261,20 @@ static void tree_iterator__rewrite_filename(tree_iterator *ti)
} }
} }
static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) static int tree_iterator__tree_entry_cmp(
const git_tree_entry *a,
const git_tree_entry *b,
int (*strncomp)(const char *, const char *, size_t))
{ {
tree_iterator_frame *tf = p; size_t common = min(a->filename_len, b->filename_len);
const git_tree_entry *ae = tree_iterator__get_tree_entry(tf, a, 0); int cmp = strncomp(a->filename, b->filename, common);
const git_tree_entry *be = tree_iterator__get_tree_entry(tf, b, 0);
size_t common_len = min(ae->filename_len, be->filename_len);
int cmp = git__strncasecmp(ae->filename, be->filename, common_len);
if (!cmp) { if (!cmp) {
char a_next = ae->filename[common_len]; char a_next = a->filename[common], b_next = b->filename[common];
char b_next = be->filename[common_len];
if (!a_next && ae->attr == GIT_FILEMODE_TREE) if (!a_next && a->attr == GIT_FILEMODE_TREE)
a_next = '/'; a_next = '/';
if (!b_next && be->attr == GIT_FILEMODE_TREE) if (!b_next && b->attr == GIT_FILEMODE_TREE)
b_next = '/'; b_next = '/';
cmp = (int)a_next - (int)b_next; cmp = (int)a_next - (int)b_next;
...@@ -280,17 +283,24 @@ static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) ...@@ -280,17 +283,24 @@ static int tree_iterator__entry_cmp(const void *a, const void *b, void *p)
return cmp; return cmp;
} }
static int tree_iterator__entry_cmp(const void *a, const void *b, void *p)
{
return tree_iterator__tree_entry_cmp(
tree_iterator__tree_entry(p, a), tree_iterator__tree_entry(p, b),
git__strncasecmp);
}
static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
{ {
/* find next and load trees for current range */ /* find next and load trees for current range */
int error = 0; int error = 0;
const git_tree_entry *te, *last_te = NULL; const git_tree_entry *te, *last = NULL;
tf->next = tf->current; tf->next = tf->current;
while (tf->next < tf->n_entries) { while (tf->next < tf->n_entries) {
if (!(te = tree_iterator__get_tree_entry(tf, 0, tf->next)) || if (!(te = tree_iterator__tree_entry_by_index(tf, tf->next)) ||
(last_te && ti->strcomp(last_te->filename, te->filename) != 0)) (last && tree_iterator__tree_entry_cmp(last, te, ti->strncomp)))
break; break;
if (git_tree_entry__is_tree(te) && if (git_tree_entry__is_tree(te) &&
...@@ -299,13 +309,13 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) ...@@ -299,13 +309,13 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
break; break;
tf->next++; tf->next++;
last_te = te; last = te;
} }
if (tf->next > tf->current + 1) if (tf->next > tf->current + 1)
ti->path_ambiguities++; ti->path_ambiguities++;
if (last_te && !tree_iterator__current_filename(ti, last_te)) if (last && !tree_iterator__current_filename(ti, last))
return -1; return -1;
return error; return error;
...@@ -360,7 +370,7 @@ static int tree_iterator__push_frame(tree_iterator *ti) ...@@ -360,7 +370,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
if (tf->startlen > 0) { if (tf->startlen > 0) {
/* find first item >= start */ /* find first item >= start */
for (i = 0; i < new_tf->n_entries; ++i) { for (i = 0; i < new_tf->n_entries; ++i) {
if (!(te = tree_iterator__get_tree_entry(new_tf, NULL, i))) if (!(te = tree_iterator__tree_entry_by_index(new_tf, i)))
break; break;
sz = min(tf->startlen, te->filename_len); sz = min(tf->startlen, te->filename_len);
if (ti->strncomp(tf->start, te->filename, sz) <= 0 && if (ti->strncomp(tf->start, te->filename, sz) <= 0 &&
...@@ -440,7 +450,7 @@ static int tree_iterator__current( ...@@ -440,7 +450,7 @@ static int tree_iterator__current(
iterator__clear_entry(entry); iterator__clear_entry(entry);
if (!(te = tree_iterator__get_tree_entry(tf, NULL, tf->current))) if (!(te = tree_iterator__tree_entry_by_index(tf, tf->current)))
return 0; return 0;
ti->entry.mode = te->attr; ti->entry.mode = te->attr;
...@@ -596,12 +606,7 @@ int git_iterator_for_tree( ...@@ -596,12 +606,7 @@ int git_iterator_for_tree(
if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0) if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
goto fail; goto fail;
ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp;
if (iterator__ignore_case(ti)) {
ti->strcomp = git__strcasecmp; ti->strncomp = git__strncasecmp;
} else {
ti->strcomp = git__strcmp; ti->strncomp = git__strncmp;
}
if ((error = tree_iterator__create_top_frame(ti, tree)) < 0 || if ((error = tree_iterator__create_top_frame(ti, tree)) < 0 ||
(error = tree_iterator__push_frame(ti)) < 0) /* expand top right now */ (error = tree_iterator__push_frame(ti)) < 0) /* expand top right now */
...@@ -1284,9 +1289,8 @@ int git_iterator_current_tree_entry( ...@@ -1284,9 +1289,8 @@ int git_iterator_current_tree_entry(
if (iter->type != GIT_ITERATOR_TYPE_TREE) if (iter->type != GIT_ITERATOR_TYPE_TREE)
*tree_entry = NULL; *tree_entry = NULL;
else { else {
tree_iterator *ti = (tree_iterator *)iter; tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
*tree_entry = *tree_entry = tree_iterator__tree_entry_by_index(tf, tf->current);
tree_iterator__get_tree_entry(ti->head, NULL, ti->head->current);
} }
return 0; return 0;
...@@ -1312,7 +1316,7 @@ int git_iterator_current_parent_tree( ...@@ -1312,7 +1316,7 @@ int git_iterator_current_parent_tree(
while (*scan) { while (*scan) {
/* get entry of this parent that child is currently on */ /* get entry of this parent that child is currently on */
if (!(tf = tf->child) || if (!(tf = tf->child) ||
!(te = tree_iterator__get_tree_entry(tf, NULL, tf->current)) || !(te = tree_iterator__tree_entry_by_index(tf, tf->current)) ||
ti->strncomp(scan, te->filename, te->filename_len) != 0) ti->strncomp(scan, te->filename, te->filename_len) != 0)
return 0; return 0;
......
...@@ -501,6 +501,61 @@ void test_repo_iterator__tree_case_conflicts(void) ...@@ -501,6 +501,61 @@ void test_repo_iterator__tree_case_conflicts(void)
git_tree_free(tree); git_tree_free(tree);
} }
void test_repo_iterator__tree_case_conflicts_2(void)
{
const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
git_tree *tree;
git_oid blob_id, Ab_id, biga_id, littlea_id, tree_id;
git_iterator *i;
const char *expect_cs[] = {
"A/a", "A/b/1", "A/c", "a/C", "a/a", "a/b" };
const char *expect_ci[] = {
"A/a", "a/b", "A/b/1", "A/c" };
const char *expect_cs_trees[] = {
"A/", "A/a", "A/b/", "A/b/1", "A/c", "a/", "a/C", "a/a", "a/b" };
const char *expect_ci_trees[] = {
"A/", "A/a", "a/b", "A/b/", "A/b/1", "A/c" };
g_repo = cl_git_sandbox_init("icase");
cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */
/* create: A/a A/b/1 A/c a/a a/b a/C */
build_test_tree(&Ab_id, g_repo, "b/1/", &blob_id);
build_test_tree(
&biga_id, g_repo, "b/a/,t/b/,b/c/", &blob_id, &Ab_id, &blob_id);
build_test_tree(
&littlea_id, g_repo, "b/a/,b/b/,b/C/", &blob_id, &blob_id, &blob_id);
build_test_tree(
&tree_id, g_repo, "t/A/,t/a/", &biga_id, &littlea_id);
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
cl_git_pass(git_iterator_for_tree(
&i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
expect_iterator_items(i, 6, expect_cs, 6, expect_cs);
git_iterator_free(i);
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);
git_iterator_free(i);
cl_git_pass(git_iterator_for_tree(
&i, tree, GIT_ITERATOR_DONT_IGNORE_CASE |
GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
expect_iterator_items(i, 9, expect_cs_trees, 9, expect_cs_trees);
git_iterator_free(i);
cl_git_pass(git_iterator_for_tree(
&i, tree, GIT_ITERATOR_IGNORE_CASE |
GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
expect_iterator_items(i, 6, expect_ci_trees, 6, expect_ci_trees);
git_iterator_free(i);
git_tree_free(tree);
}
void test_repo_iterator__workdir(void) void test_repo_iterator__workdir(void)
{ {
git_iterator *i; git_iterator *i;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment