mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-04 09:47:23 +00:00
Fix iterator reset and add reset ranges
The `git_iterator_reset` command has not been working in all cases particularly when there is a start and end range. This fixes it and adds tests for it, and also extends it with the ability to update the start/end range strings when an iterator is reset.
This commit is contained in:
parent
9950d27ab6
commit
91e7d26303
250
src/iterator.c
250
src/iterator.c
@ -29,6 +29,25 @@
|
|||||||
return -1; \
|
return -1; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
static int iterator__reset_range(
|
||||||
|
git_iterator *iter, const char *start, const char *end)
|
||||||
|
{
|
||||||
|
if (start) {
|
||||||
|
if (iter->start)
|
||||||
|
git__free(iter->start);
|
||||||
|
iter->start = git__strdup(start);
|
||||||
|
GITERR_CHECK_ALLOC(iter->start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
if (iter->end)
|
||||||
|
git__free(iter->end);
|
||||||
|
iter->end = git__strdup(end);
|
||||||
|
GITERR_CHECK_ALLOC(iter->end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int empty_iterator__no_item(
|
static int empty_iterator__no_item(
|
||||||
git_iterator *iter, const git_index_entry **entry)
|
git_iterator *iter, const git_index_entry **entry)
|
||||||
@ -44,16 +63,16 @@ static int empty_iterator__at_end(git_iterator *iter)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int empty_iterator__noop(git_iterator *iter)
|
static int empty_iterator__reset(
|
||||||
|
git_iterator *iter, const char *start, const char *end)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(iter);
|
GIT_UNUSED(iter); GIT_UNUSED(start); GIT_UNUSED(end);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int empty_iterator__seek(git_iterator *iter, const char *prefix)
|
static int empty_iterator__seek(git_iterator *iter, const char *prefix)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(iter);
|
GIT_UNUSED(iter); GIT_UNUSED(prefix);
|
||||||
GIT_UNUSED(prefix);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +91,7 @@ int git_iterator_for_nothing(git_iterator **iter)
|
|||||||
i->at_end = empty_iterator__at_end;
|
i->at_end = empty_iterator__at_end;
|
||||||
i->advance = empty_iterator__no_item;
|
i->advance = empty_iterator__no_item;
|
||||||
i->seek = empty_iterator__seek;
|
i->seek = empty_iterator__seek;
|
||||||
i->reset = empty_iterator__noop;
|
i->reset = empty_iterator__reset;
|
||||||
i->free = empty_iterator__free;
|
i->free = empty_iterator__free;
|
||||||
|
|
||||||
*iter = i;
|
*iter = i;
|
||||||
@ -97,10 +116,9 @@ typedef struct {
|
|||||||
bool path_has_filename;
|
bool path_has_filename;
|
||||||
} tree_iterator;
|
} tree_iterator;
|
||||||
|
|
||||||
static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)
|
GIT_INLINE(const git_tree_entry *)tree_iterator__tree_entry(tree_iterator *ti)
|
||||||
{
|
{
|
||||||
return (ti->stack == NULL) ? NULL :
|
return git_tree_entry_byindex(ti->stack->tree, ti->stack->index);
|
||||||
git_tree_entry_byindex(ti->stack->tree, ti->stack->index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *tree_iterator__current_filename(
|
static char *tree_iterator__current_filename(
|
||||||
@ -115,25 +133,34 @@ static char *tree_iterator__current_filename(
|
|||||||
return ti->path.ptr;
|
return ti->path.ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tree_iterator__pop_frame(tree_iterator *ti)
|
static void tree_iterator__free_frame(tree_iterator_frame *tf)
|
||||||
|
{
|
||||||
|
if (!tf)
|
||||||
|
return;
|
||||||
|
git_tree_free(tf->tree);
|
||||||
|
git__free(tf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tree_iterator__pop_frame(tree_iterator *ti)
|
||||||
{
|
{
|
||||||
tree_iterator_frame *tf = ti->stack;
|
tree_iterator_frame *tf = ti->stack;
|
||||||
|
|
||||||
|
/* don't free the initial tree/frame */
|
||||||
|
if (!tf->next)
|
||||||
|
return false;
|
||||||
|
|
||||||
ti->stack = tf->next;
|
ti->stack = tf->next;
|
||||||
if (ti->stack != NULL) {
|
ti->stack->prev = NULL;
|
||||||
git_tree_free(tf->tree); /* don't free the initial tree */
|
|
||||||
ti->stack->prev = NULL; /* disconnect prev */
|
tree_iterator__free_frame(tf);
|
||||||
}
|
|
||||||
git__free(tf);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tree_iterator__to_end(tree_iterator *ti)
|
static int tree_iterator__to_end(tree_iterator *ti)
|
||||||
{
|
{
|
||||||
while (ti->stack && ti->stack->next)
|
while (tree_iterator__pop_frame(ti)) /* pop all */;
|
||||||
tree_iterator__pop_frame(ti);
|
ti->stack->index = git_tree_entrycount(ti->stack->tree);
|
||||||
|
|
||||||
if (ti->stack)
|
|
||||||
ti->stack->index = git_tree_entrycount(ti->stack->tree);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,12 +273,13 @@ static int tree_iterator__advance(
|
|||||||
ti->path_has_filename = false;
|
ti->path_has_filename = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ti->stack != NULL) {
|
while (1) {
|
||||||
te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);
|
te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);
|
||||||
if (te != NULL)
|
if (te != NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
tree_iterator__pop_frame(ti);
|
if (!tree_iterator__pop_frame(ti))
|
||||||
|
break; /* no frames left to pop */
|
||||||
|
|
||||||
git_buf_rtruncate_at_char(&ti->path, '/');
|
git_buf_rtruncate_at_char(&ti->path, '/');
|
||||||
}
|
}
|
||||||
@ -278,23 +306,30 @@ static int tree_iterator__seek(git_iterator *self, const char *prefix)
|
|||||||
static void tree_iterator__free(git_iterator *self)
|
static void tree_iterator__free(git_iterator *self)
|
||||||
{
|
{
|
||||||
tree_iterator *ti = (tree_iterator *)self;
|
tree_iterator *ti = (tree_iterator *)self;
|
||||||
while (ti->stack != NULL)
|
|
||||||
tree_iterator__pop_frame(ti);
|
while (tree_iterator__pop_frame(ti)) /* pop all */;
|
||||||
|
|
||||||
|
tree_iterator__free_frame(ti->stack);
|
||||||
|
ti->stack = ti->tail = NULL;
|
||||||
|
|
||||||
git_buf_free(&ti->path);
|
git_buf_free(&ti->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tree_iterator__reset(git_iterator *self)
|
static int tree_iterator__reset(
|
||||||
|
git_iterator *self, const char *start, const char *end)
|
||||||
{
|
{
|
||||||
tree_iterator *ti = (tree_iterator *)self;
|
tree_iterator *ti = (tree_iterator *)self;
|
||||||
|
|
||||||
while (ti->stack && ti->stack->next)
|
while (tree_iterator__pop_frame(ti)) /* pop all */;
|
||||||
tree_iterator__pop_frame(ti);
|
|
||||||
|
|
||||||
if (ti->stack)
|
if (iterator__reset_range(self, start, end) < 0)
|
||||||
ti->stack->index =
|
return -1;
|
||||||
git_tree__prefix_position(ti->stack->tree, ti->base.start);
|
|
||||||
|
ti->stack->index =
|
||||||
|
git_tree__prefix_position(ti->stack->tree, ti->base.start);
|
||||||
|
|
||||||
git_buf_clear(&ti->path);
|
git_buf_clear(&ti->path);
|
||||||
|
ti->path_has_filename = false;
|
||||||
|
|
||||||
return tree_iterator__expand_tree(ti);
|
return tree_iterator__expand_tree(ti);
|
||||||
}
|
}
|
||||||
@ -311,6 +346,9 @@ int git_iterator_for_tree_range(
|
|||||||
if (tree == NULL)
|
if (tree == NULL)
|
||||||
return git_iterator_for_nothing(iter);
|
return git_iterator_for_nothing(iter);
|
||||||
|
|
||||||
|
if ((error = git_tree__dup(&tree, tree)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
ITERATOR_BASE_INIT(ti, tree, TREE);
|
ITERATOR_BASE_INIT(ti, tree, TREE);
|
||||||
|
|
||||||
ti->base.repo = git_tree_owner(tree);
|
ti->base.repo = git_tree_owner(tree);
|
||||||
@ -394,9 +432,12 @@ static int index_iterator__seek(git_iterator *self, const char *prefix)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int index_iterator__reset(git_iterator *self)
|
static int index_iterator__reset(
|
||||||
|
git_iterator *self, const char *start, const char *end)
|
||||||
{
|
{
|
||||||
index_iterator *ii = (index_iterator *)self;
|
index_iterator *ii = (index_iterator *)self;
|
||||||
|
if (iterator__reset_range(self, start, end) < 0)
|
||||||
|
return -1;
|
||||||
ii->current = ii->base.start ?
|
ii->current = ii->base.start ?
|
||||||
git_index__prefix_position(ii->index, ii->base.start) : 0;
|
git_index__prefix_position(ii->index, ii->base.start) : 0;
|
||||||
index_iterator__skip_conflicts(ii);
|
index_iterator__skip_conflicts(ii);
|
||||||
@ -425,7 +466,7 @@ int git_iterator_for_index_range(
|
|||||||
ii->base.repo = git_index_owner(index);
|
ii->base.repo = git_index_owner(index);
|
||||||
ii->base.ignore_case = ii->index->ignore_case;
|
ii->base.ignore_case = ii->index->ignore_case;
|
||||||
|
|
||||||
index_iterator__reset((git_iterator *)ii);
|
index_iterator__reset((git_iterator *)ii, NULL, NULL);
|
||||||
|
|
||||||
*iter = (git_iterator *)ii;
|
*iter = (git_iterator *)ii;
|
||||||
|
|
||||||
@ -455,35 +496,19 @@ struct workdir_iterator_frame {
|
|||||||
workdir_iterator_frame *next;
|
workdir_iterator_frame *next;
|
||||||
git_vector entries;
|
git_vector entries;
|
||||||
size_t index;
|
size_t index;
|
||||||
char *start;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
git_iterator base;
|
git_iterator base;
|
||||||
size_t root_len;
|
|
||||||
workdir_iterator_frame *stack;
|
workdir_iterator_frame *stack;
|
||||||
|
int (*entrycmp)(const void *pfx, const void *item);
|
||||||
git_ignores ignores;
|
git_ignores ignores;
|
||||||
git_index_entry entry;
|
git_index_entry entry;
|
||||||
git_buf path;
|
git_buf path;
|
||||||
|
size_t root_len;
|
||||||
int is_ignored;
|
int is_ignored;
|
||||||
} workdir_iterator;
|
} workdir_iterator;
|
||||||
|
|
||||||
static int git_path_with_stat_cmp_case(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
const git_path_with_stat *path_with_stat_a = a;
|
|
||||||
const git_path_with_stat *path_with_stat_b = b;
|
|
||||||
|
|
||||||
return strcmp(path_with_stat_a->path, path_with_stat_b->path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int git_path_with_stat_cmp_icase(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
const git_path_with_stat *path_with_stat_a = a;
|
|
||||||
const git_path_with_stat *path_with_stat_b = b;
|
|
||||||
|
|
||||||
return strcasecmp(path_with_stat_a->path, path_with_stat_b->path);
|
|
||||||
}
|
|
||||||
|
|
||||||
GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
|
GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
|
||||||
{
|
{
|
||||||
if (!ps)
|
if (!ps)
|
||||||
@ -492,26 +517,34 @@ GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
|
|||||||
const char *path = ps->path;
|
const char *path = ps->path;
|
||||||
size_t len = ps->path_len;
|
size_t len = ps->path_len;
|
||||||
|
|
||||||
return len >= 4 &&
|
if (len < 4)
|
||||||
tolower(path[len - 1]) == 't' &&
|
return false;
|
||||||
tolower(path[len - 2]) == 'i' &&
|
if (path[len - 1] == '/')
|
||||||
tolower(path[len - 3]) == 'g' &&
|
len--;
|
||||||
path[len - 4] == '.' &&
|
if (tolower(path[len - 1]) != 't' ||
|
||||||
(len == 4 || path[len - 5] == '/');
|
tolower(path[len - 2]) != 'i' ||
|
||||||
|
tolower(path[len - 3]) != 'g' ||
|
||||||
|
tolower(path[len - 4]) != '.')
|
||||||
|
return false;
|
||||||
|
return (len == 4 || path[len - 5] == '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static workdir_iterator_frame *workdir_iterator__alloc_frame(workdir_iterator *wi)
|
static workdir_iterator_frame *workdir_iterator__alloc_frame(
|
||||||
|
workdir_iterator *wi)
|
||||||
{
|
{
|
||||||
workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
|
workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
|
||||||
git_vector_cmp entry_compare = CASESELECT(wi->base.ignore_case, git_path_with_stat_cmp_icase, git_path_with_stat_cmp_case);
|
git_vector_cmp entry_compare = CASESELECT(wi->base.ignore_case,
|
||||||
|
git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
|
||||||
|
|
||||||
if (wf == NULL)
|
if (wf == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
|
if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
|
||||||
git__free(wf);
|
git__free(wf);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return wf;
|
return wf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,16 +561,32 @@ static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
|
|||||||
|
|
||||||
static int workdir_iterator__update_entry(workdir_iterator *wi);
|
static int workdir_iterator__update_entry(workdir_iterator *wi);
|
||||||
|
|
||||||
static int workdir_iterator__entry_cmp_case(const void *prefix, const void *item)
|
static int workdir_iterator__entry_cmp_case(const void *pfx, const void *item)
|
||||||
{
|
{
|
||||||
const git_path_with_stat *ps = item;
|
const git_path_with_stat *ps = item;
|
||||||
return git__prefixcmp((const char *)prefix, ps->path);
|
return git__prefixcmp((const char *)pfx, ps->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int workdir_iterator__entry_cmp_icase(const void *prefix, const void *item)
|
static int workdir_iterator__entry_cmp_icase(const void *pfx, const void *item)
|
||||||
{
|
{
|
||||||
const git_path_with_stat *ps = item;
|
const git_path_with_stat *ps = item;
|
||||||
return git__prefixcmp_icase((const char *)prefix, ps->path);
|
return git__prefixcmp_icase((const char *)pfx, ps->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workdir_iterator__seek_frame_start(
|
||||||
|
workdir_iterator *wi, workdir_iterator_frame *wf)
|
||||||
|
{
|
||||||
|
if (!wf)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wi->base.start)
|
||||||
|
git_vector_bsearch3(
|
||||||
|
&wf->index, &wf->entries, wi->entrycmp, wi->base.start);
|
||||||
|
else
|
||||||
|
wf->index = 0;
|
||||||
|
|
||||||
|
if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
|
||||||
|
wf->index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int workdir_iterator__expand_dir(workdir_iterator *wi)
|
static int workdir_iterator__expand_dir(workdir_iterator *wi)
|
||||||
@ -546,39 +595,26 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
|
|||||||
workdir_iterator_frame *wf = workdir_iterator__alloc_frame(wi);
|
workdir_iterator_frame *wf = workdir_iterator__alloc_frame(wi);
|
||||||
GITERR_CHECK_ALLOC(wf);
|
GITERR_CHECK_ALLOC(wf);
|
||||||
|
|
||||||
error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
|
error = git_path_dirload_with_stat(
|
||||||
|
wi->path.ptr, wi->root_len, wi->base.ignore_case,
|
||||||
|
wi->base.start, wi->base.end, &wf->entries);
|
||||||
|
|
||||||
if (error < 0 || wf->entries.length == 0) {
|
if (error < 0 || wf->entries.length == 0) {
|
||||||
workdir_iterator__free_frame(wf);
|
workdir_iterator__free_frame(wf);
|
||||||
return GIT_ENOTFOUND;
|
return GIT_ENOTFOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
git_vector_sort(&wf->entries);
|
workdir_iterator__seek_frame_start(wi, wf);
|
||||||
|
|
||||||
if (!wi->stack)
|
|
||||||
wf->start = wi->base.start;
|
|
||||||
else if (wi->stack->start &&
|
|
||||||
ITERATOR_PREFIXCMP(wi->base, wi->stack->start, wi->path.ptr + wi->root_len) == 0)
|
|
||||||
wf->start = wi->stack->start;
|
|
||||||
|
|
||||||
if (wf->start)
|
|
||||||
git_vector_bsearch3(
|
|
||||||
&wf->index,
|
|
||||||
&wf->entries,
|
|
||||||
CASESELECT(wi->base.ignore_case, workdir_iterator__entry_cmp_icase, workdir_iterator__entry_cmp_case),
|
|
||||||
wf->start);
|
|
||||||
|
|
||||||
if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
|
|
||||||
wf->index++;
|
|
||||||
|
|
||||||
wf->next = wi->stack;
|
|
||||||
wi->stack = wf;
|
|
||||||
|
|
||||||
/* only push new ignores if this is not top level directory */
|
/* only push new ignores if this is not top level directory */
|
||||||
if (wi->stack->next != NULL) {
|
if (wi->stack != NULL) {
|
||||||
ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
|
ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
|
||||||
(void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
|
(void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wf->next = wi->stack;
|
||||||
|
wi->stack = wf;
|
||||||
|
|
||||||
return workdir_iterator__update_entry(wi);
|
return workdir_iterator__update_entry(wi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,8 +645,10 @@ static int workdir_iterator__advance(
|
|||||||
if (wi->entry.path == NULL)
|
if (wi->entry.path == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
while ((wf = wi->stack) != NULL) {
|
while (1) {
|
||||||
|
wf = wi->stack;
|
||||||
next = git_vector_get(&wf->entries, ++wf->index);
|
next = git_vector_get(&wf->entries, ++wf->index);
|
||||||
|
|
||||||
if (next != NULL) {
|
if (next != NULL) {
|
||||||
/* match git's behavior of ignoring anything named ".git" */
|
/* match git's behavior of ignoring anything named ".git" */
|
||||||
if (path_is_dotgit(next))
|
if (path_is_dotgit(next))
|
||||||
@ -619,15 +657,15 @@ static int workdir_iterator__advance(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pop workdir directory stack */
|
/* pop stack if anything is left to pop */
|
||||||
wi->stack = wf->next;
|
if (!wf->next) {
|
||||||
workdir_iterator__free_frame(wf);
|
|
||||||
git_ignore__pop_dir(&wi->ignores);
|
|
||||||
|
|
||||||
if (wi->stack == NULL) {
|
|
||||||
memset(&wi->entry, 0, sizeof(wi->entry));
|
memset(&wi->entry, 0, sizeof(wi->entry));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wi->stack = wf->next;
|
||||||
|
workdir_iterator__free_frame(wf);
|
||||||
|
git_ignore__pop_dir(&wi->ignores);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = workdir_iterator__update_entry(wi);
|
error = workdir_iterator__update_entry(wi);
|
||||||
@ -648,18 +686,24 @@ static int workdir_iterator__seek(git_iterator *self, const char *prefix)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int workdir_iterator__reset(git_iterator *self)
|
static int workdir_iterator__reset(
|
||||||
|
git_iterator *self, const char *start, const char *end)
|
||||||
{
|
{
|
||||||
workdir_iterator *wi = (workdir_iterator *)self;
|
workdir_iterator *wi = (workdir_iterator *)self;
|
||||||
|
|
||||||
while (wi->stack != NULL && wi->stack->next != NULL) {
|
while (wi->stack != NULL && wi->stack->next != NULL) {
|
||||||
workdir_iterator_frame *wf = wi->stack;
|
workdir_iterator_frame *wf = wi->stack;
|
||||||
wi->stack = wf->next;
|
wi->stack = wf->next;
|
||||||
workdir_iterator__free_frame(wf);
|
workdir_iterator__free_frame(wf);
|
||||||
git_ignore__pop_dir(&wi->ignores);
|
git_ignore__pop_dir(&wi->ignores);
|
||||||
}
|
}
|
||||||
if (wi->stack)
|
|
||||||
wi->stack->index = 0;
|
if (iterator__reset_range(self, start, end) < 0)
|
||||||
return 0;
|
return -1;
|
||||||
|
|
||||||
|
workdir_iterator__seek_frame_start(wi, wi->stack);
|
||||||
|
|
||||||
|
return workdir_iterator__update_entry(wi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void workdir_iterator__free(git_iterator *self)
|
static void workdir_iterator__free(git_iterator *self)
|
||||||
@ -678,7 +722,8 @@ static void workdir_iterator__free(git_iterator *self)
|
|||||||
|
|
||||||
static int workdir_iterator__update_entry(workdir_iterator *wi)
|
static int workdir_iterator__update_entry(workdir_iterator *wi)
|
||||||
{
|
{
|
||||||
git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
|
git_path_with_stat *ps =
|
||||||
|
git_vector_get(&wi->stack->entries, wi->stack->index);
|
||||||
|
|
||||||
git_buf_truncate(&wi->path, wi->root_len);
|
git_buf_truncate(&wi->path, wi->root_len);
|
||||||
memset(&wi->entry, 0, sizeof(wi->entry));
|
memset(&wi->entry, 0, sizeof(wi->entry));
|
||||||
@ -689,8 +734,8 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
|
|||||||
if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
|
if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (wi->base.end &&
|
if (wi->base.end && ITERATOR_PREFIXCMP(
|
||||||
ITERATOR_PREFIXCMP(wi->base, wi->path.ptr + wi->root_len, wi->base.end) > 0)
|
wi->base, wi->path.ptr + wi->root_len, wi->base.end) > 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
wi->entry.path = ps->path;
|
wi->entry.path = ps->path;
|
||||||
@ -751,7 +796,7 @@ int git_iterator_for_workdir_range(
|
|||||||
wi->base.repo = repo;
|
wi->base.repo = repo;
|
||||||
|
|
||||||
if ((error = git_repository_index__weakptr(&index, repo)) < 0) {
|
if ((error = git_repository_index__weakptr(&index, repo)) < 0) {
|
||||||
git__free(wi);
|
git_iterator_free((git_iterator *)wi);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,6 +812,8 @@ int git_iterator_for_workdir_range(
|
|||||||
}
|
}
|
||||||
|
|
||||||
wi->root_len = wi->path.size;
|
wi->root_len = wi->path.size;
|
||||||
|
wi->entrycmp = wi->base.ignore_case ?
|
||||||
|
workdir_iterator__entry_cmp_icase : workdir_iterator__entry_cmp_case;
|
||||||
|
|
||||||
if ((error = workdir_iterator__expand_dir(wi)) < 0) {
|
if ((error = workdir_iterator__expand_dir(wi)) < 0) {
|
||||||
if (error == GIT_ENOTFOUND)
|
if (error == GIT_ENOTFOUND)
|
||||||
@ -835,10 +882,13 @@ static int spoolandsort_iterator__seek(git_iterator *self, const char *prefix)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spoolandsort_iterator__reset(git_iterator *self)
|
static int spoolandsort_iterator__reset(
|
||||||
|
git_iterator *self, const char *start, const char *end)
|
||||||
{
|
{
|
||||||
spoolandsort_iterator *si = (spoolandsort_iterator *)self;
|
spoolandsort_iterator *si = (spoolandsort_iterator *)self;
|
||||||
|
|
||||||
|
GIT_UNUSED(start); GIT_UNUSED(end);
|
||||||
|
|
||||||
si->position = 0;
|
si->position = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -37,7 +37,7 @@ struct git_iterator {
|
|||||||
int (*at_end)(git_iterator *);
|
int (*at_end)(git_iterator *);
|
||||||
int (*advance)(git_iterator *, const git_index_entry **);
|
int (*advance)(git_iterator *, const git_index_entry **);
|
||||||
int (*seek)(git_iterator *, const char *prefix);
|
int (*seek)(git_iterator *, const char *prefix);
|
||||||
int (*reset)(git_iterator *);
|
int (*reset)(git_iterator *, const char *start, const char *end);
|
||||||
void (*free)(git_iterator *);
|
void (*free)(git_iterator *);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,9 +126,10 @@ GIT_INLINE(int) git_iterator_seek(
|
|||||||
return iter->seek(iter, prefix);
|
return iter->seek(iter, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
|
GIT_INLINE(int) git_iterator_reset(
|
||||||
|
git_iterator *iter, const char *start, const char *end)
|
||||||
{
|
{
|
||||||
return iter->reset(iter);
|
return iter->reset(iter, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_INLINE(void) git_iterator_free(git_iterator *iter)
|
GIT_INLINE(void) git_iterator_free(git_iterator *iter)
|
||||||
|
35
src/path.c
35
src/path.c
@ -770,18 +770,30 @@ int git_path_dirload(
|
|||||||
int git_path_with_stat_cmp(const void *a, const void *b)
|
int git_path_with_stat_cmp(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
const git_path_with_stat *psa = a, *psb = b;
|
const git_path_with_stat *psa = a, *psb = b;
|
||||||
return git__strcmp_cb(psa->path, psb->path);
|
return strcmp(psa->path, psb->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_path_with_stat_cmp_icase(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const git_path_with_stat *psa = a, *psb = b;
|
||||||
|
return strcasecmp(psa->path, psb->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_path_dirload_with_stat(
|
int git_path_dirload_with_stat(
|
||||||
const char *path,
|
const char *path,
|
||||||
size_t prefix_len,
|
size_t prefix_len,
|
||||||
|
bool ignore_case,
|
||||||
|
const char *start_stat,
|
||||||
|
const char *end_stat,
|
||||||
git_vector *contents)
|
git_vector *contents)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
git_path_with_stat *ps;
|
git_path_with_stat *ps;
|
||||||
git_buf full = GIT_BUF_INIT;
|
git_buf full = GIT_BUF_INIT;
|
||||||
|
int (*strncomp)(const char *a, const char *b, size_t sz);
|
||||||
|
size_t start_len = start_stat ? strlen(start_stat) : 0;
|
||||||
|
size_t end_len = end_stat ? strlen(end_stat) : 0, cmp_len;
|
||||||
|
|
||||||
if (git_buf_set(&full, path, prefix_len) < 0)
|
if (git_buf_set(&full, path, prefix_len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -793,11 +805,23 @@ int git_path_dirload_with_stat(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strncomp = ignore_case ? git__strncasecmp : git__strncmp;
|
||||||
|
|
||||||
|
/* stat struct at start of git_path_with_stat, so shift path text */
|
||||||
git_vector_foreach(contents, i, ps) {
|
git_vector_foreach(contents, i, ps) {
|
||||||
size_t path_len = strlen((char *)ps);
|
size_t path_len = strlen((char *)ps);
|
||||||
|
|
||||||
memmove(ps->path, ps, path_len + 1);
|
memmove(ps->path, ps, path_len + 1);
|
||||||
ps->path_len = path_len;
|
ps->path_len = path_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
git_vector_foreach(contents, i, ps) {
|
||||||
|
/* skip if before start_stat or after end_stat */
|
||||||
|
cmp_len = min(start_len, ps->path_len);
|
||||||
|
if (cmp_len && strncomp(ps->path, start_stat, cmp_len) < 0)
|
||||||
|
continue;
|
||||||
|
cmp_len = min(end_len, ps->path_len);
|
||||||
|
if (cmp_len && strncomp(ps->path, end_stat, cmp_len) > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
|
if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
|
||||||
(error = git_path_lstat(full.ptr, &ps->st)) < 0)
|
(error = git_path_lstat(full.ptr, &ps->st)) < 0)
|
||||||
@ -806,11 +830,14 @@ int git_path_dirload_with_stat(
|
|||||||
git_buf_truncate(&full, prefix_len);
|
git_buf_truncate(&full, prefix_len);
|
||||||
|
|
||||||
if (S_ISDIR(ps->st.st_mode)) {
|
if (S_ISDIR(ps->st.st_mode)) {
|
||||||
ps->path[path_len] = '/';
|
ps->path[ps->path_len++] = '/';
|
||||||
ps->path[path_len + 1] = '\0';
|
ps->path[ps->path_len] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* sort now that directory suffix is added */
|
||||||
|
git_vector_sort(contents);
|
||||||
|
|
||||||
git_buf_free(&full);
|
git_buf_free(&full);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
|
23
src/path.h
23
src/path.h
@ -321,18 +321,33 @@ typedef struct {
|
|||||||
} git_path_with_stat;
|
} git_path_with_stat;
|
||||||
|
|
||||||
extern int git_path_with_stat_cmp(const void *a, const void *b);
|
extern int git_path_with_stat_cmp(const void *a, const void *b);
|
||||||
|
extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all directory entries along with stat info into a vector.
|
* Load all directory entries along with stat info into a vector.
|
||||||
*
|
*
|
||||||
* This is just like git_path_dirload except that each entry in the
|
* This adds four things on top of plain `git_path_dirload`:
|
||||||
* vector is a git_path_with_stat structure that contains both the
|
*
|
||||||
* path and the stat info, plus directories will have a / suffixed
|
* 1. Each entry in the vector is a `git_path_with_stat` struct that
|
||||||
* to their path name.
|
* contains both the path and the stat info
|
||||||
|
* 2. The entries will be sorted alphabetically
|
||||||
|
* 3. Entries that are directories will be suffixed with a '/'
|
||||||
|
* 4. Optionally, you can be a start and end prefix and only elements
|
||||||
|
* after the start and before the end (inclusively) will be stat'ed.
|
||||||
|
*
|
||||||
|
* @param path The directory to read from
|
||||||
|
* @param prefix_len The trailing part of path to prefix to entry paths
|
||||||
|
* @param ignore_case How to sort and compare paths with start/end limits
|
||||||
|
* @param start_stat As optimization, only stat values after this prefix
|
||||||
|
* @param end_stat As optimization, only stat values before this prefix
|
||||||
|
* @param contents Vector to fill with git_path_with_stat structures
|
||||||
*/
|
*/
|
||||||
extern int git_path_dirload_with_stat(
|
extern int git_path_dirload_with_stat(
|
||||||
const char *path,
|
const char *path,
|
||||||
size_t prefix_len,
|
size_t prefix_len,
|
||||||
|
bool ignore_case,
|
||||||
|
const char *start_stat,
|
||||||
|
const char *end_stat,
|
||||||
git_vector *contents);
|
git_vector *contents);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -301,6 +301,9 @@ int git_tree__prefix_position(git_tree *tree, const char *path)
|
|||||||
struct tree_key_search ksearch;
|
struct tree_key_search ksearch;
|
||||||
size_t at_pos;
|
size_t at_pos;
|
||||||
|
|
||||||
|
if (!path)
|
||||||
|
return 0;
|
||||||
|
|
||||||
ksearch.filename = path;
|
ksearch.filename = path;
|
||||||
ksearch.filename_len = strlen(path);
|
ksearch.filename_len = strlen(path);
|
||||||
|
|
||||||
|
@ -29,6 +29,10 @@ struct git_treebuilder {
|
|||||||
git_vector entries;
|
git_vector entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
GIT_INLINE(int) git_tree__dup(git_tree **dest, git_tree *source)
|
||||||
|
{
|
||||||
|
return git_object__dup((git_object **)dest, (git_object *)source);
|
||||||
|
}
|
||||||
|
|
||||||
GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
|
GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
|
||||||
{
|
{
|
||||||
|
12
src/util.c
12
src/util.c
@ -199,9 +199,17 @@ int git__strncmp(const char *a, const char *b, size_t sz)
|
|||||||
|
|
||||||
int git__strncasecmp(const char *a, const char *b, size_t sz)
|
int git__strncasecmp(const char *a, const char *b, size_t sz)
|
||||||
{
|
{
|
||||||
while (sz && *a && *b && tolower(*a) == tolower(*b))
|
int al, bl;
|
||||||
|
|
||||||
|
while (sz && *a && *b) {
|
||||||
|
al = (unsigned char)tolower(*a);
|
||||||
|
bl = (unsigned char)tolower(*b);
|
||||||
|
if (al != bl)
|
||||||
|
break;
|
||||||
--sz, ++a, ++b;
|
--sz, ++a, ++b;
|
||||||
return !sz ? 0 : (tolower(*a) - tolower(*b));
|
}
|
||||||
|
|
||||||
|
return !sz ? 0 : al - bl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void git__strntolower(char *str, size_t len)
|
void git__strntolower(char *str, size_t len)
|
||||||
|
@ -31,25 +31,35 @@ static void tree_iterator_test(
|
|||||||
git_tree *t;
|
git_tree *t;
|
||||||
git_iterator *i;
|
git_iterator *i;
|
||||||
const git_index_entry *entry;
|
const git_index_entry *entry;
|
||||||
int count = 0;
|
int count = 0, count_post_reset = 0;
|
||||||
git_repository *repo = cl_git_sandbox_init(sandbox);
|
git_repository *repo = cl_git_sandbox_init(sandbox);
|
||||||
|
|
||||||
cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
|
cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
|
||||||
cl_git_pass(git_iterator_for_tree_range(&i, t, start, end));
|
cl_git_pass(git_iterator_for_tree_range(&i, t, start, end));
|
||||||
cl_git_pass(git_iterator_current(i, &entry));
|
|
||||||
|
|
||||||
|
/* test loop */
|
||||||
|
cl_git_pass(git_iterator_current(i, &entry));
|
||||||
while (entry != NULL) {
|
while (entry != NULL) {
|
||||||
if (expected_values != NULL)
|
if (expected_values != NULL)
|
||||||
cl_assert_equal_s(expected_values[count], entry->path);
|
cl_assert_equal_s(expected_values[count], entry->path);
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
|
cl_git_pass(git_iterator_advance(i, &entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test reset */
|
||||||
|
cl_git_pass(git_iterator_reset(i, NULL, NULL));
|
||||||
|
cl_git_pass(git_iterator_current(i, &entry));
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (expected_values != NULL)
|
||||||
|
cl_assert_equal_s(expected_values[count_post_reset], entry->path);
|
||||||
|
count_post_reset++;
|
||||||
cl_git_pass(git_iterator_advance(i, &entry));
|
cl_git_pass(git_iterator_advance(i, &entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
git_iterator_free(i);
|
git_iterator_free(i);
|
||||||
|
|
||||||
cl_assert(expected_count == count);
|
cl_assert_equal_i(expected_count, count);
|
||||||
|
cl_assert_equal_i(count, count_post_reset);
|
||||||
|
|
||||||
git_tree_free(t);
|
git_tree_free(t);
|
||||||
}
|
}
|
||||||
@ -520,7 +530,7 @@ static void workdir_iterator_test(
|
|||||||
{
|
{
|
||||||
git_iterator *i;
|
git_iterator *i;
|
||||||
const git_index_entry *entry;
|
const git_index_entry *entry;
|
||||||
int count = 0, count_all = 0;
|
int count = 0, count_all = 0, count_all_post_reset = 0;
|
||||||
git_repository *repo = cl_git_sandbox_init(sandbox);
|
git_repository *repo = cl_git_sandbox_init(sandbox);
|
||||||
|
|
||||||
cl_git_pass(git_iterator_for_workdir_range(&i, repo, start, end));
|
cl_git_pass(git_iterator_for_workdir_range(&i, repo, start, end));
|
||||||
@ -547,10 +557,26 @@ static void workdir_iterator_test(
|
|||||||
cl_git_pass(git_iterator_advance(i, &entry));
|
cl_git_pass(git_iterator_advance(i, &entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cl_git_pass(git_iterator_reset(i, NULL, NULL));
|
||||||
|
cl_git_pass(git_iterator_current(i, &entry));
|
||||||
|
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (S_ISDIR(entry->mode)) {
|
||||||
|
cl_git_pass(git_iterator_advance_into_directory(i, &entry));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (expected_names != NULL)
|
||||||
|
cl_assert_equal_s(
|
||||||
|
expected_names[count_all_post_reset], entry->path);
|
||||||
|
count_all_post_reset++;
|
||||||
|
cl_git_pass(git_iterator_advance(i, &entry));
|
||||||
|
}
|
||||||
|
|
||||||
git_iterator_free(i);
|
git_iterator_free(i);
|
||||||
|
|
||||||
cl_assert_equal_i(expected_count,count);
|
cl_assert_equal_i(expected_count, count);
|
||||||
cl_assert_equal_i(expected_count + expected_ignores, count_all);
|
cl_assert_equal_i(expected_count + expected_ignores, count_all);
|
||||||
|
cl_assert_equal_i(count_all, count_all_post_reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_diff_iterator__workdir_0(void)
|
void test_diff_iterator__workdir_0(void)
|
||||||
|
Loading…
Reference in New Issue
Block a user