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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
diff --git a/src/iterator.c b/src/iterator.c
index 84664c0..53ec6f6 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,
diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c
index 63613e9..804bc83 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);