mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-11 11:46:04 +00:00
Fix tree iterator path for tree issue + cleanups
This fixes an off by one error for generating full paths for tree entries in tree iterators when INCLUDE_TREES is set. Also, contains a bunch of small code cleanups with a couple of small utility functions and macro changes to eliminate redundant code.
This commit is contained in:
parent
61c7b61e6f
commit
92028ea585
111
src/iterator.c
111
src/iterator.c
@ -25,12 +25,13 @@
|
|||||||
#define ITERATOR_CASE_FLAGS \
|
#define ITERATOR_CASE_FLAGS \
|
||||||
(GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
|
(GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
|
||||||
|
|
||||||
#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \
|
#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
|
||||||
(P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
|
(P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
|
||||||
GITERR_CHECK_ALLOC(P); \
|
GITERR_CHECK_ALLOC(P); \
|
||||||
(P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
|
(P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
|
||||||
(P)->base.cb = &(P)->cb; \
|
(P)->base.cb = &(P)->cb; \
|
||||||
ITERATOR_SET_CB(P,NAME_LC); \
|
ITERATOR_SET_CB(P,NAME_LC); \
|
||||||
|
(P)->base.repo = (REPO); \
|
||||||
(P)->base.start = start ? git__strdup(start) : NULL; \
|
(P)->base.start = start ? git__strdup(start) : NULL; \
|
||||||
(P)->base.end = end ? git__strdup(end) : NULL; \
|
(P)->base.end = end ? git__strdup(end) : NULL; \
|
||||||
if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
|
if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
|
||||||
@ -72,7 +73,7 @@ static int iterator__reset_range(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iterator_update_ignore_case(
|
static int iterator__update_ignore_case(
|
||||||
git_iterator *iter,
|
git_iterator *iter,
|
||||||
git_iterator_flag_t flags)
|
git_iterator_flag_t flags)
|
||||||
{
|
{
|
||||||
@ -100,37 +101,40 @@ static int iterator_update_ignore_case(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry)
|
||||||
static int empty_iterator__noop(
|
|
||||||
const git_index_entry **entry, git_iterator *iter)
|
|
||||||
{
|
{
|
||||||
GIT_UNUSED(iter);
|
|
||||||
if (entry) *entry = NULL;
|
if (entry) *entry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
|
||||||
|
{
|
||||||
|
GIT_UNUSED(i);
|
||||||
|
iterator__clear_entry(e);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int empty_iterator__seek(git_iterator *iter, const char *prefix)
|
static int empty_iterator__seek(git_iterator *i, const char *p)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(iter); GIT_UNUSED(prefix);
|
GIT_UNUSED(i); GIT_UNUSED(p);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int empty_iterator__reset(
|
static int empty_iterator__reset(git_iterator *i, const char *s, const char *e)
|
||||||
git_iterator *iter, const char *start, const char *end)
|
|
||||||
{
|
{
|
||||||
GIT_UNUSED(iter); GIT_UNUSED(start); GIT_UNUSED(end);
|
GIT_UNUSED(i); GIT_UNUSED(s); GIT_UNUSED(e);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int empty_iterator__at_end(git_iterator *iter)
|
static int empty_iterator__at_end(git_iterator *i)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(iter);
|
GIT_UNUSED(i);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void empty_iterator__free(git_iterator *iter)
|
static void empty_iterator__free(git_iterator *i)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(iter);
|
GIT_UNUSED(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -151,18 +155,16 @@ int git_iterator_for_nothing(
|
|||||||
#define empty_iterator__advance empty_iterator__noop
|
#define empty_iterator__advance empty_iterator__noop
|
||||||
#define empty_iterator__advance_into empty_iterator__noop
|
#define empty_iterator__advance_into empty_iterator__noop
|
||||||
|
|
||||||
ITERATOR_BASE_INIT(i, empty, EMPTY);
|
ITERATOR_BASE_INIT(i, empty, EMPTY, NULL);
|
||||||
|
|
||||||
if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
|
if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
|
||||||
i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
|
i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
|
||||||
|
|
||||||
*iter = (git_iterator *)i;
|
*iter = (git_iterator *)i;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t parent_entry_index; /* index in parent entries array */
|
size_t parent_entry_index; /* index in parent entries array */
|
||||||
size_t parent_tree_index; /* index in parent entry tree */
|
size_t parent_tree_index; /* index in parent entry tree */
|
||||||
@ -236,6 +238,9 @@ static void tree_iterator__rewrite_filename(tree_iterator *ti)
|
|||||||
ssize_t strpos = ti->path.size;
|
ssize_t strpos = ti->path.size;
|
||||||
const git_tree_entry *te;
|
const git_tree_entry *te;
|
||||||
|
|
||||||
|
if (strpos && ti->path.ptr[strpos - 1] == '/')
|
||||||
|
strpos--;
|
||||||
|
|
||||||
while (scan && scan->parent) {
|
while (scan && scan->parent) {
|
||||||
tree_iterator_entry *entry = &scan->entries[current];
|
tree_iterator_entry *entry = &scan->entries[current];
|
||||||
|
|
||||||
@ -386,18 +391,23 @@ static int tree_iterator__push_frame(tree_iterator *ti)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GIT_INLINE(void) tree_iterator__free_tree(tree_iterator_entry *entry)
|
||||||
|
{
|
||||||
|
if (entry->tree) {
|
||||||
|
git_tree_free(entry->tree);
|
||||||
|
entry->tree = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool tree_iterator__move_to_next(
|
static bool tree_iterator__move_to_next(
|
||||||
tree_iterator *ti, tree_iterator_frame *tf)
|
tree_iterator *ti, tree_iterator_frame *tf)
|
||||||
{
|
{
|
||||||
if (tf->next > tf->current + 1)
|
if (tf->next > tf->current + 1)
|
||||||
ti->path_ambiguities--;
|
ti->path_ambiguities--;
|
||||||
|
|
||||||
while (tf->current < tf->next) {
|
for (; tf->current < tf->next; tf->current++) {
|
||||||
if (tf->parent && tf->entries[tf->current].tree) {
|
if (tf->parent)
|
||||||
git_tree_free(tf->entries[tf->current].tree);
|
tree_iterator__free_tree(&tf->entries[tf->current]);
|
||||||
tf->entries[tf->current].tree = NULL;
|
|
||||||
}
|
|
||||||
tf->current++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (tf->current < tf->n_entries);
|
return (tf->current < tf->n_entries);
|
||||||
@ -428,8 +438,7 @@ static int tree_iterator__current(
|
|||||||
tree_iterator_frame *tf = ti->head;
|
tree_iterator_frame *tf = ti->head;
|
||||||
const git_tree_entry *te;
|
const git_tree_entry *te;
|
||||||
|
|
||||||
if (entry)
|
iterator__clear_entry(entry);
|
||||||
*entry = NULL;
|
|
||||||
|
|
||||||
if (!(te = tree_iterator__get_tree_entry(tf, NULL, tf->current)))
|
if (!(te = tree_iterator__get_tree_entry(tf, NULL, tf->current)))
|
||||||
return 0;
|
return 0;
|
||||||
@ -462,8 +471,7 @@ static int tree_iterator__advance_into(
|
|||||||
int error = 0;
|
int error = 0;
|
||||||
tree_iterator *ti = (tree_iterator *)self;
|
tree_iterator *ti = (tree_iterator *)self;
|
||||||
|
|
||||||
if (entry)
|
iterator__clear_entry(entry);
|
||||||
*entry = NULL;
|
|
||||||
|
|
||||||
if (tree_iterator__at_tree(ti) &&
|
if (tree_iterator__at_tree(ti) &&
|
||||||
!(error = tree_iterator__push_frame(ti)))
|
!(error = tree_iterator__push_frame(ti)))
|
||||||
@ -479,8 +487,7 @@ static int tree_iterator__advance(
|
|||||||
tree_iterator *ti = (tree_iterator *)self;
|
tree_iterator *ti = (tree_iterator *)self;
|
||||||
tree_iterator_frame *tf = ti->head;
|
tree_iterator_frame *tf = ti->head;
|
||||||
|
|
||||||
if (entry)
|
iterator__clear_entry(entry);
|
||||||
*entry = NULL;
|
|
||||||
|
|
||||||
if (tf->current > tf->n_entries)
|
if (tf->current > tf->n_entries)
|
||||||
return 0;
|
return 0;
|
||||||
@ -511,8 +518,7 @@ static int tree_iterator__advance(
|
|||||||
|
|
||||||
static int tree_iterator__seek(git_iterator *self, const char *prefix)
|
static int tree_iterator__seek(git_iterator *self, const char *prefix)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(self);
|
GIT_UNUSED(self); GIT_UNUSED(prefix);
|
||||||
GIT_UNUSED(prefix);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,10 +550,8 @@ static void tree_iterator__free(git_iterator *self)
|
|||||||
|
|
||||||
while (tree_iterator__pop_frame(ti)) /* pop to top */;
|
while (tree_iterator__pop_frame(ti)) /* pop to top */;
|
||||||
|
|
||||||
/* free base frame */
|
|
||||||
if (ti->head) {
|
if (ti->head) {
|
||||||
git_tree_free(ti->head->entries[0].tree);
|
tree_iterator__free_tree(&ti->head->entries[0]);
|
||||||
ti->head->entries[0].tree = NULL;
|
|
||||||
git__free(ti->head);
|
git__free(ti->head);
|
||||||
}
|
}
|
||||||
ti->head = ti->top = NULL;
|
ti->head = ti->top = NULL;
|
||||||
@ -588,10 +592,9 @@ int git_iterator_for_tree(
|
|||||||
if ((error = git_tree__dup(&tree, tree)) < 0)
|
if ((error = git_tree__dup(&tree, tree)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
ITERATOR_BASE_INIT(ti, tree, TREE);
|
ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
|
||||||
ti->base.repo = git_tree_owner(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;
|
||||||
|
|
||||||
if (iterator__ignore_case(ti)) {
|
if (iterator__ignore_case(ti)) {
|
||||||
@ -678,7 +681,6 @@ static int index_iterator__first_prefix_tree(index_iterator *ii)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* find longest common prefix with prior index entry */
|
/* find longest common prefix with prior index entry */
|
||||||
|
|
||||||
for (scan = slash = ie->path, prior = ii->partial.ptr;
|
for (scan = slash = ie->path, prior = ii->partial.ptr;
|
||||||
*scan && *scan == *prior; ++scan, ++prior)
|
*scan && *scan == *prior; ++scan, ++prior)
|
||||||
if (*scan == '/')
|
if (*scan == '/')
|
||||||
@ -731,9 +733,7 @@ static int index_iterator__advance(
|
|||||||
ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
|
ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
|
||||||
index_iterator__next_prefix_tree(ii);
|
index_iterator__next_prefix_tree(ii);
|
||||||
} else {
|
} else {
|
||||||
/* advance to sibling tree (i.e. until we find entry that does
|
/* advance to sibling tree (i.e. find entry with new prefix) */
|
||||||
* not share this prefix)
|
|
||||||
*/
|
|
||||||
while (ii->current < entrycount) {
|
while (ii->current < entrycount) {
|
||||||
ii->current++;
|
ii->current++;
|
||||||
|
|
||||||
@ -773,9 +773,7 @@ static int index_iterator__advance_into(
|
|||||||
|
|
||||||
static int index_iterator__seek(git_iterator *self, const char *prefix)
|
static int index_iterator__seek(git_iterator *self, const char *prefix)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(self);
|
GIT_UNUSED(self); GIT_UNUSED(prefix);
|
||||||
GIT_UNUSED(prefix);
|
|
||||||
/* find last item before prefix */
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -829,9 +827,7 @@ int git_iterator_for_index(
|
|||||||
{
|
{
|
||||||
index_iterator *ii;
|
index_iterator *ii;
|
||||||
|
|
||||||
ITERATOR_BASE_INIT(ii, index, INDEX);
|
ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
|
||||||
|
|
||||||
ii->base.repo = git_index_owner(index);
|
|
||||||
|
|
||||||
if (index->ignore_case) {
|
if (index->ignore_case) {
|
||||||
ii->base.flags |= GIT_ITERATOR_IGNORE_CASE;
|
ii->base.flags |= GIT_ITERATOR_IGNORE_CASE;
|
||||||
@ -969,8 +965,7 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
|
|||||||
GITERR_CHECK_ALLOC(wf);
|
GITERR_CHECK_ALLOC(wf);
|
||||||
|
|
||||||
error = git_path_dirload_with_stat(
|
error = git_path_dirload_with_stat(
|
||||||
wi->path.ptr, wi->root_len,
|
wi->path.ptr, wi->root_len, iterator__ignore_case(wi),
|
||||||
iterator__ignore_case(wi),
|
|
||||||
wi->base.start, wi->base.end, &wf->entries);
|
wi->base.start, wi->base.end, &wf->entries);
|
||||||
|
|
||||||
if (error < 0 || wf->entries.length == 0) {
|
if (error < 0 || wf->entries.length == 0) {
|
||||||
@ -1012,8 +1007,7 @@ static int workdir_iterator__advance_into(
|
|||||||
int error = 0;
|
int error = 0;
|
||||||
workdir_iterator *wi = (workdir_iterator *)iter;
|
workdir_iterator *wi = (workdir_iterator *)iter;
|
||||||
|
|
||||||
if (entry)
|
iterator__clear_entry(entry);
|
||||||
*entry = NULL;
|
|
||||||
|
|
||||||
/* workdir iterator will allow you to explicitly advance into a
|
/* workdir iterator will allow you to explicitly advance into a
|
||||||
* commit/submodule (as well as a tree) to avoid some cases where an
|
* commit/submodule (as well as a tree) to avoid some cases where an
|
||||||
@ -1206,13 +1200,12 @@ int git_iterator_for_workdir(
|
|||||||
assert(iter && repo);
|
assert(iter && repo);
|
||||||
|
|
||||||
if ((error = git_repository__ensure_not_bare(
|
if ((error = git_repository__ensure_not_bare(
|
||||||
repo, "scan working directory")) < 0)
|
repo, "scan working directory")) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
ITERATOR_BASE_INIT(wi, workdir, WORKDIR);
|
ITERATOR_BASE_INIT(wi, workdir, WORKDIR, repo);
|
||||||
wi->base.repo = repo;
|
|
||||||
|
|
||||||
if ((error = iterator_update_ignore_case((git_iterator *)wi, flags)) < 0)
|
if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
|
if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
|
||||||
@ -1282,7 +1275,6 @@ git_index *git_iterator_get_index(git_iterator *iter)
|
|||||||
{
|
{
|
||||||
if (iter->type == GIT_ITERATOR_TYPE_INDEX)
|
if (iter->type == GIT_ITERATOR_TYPE_INDEX)
|
||||||
return ((index_iterator *)iter)->index;
|
return ((index_iterator *)iter)->index;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1354,8 +1346,7 @@ int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
|
|||||||
const git_index_entry *entry;
|
const git_index_entry *entry;
|
||||||
|
|
||||||
/* a "done" iterator is after every prefix */
|
/* a "done" iterator is after every prefix */
|
||||||
if (git_iterator_current(&entry, iter) < 0 ||
|
if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
|
||||||
entry == NULL)
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* a NULL prefix is after any valid iterator */
|
/* a NULL prefix is after any valid iterator */
|
||||||
|
@ -457,6 +457,10 @@ void test_repo_iterator__tree_case_conflicts(void)
|
|||||||
"A/1.file", "A/3.file", "a/2.file", "a/4.file" };
|
"A/1.file", "A/3.file", "a/2.file", "a/4.file" };
|
||||||
const char *expect_ci[] = {
|
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" };
|
||||||
|
const char *expect_cs_trees[] = {
|
||||||
|
"A/", "A/1.file", "A/3.file", "a/", "a/2.file", "a/4.file" };
|
||||||
|
const char *expect_ci_trees[] = {
|
||||||
|
"A/", "A/1.file", "a/2.file", "A/3.file", "a/4.file" };
|
||||||
|
|
||||||
g_repo = cl_git_sandbox_init("icase");
|
g_repo = cl_git_sandbox_init("icase");
|
||||||
|
|
||||||
@ -482,6 +486,18 @@ void test_repo_iterator__tree_case_conflicts(void)
|
|||||||
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_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, 6, expect_cs_trees, 6, 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, 5, expect_ci_trees, 5, expect_ci_trees);
|
||||||
|
git_iterator_free(i);
|
||||||
|
|
||||||
git_tree_free(tree);
|
git_tree_free(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user