Commit 1ac10aae by Vicent Martí

Merge pull request #1408 from arrbee/refactor-iterators

Refactor iterators
parents b70bf922 62beacd3
......@@ -119,7 +119,7 @@ void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
GIT_INLINE(ssize_t) git_buf_rfind_next(const git_buf *buf, char ch)
{
ssize_t idx = (ssize_t)buf->size - 1;
while (idx >= 0 && buf->ptr[idx] == ch) idx--;
......@@ -127,18 +127,17 @@ GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
return idx;
}
GIT_INLINE(ssize_t) git_buf_rfind(git_buf *buf, char ch)
GIT_INLINE(ssize_t) git_buf_rfind(const git_buf *buf, char ch)
{
ssize_t idx = (ssize_t)buf->size - 1;
while (idx >= 0 && buf->ptr[idx] != ch) idx--;
return idx;
}
GIT_INLINE(ssize_t) git_buf_find(git_buf *buf, char ch)
GIT_INLINE(ssize_t) git_buf_find(const git_buf *buf, char ch)
{
size_t idx = 0;
while (idx < buf->size && buf->ptr[idx] != ch) idx++;
return (idx == buf->size) ? -1 : (ssize_t)idx;
void *found = memchr(buf->ptr, ch, buf->size);
return found ? (ssize_t)((const char *)found - buf->ptr) : -1;
}
/* Remove whitespace from the end of the buffer */
......
......@@ -473,7 +473,7 @@ static int checkout_action(
if (cmp == 0) {
if (wd->mode == GIT_FILEMODE_TREE) {
/* case 2 - entry prefixed by workdir tree */
if (git_iterator_advance_into_directory(workdir, &wd) < 0)
if (git_iterator_advance_into(&wd, workdir) < 0)
goto fail;
*wditem_ptr = wd;
......@@ -484,14 +484,14 @@ static int checkout_action(
if (delta->old_file.path[strlen(wd->path)] == '/') {
act = checkout_action_with_wd_blocker(data, delta, wd);
*wditem_ptr =
git_iterator_advance(workdir, &wd) ? NULL : wd;
git_iterator_advance(&wd, workdir) ? NULL : wd;
return act;
}
}
/* case 1 - handle wd item (if it matches pathspec) */
if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0 ||
git_iterator_advance(workdir, &wd) < 0)
git_iterator_advance(&wd, workdir) < 0)
goto fail;
*wditem_ptr = wd;
......@@ -501,7 +501,7 @@ static int checkout_action(
if (cmp == 0) {
/* case 4 */
act = checkout_action_with_wd(data, delta, wd);
*wditem_ptr = git_iterator_advance(workdir, &wd) ? NULL : wd;
*wditem_ptr = git_iterator_advance(&wd, workdir) ? NULL : wd;
return act;
}
......@@ -514,7 +514,7 @@ static int checkout_action(
if (delta->status == GIT_DELTA_TYPECHANGE) {
if (delta->old_file.mode == GIT_FILEMODE_TREE) {
act = checkout_action_with_wd(data, delta, wd);
if (git_iterator_advance_into_directory(workdir, &wd) < 0)
if (git_iterator_advance_into(&wd, workdir) < 0)
wd = NULL;
*wditem_ptr = wd;
return act;
......@@ -525,7 +525,7 @@ static int checkout_action(
delta->old_file.mode == GIT_FILEMODE_COMMIT)
{
act = checkout_action_with_wd(data, delta, wd);
if (git_iterator_advance(workdir, &wd) < 0)
if (git_iterator_advance(&wd, workdir) < 0)
wd = NULL;
*wditem_ptr = wd;
return act;
......@@ -554,7 +554,7 @@ static int checkout_remaining_wd_items(
while (wd && !error) {
if (!(error = checkout_action_wd_only(data, workdir, wd, spec)))
error = git_iterator_advance(workdir, &wd);
error = git_iterator_advance(&wd, workdir);
}
return error;
......@@ -578,7 +578,7 @@ static int checkout_get_actions(
git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
return -1;
if ((error = git_iterator_current(workdir, &wditem)) < 0)
if ((error = git_iterator_current(&wditem, workdir)) < 0)
goto fail;
deltas = &data->diff->deltas;
......@@ -1134,9 +1134,10 @@ static int checkout_data_init(
if ((error = git_config_refresh(cfg)) < 0)
goto cleanup;
if (git_iterator_inner_type(target) == GIT_ITERATOR_TYPE_INDEX) {
/* if we are iterating over the index, don't reload */
data->index = git_iterator_index_get_index(target);
/* if we are checking out the index, don't reload,
* otherwise get index and force reload
*/
if ((data->index = git_iterator_get_index(target)) != NULL) {
GIT_REFCOUNT_INC(data->index);
} else {
/* otherwise, grab and reload the index */
......@@ -1241,16 +1242,15 @@ int git_checkout_iterator(
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_workdir_range(
&workdir, data.repo, iterflags, data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_tree_range(
(error = git_iterator_for_workdir(
&workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_tree(
&baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0)
goto cleanup;
/* Handle case insensitivity for baseline if necessary */
if (git_iterator_ignore_case(workdir) != git_iterator_ignore_case(baseline))
if ((error = git_iterator_spoolandsort_push(baseline, true)) < 0)
goto cleanup;
/* Should not have case insensitivity mismatch */
assert(git_iterator_ignore_case(workdir) == git_iterator_ignore_case(baseline));
/* Generate baseline-to-target diff which will include an entry for
* every possible update that might need to be made.
......@@ -1321,7 +1321,7 @@ int git_checkout_index(
return error;
GIT_REFCOUNT_INC(index);
if (!(error = git_iterator_for_index(&index_i, index)))
if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL)))
error = git_checkout_iterator(index_i, opts);
git_iterator_free(index_i);
......@@ -1348,7 +1348,7 @@ int git_checkout_tree(
return -1;
}
if (!(error = git_iterator_for_tree(&tree_i, tree)))
if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL)))
error = git_checkout_iterator(tree_i, opts);
git_iterator_free(tree_i);
......@@ -1369,7 +1369,7 @@ int git_checkout_head(
return error;
if (!(error = checkout_lookup_head_tree(&head, repo)) &&
!(error = git_iterator_for_tree(&head_i, head)))
!(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL)))
error = git_checkout_iterator(head_i, opts);
git_iterator_free(head_i);
......
......@@ -623,18 +623,13 @@ int git_diff__from_iterators(
goto fail;
if (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
/* If either iterator does not have ignore_case set, then we will
* spool its data, sort it icase, and use that for the merge join
* with the other iterator which was icase sorted. This call is
* a no-op on an iterator that already matches "ignore_case".
*/
if (git_iterator_spoolandsort_push(old_iter, true) < 0 ||
git_iterator_spoolandsort_push(new_iter, true) < 0)
if (git_iterator_set_ignore_case(old_iter, true) < 0 ||
git_iterator_set_ignore_case(new_iter, true) < 0)
goto fail;
}
if (git_iterator_current(old_iter, &oitem) < 0 ||
git_iterator_current(new_iter, &nitem) < 0)
if (git_iterator_current(&oitem, old_iter) < 0 ||
git_iterator_current(&nitem, new_iter) < 0)
goto fail;
/* run iterators building diffs */
......@@ -666,12 +661,12 @@ int git_diff__from_iterators(
if (S_ISDIR(nitem->mode) &&
!(diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS))
{
if (git_iterator_advance(new_iter, &nitem) < 0)
if (git_iterator_advance(&nitem, new_iter) < 0)
goto fail;
}
}
if (git_iterator_advance(old_iter, &oitem) < 0)
if (git_iterator_advance(&oitem, old_iter) < 0)
goto fail;
}
......@@ -699,7 +694,7 @@ int git_diff__from_iterators(
/* do not advance into directories that contain a .git file */
if (!contains_oitem && recurse_untracked) {
git_buf *full = NULL;
if (git_iterator_current_workdir_path(new_iter, &full) < 0)
if (git_iterator_current_workdir_path(&full, new_iter) < 0)
goto fail;
if (git_path_contains_dir(full, DOT_GIT))
recurse_untracked = false;
......@@ -713,9 +708,19 @@ int git_diff__from_iterators(
git_iterator_current_is_ignored(new_iter))
git_buf_sets(&ignore_prefix, nitem->path);
if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
goto fail;
/* advance into directory */
error = git_iterator_advance_into(&nitem, new_iter);
/* if directory is empty, can't advance into it, so skip */
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = git_iterator_advance(&nitem, new_iter);
git_buf_clear(&ignore_prefix);
}
if (error < 0)
goto fail;
continue;
}
}
......@@ -736,7 +741,7 @@ int git_diff__from_iterators(
* skip the file.
*/
else if (delta_type == GIT_DELTA_IGNORED) {
if (git_iterator_advance(new_iter, &nitem) < 0)
if (git_iterator_advance(&nitem, new_iter) < 0)
goto fail;
continue; /* ignored parent directory, so skip completely */
}
......@@ -765,7 +770,7 @@ int git_diff__from_iterators(
}
}
if (git_iterator_advance(new_iter, &nitem) < 0)
if (git_iterator_advance(&nitem, new_iter) < 0)
goto fail;
}
......@@ -775,10 +780,9 @@ int git_diff__from_iterators(
else {
assert(oitem && nitem && cmp == 0);
if (maybe_modified(
old_iter, oitem, new_iter, nitem, diff) < 0 ||
git_iterator_advance(old_iter, &oitem) < 0 ||
git_iterator_advance(new_iter, &nitem) < 0)
if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
git_iterator_advance(&oitem, old_iter) < 0 ||
git_iterator_advance(&nitem, new_iter) < 0)
goto fail;
}
}
......@@ -817,8 +821,8 @@ int git_diff_tree_to_tree(
assert(diff && repo);
DIFF_FROM_ITERATORS(
git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx),
git_iterator_for_tree_range(&b, new_tree, 0, pfx, pfx)
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
git_iterator_for_tree(&b, new_tree, 0, pfx, pfx)
);
return error;
......@@ -839,8 +843,8 @@ int git_diff_tree_to_index(
return error;
DIFF_FROM_ITERATORS(
git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx),
git_iterator_for_index_range(&b, index, 0, pfx, pfx)
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
git_iterator_for_index(&b, index, 0, pfx, pfx)
);
return error;
......@@ -860,8 +864,9 @@ int git_diff_index_to_workdir(
return error;
DIFF_FROM_ITERATORS(
git_iterator_for_index_range(&a, index, 0, pfx, pfx),
git_iterator_for_workdir_range(&b, repo, 0, pfx, pfx)
git_iterator_for_index(&a, index, 0, pfx, pfx),
git_iterator_for_workdir(
&b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
);
return error;
......@@ -879,8 +884,9 @@ int git_diff_tree_to_workdir(
assert(diff && repo);
DIFF_FROM_ITERATORS(
git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx),
git_iterator_for_workdir_range(&b, repo, 0, pfx, pfx)
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
git_iterator_for_workdir(
&b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
);
return error;
......
......@@ -6,6 +6,7 @@
*/
#include "hashsig.h"
#include "fileops.h"
#include "util.h"
typedef uint32_t hashsig_t;
typedef uint64_t hashsig_state;
......@@ -19,7 +20,7 @@ typedef uint64_t hashsig_state;
#define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
typedef int (GIT_STDLIB_CALL *hashsig_cmp)(const void *a, const void *b);
typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
typedef struct {
int size, asize;
......@@ -53,15 +54,17 @@ static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
h->cmp = cmp;
}
static int GIT_STDLIB_CALL hashsig_cmp_max(const void *a, const void *b)
static int hashsig_cmp_max(const void *a, const void *b, void *payload)
{
hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
GIT_UNUSED(payload);
return (av < bv) ? -1 : (av > bv) ? 1 : 0;
}
static int GIT_STDLIB_CALL hashsig_cmp_min(const void *a, const void *b)
static int hashsig_cmp_min(const void *a, const void *b, void *payload)
{
hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
GIT_UNUSED(payload);
return (av > bv) ? -1 : (av < bv) ? 1 : 0;
}
......@@ -69,7 +72,7 @@ static void hashsig_heap_up(hashsig_heap *h, int el)
{
int parent_el = HEAP_PARENT_OF(el);
while (el > 0 && h->cmp(&h->values[parent_el], &h->values[el]) > 0) {
while (el > 0 && h->cmp(&h->values[parent_el], &h->values[el], NULL) > 0) {
hashsig_t t = h->values[el];
h->values[el] = h->values[parent_el];
h->values[parent_el] = t;
......@@ -92,10 +95,10 @@ static void hashsig_heap_down(hashsig_heap *h, int el)
lv = h->values[lel];
rv = h->values[rel];
if (h->cmp(&v, &lv) < 0 && h->cmp(&v, &rv) < 0)
if (h->cmp(&v, &lv, NULL) < 0 && h->cmp(&v, &rv, NULL) < 0)
break;
swapel = (h->cmp(&lv, &rv) < 0) ? lel : rel;
swapel = (h->cmp(&lv, &rv, NULL) < 0) ? lel : rel;
h->values[el] = h->values[swapel];
h->values[swapel] = v;
......@@ -107,13 +110,13 @@ static void hashsig_heap_down(hashsig_heap *h, int el)
static void hashsig_heap_sort(hashsig_heap *h)
{
/* only need to do this at the end for signature comparison */
qsort(h->values, h->size, sizeof(hashsig_t), h->cmp);
git__qsort_r(h->values, h->size, sizeof(hashsig_t), h->cmp, NULL);
}
static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
{
/* if heap is full, pop top if new element should replace it */
if (h->size == h->asize && h->cmp(&val, &h->values[0]) > 0) {
if (h->size == h->asize && h->cmp(&val, &h->values[0], NULL) > 0) {
h->size--;
h->values[0] = h->values[h->size];
hashsig_heap_down(h, 0);
......@@ -343,7 +346,7 @@ static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
/* hash heaps are sorted - just look for overlap vs total */
for (i = 0, j = 0; i < a->size && j < b->size; ) {
cmp = a->cmp(&a->values[i], &b->values[j]);
cmp = a->cmp(&a->values[i], &b->values[j], NULL);
if (cmp < 0)
++i;
......
......@@ -1679,54 +1679,3 @@ git_repository *git_index_owner(const git_index *index)
{
return INDEX_OWNER(index);
}
int git_index_read_tree_match(
git_index *index, git_tree *tree, git_strarray *strspec)
{
#if 0
git_iterator *iter = NULL;
const git_index_entry *entry;
char *pfx = NULL;
git_vector pathspec = GIT_VECTOR_INIT;
git_pool pathpool = GIT_POOL_INIT_STRINGPOOL;
#endif
if (!git_pathspec_is_interesting(strspec))
return git_index_read_tree(index, tree);
return git_index_read_tree(index, tree);
#if 0
/* The following loads the matches into the index, but doesn't
* erase obsoleted entries (e.g. you load a blob at "a/b" which
* should obsolete a blob at "a/b/c/d" since b is no longer a tree)
*/
if (git_pathspec_init(&pathspec, strspec, &pathpool) < 0)
return -1;
pfx = git_pathspec_prefix(strspec);
if ((error = git_iterator_for_tree_range(&iter, tree, pfx, pfx)) < 0 ||
(error = git_iterator_current(iter, &entry)) < 0)
goto cleanup;
while (entry != NULL) {
if (git_pathspec_match_path(
&pathspec, entry->path, false, false, NULL) &&
(error = git_index_add(index, entry)) < 0)
goto cleanup;
if ((error = git_iterator_advance(iter, &entry)) < 0)
goto cleanup;
}
cleanup:
git_iterator_free(iter);
git_pathspec_free(&pathspec);
git_pool_clear(&pathpool);
git__free(pfx);
return error;
#endif
}
......@@ -50,7 +50,4 @@ extern int git_index_entry__cmp_icase(const void *a, const void *b);
extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
extern int git_index_read_tree_match(
git_index *index, git_tree *tree, git_strarray *strspec);
#endif
......@@ -12,11 +12,6 @@
#include "vector.h"
#include "buffer.h"
#define ITERATOR_PREFIXCMP(ITER, STR, PREFIX) \
(((ITER).flags & GIT_ITERATOR_IGNORE_CASE) != 0 ? \
git__prefixcmp_icase((STR), (PREFIX)) : \
git__prefixcmp((STR), (PREFIX)))
typedef struct git_iterator git_iterator;
typedef enum {
......@@ -24,20 +19,26 @@ typedef enum {
GIT_ITERATOR_TYPE_TREE = 1,
GIT_ITERATOR_TYPE_INDEX = 2,
GIT_ITERATOR_TYPE_WORKDIR = 3,
GIT_ITERATOR_TYPE_SPOOLANDSORT = 4
} git_iterator_type_t;
typedef enum {
GIT_ITERATOR_IGNORE_CASE = (1 << 0), /* ignore_case */
GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1), /* force ignore_case off */
/** ignore case for entry sort order */
GIT_ITERATOR_IGNORE_CASE = (1 << 0),
/** force case sensitivity for entry sort order */
GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1),
/** return tree items in addition to blob items */
GIT_ITERATOR_INCLUDE_TREES = (1 << 2),
/** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
GIT_ITERATOR_DONT_AUTOEXPAND = (1 << 3),
} git_iterator_flag_t;
typedef struct {
int (*current)(git_iterator *, const git_index_entry **);
int (*at_end)(git_iterator *);
int (*advance)(git_iterator *, const git_index_entry **);
int (*current)(const git_index_entry **, git_iterator *);
int (*advance)(const git_index_entry **, git_iterator *);
int (*advance_into)(const git_index_entry **, git_iterator *);
int (*seek)(git_iterator *, const char *prefix);
int (*reset)(git_iterator *, const char *start, const char *end);
int (*at_end)(git_iterator *);
void (*free)(git_iterator *);
} git_iterator_callbacks;
......@@ -52,86 +53,92 @@ struct git_iterator {
};
extern int git_iterator_for_nothing(
git_iterator **out, git_iterator_flag_t flags);
git_iterator **out,
git_iterator_flag_t flags,
const char *start,
const char *end);
/* tree iterators will match the ignore_case value from the index of the
* repository, unless you override with a non-zero flag value
*/
extern int git_iterator_for_tree_range(
extern int git_iterator_for_tree(
git_iterator **out,
git_tree *tree,
git_iterator_flag_t flags,
const char *start,
const char *end);
GIT_INLINE(int) git_iterator_for_tree(git_iterator **out, git_tree *tree)
{
return git_iterator_for_tree_range(out, tree, 0, NULL, NULL);
}
/* index iterators will take the ignore_case value from the index; the
* ignore_case flags are not used
*/
extern int git_iterator_for_index_range(
extern int git_iterator_for_index(
git_iterator **out,
git_index *index,
git_iterator_flag_t flags,
const char *start,
const char *end);
GIT_INLINE(int) git_iterator_for_index(git_iterator **out, git_index *index)
{
return git_iterator_for_index_range(out, index, 0, NULL, NULL);
}
/* workdir iterators will match the ignore_case value from the index of the
* repository, unless you override with a non-zero flag value
*/
extern int git_iterator_for_workdir_range(
extern int git_iterator_for_workdir(
git_iterator **out,
git_repository *repo,
git_iterator_flag_t flags,
const char *start,
const char *end);
GIT_INLINE(int) git_iterator_for_workdir(git_iterator **out, git_repository *repo)
{
return git_iterator_for_workdir_range(out, repo, 0, NULL, NULL);
}
extern void git_iterator_free(git_iterator *iter);
/* Spool all iterator values, resort with alternative ignore_case value
* and replace callbacks with spoolandsort alternates.
*/
extern int git_iterator_spoolandsort_push(git_iterator *iter, bool ignore_case);
/* Restore original callbacks - not required in most circumstances */
extern void git_iterator_spoolandsort_pop(git_iterator *iter);
/* Entry is not guaranteed to be fully populated. For a tree iterator,
* we will only populate the mode, oid and path, for example. For a workdir
* iterator, we will not populate the oid.
/* Return a git_index_entry structure for the current value the iterator
* is looking at or NULL if the iterator is at the end.
*
* The entry may noy be fully populated. Tree iterators will only have a
* value mode, OID, and path. Workdir iterators will not have an OID (but
* you can use `git_iterator_current_oid()` to calculate it on demand).
*
* You do not need to free the entry. It is still "owned" by the iterator.
* Once you call `git_iterator_advance`, then content of the old entry is
* no longer guaranteed to be valid.
* Once you call `git_iterator_advance()` then the old entry is no longer
* guaranteed to be valid - it may be freed or just overwritten in place.
*/
GIT_INLINE(int) git_iterator_current(
git_iterator *iter, const git_index_entry **entry)
const git_index_entry **entry, git_iterator *iter)
{
return iter->cb->current(iter, entry);
return iter->cb->current(entry, iter);
}
GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)
/**
* Advance to the next item for the iterator.
*
* If GIT_ITERATOR_INCLUDE_TREES is set, this may be a tree item. If
* GIT_ITERATOR_DONT_AUTOEXPAND is set, calling this again when on a tree
* item will skip over all the items under that tree.
*/
GIT_INLINE(int) git_iterator_advance(
const git_index_entry **entry, git_iterator *iter)
{
return iter->cb->at_end(iter);
return iter->cb->advance(entry, iter);
}
GIT_INLINE(int) git_iterator_advance(
git_iterator *iter, const git_index_entry **entry)
/**
* Iterate into a tree item (when GIT_ITERATOR_DONT_AUTOEXPAND is set).
*
* git_iterator_advance() steps through all items being iterated over
* (either with or without trees, depending on GIT_ITERATOR_INCLUDE_TREES),
* but if GIT_ITERATOR_DONT_AUTOEXPAND is set, it will skip to the next
* sibling of a tree instead of going to the first child of the tree. In
* that case, use this function to advance to the first child of the tree.
*
* If the current item is not a tree, this is a no-op.
*
* For working directory iterators only, a tree (i.e. directory) can be
* empty. In that case, this function returns GIT_ENOTFOUND and does not
* advance. That can't happen for tree and index iterators.
*/
GIT_INLINE(int) git_iterator_advance_into(
const git_index_entry **entry, git_iterator *iter)
{
return iter->cb->advance(iter, entry);
return iter->cb->advance_into(entry, iter);
}
GIT_INLINE(int) git_iterator_seek(
......@@ -146,6 +153,11 @@ GIT_INLINE(int) git_iterator_reset(
return iter->cb->reset(iter, start, end);
}
GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)
{
return iter->cb->at_end(iter);
}
GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)
{
return iter->type;
......@@ -166,47 +178,28 @@ GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter)
return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0);
}
extern int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case);
extern int git_iterator_current_tree_entry(
git_iterator *iter, const git_tree_entry **tree_entry);
const git_tree_entry **entry_out, git_iterator *iter);
extern int git_iterator_current_parent_tree(
git_iterator *iter, const char *parent_path, const git_tree **tree_ptr);
const git_tree **tree_out, git_iterator *iter, const char *parent_path);
extern int git_iterator_current_is_ignored(git_iterator *iter);
/**
* Iterate into a workdir directory.
*
* Workdir iterators do not automatically descend into directories (so that
* when comparing two iterator entries you can detect a newly created
* directory in the workdir). As a result, you may get S_ISDIR items from
* a workdir iterator. If you wish to iterate over the contents of the
* directories you encounter, then call this function when you encounter
* a directory.
*
* If there are no files in the directory, this will end up acting like a
* regular advance and will skip past the directory, so you should be
* prepared for that case.
*
* On non-workdir iterators or if not pointing at a directory, this is a
* no-op and will not advance the iterator.
*/
extern int git_iterator_advance_into_directory(
git_iterator *iter, const git_index_entry **entry);
extern bool git_iterator_current_is_ignored(git_iterator *iter);
extern int git_iterator_cmp(
git_iterator *iter, const char *path_prefix);
/**
* Get the full path of the current item from a workdir iterator.
* This will return NULL for a non-workdir iterator.
* Get full path of the current item from a workdir iterator. This will
* return NULL for a non-workdir iterator. The git_buf is still owned by
* the iterator; this is exposed just for efficiency.
*/
extern int git_iterator_current_workdir_path(
git_iterator *iter, git_buf **path);
extern git_index *git_iterator_index_get_index(git_iterator *iter);
git_buf **path, git_iterator *iter);
extern git_iterator_type_t git_iterator_inner_type(git_iterator *iter);
/* Return index pointer if index iterator, else NULL */
extern git_index *git_iterator_get_index(git_iterator *iter);
#endif
......@@ -625,7 +625,7 @@ int git_note_iterator_new(
if (error < 0)
goto cleanup;
if ((error = git_iterator_for_tree(it, tree)) < 0)
if ((error = git_iterator_for_tree(it, tree, 0, NULL, NULL)) < 0)
git_iterator_free(*it);
cleanup:
......@@ -643,7 +643,7 @@ int git_note_next(
int error;
const git_index_entry *item;
if ((error = git_iterator_current(it, &item)) < 0)
if ((error = git_iterator_current(&item, it)) < 0)
goto exit;
if (item != NULL) {
......@@ -651,7 +651,7 @@ int git_note_next(
error = process_entry_path(item->path, annotated_id);
if (error >= 0)
error = git_iterator_advance(it, NULL);
error = git_iterator_advance(NULL, it);
} else {
error = GIT_ITEROVER;
}
......
......@@ -39,7 +39,6 @@ typedef int git_file;
* in Windows platforns.
*
* Use your manpages to check the docs on these.
* Straightforward
*/
extern int p_read(git_file fd, void *buf, size_t cnt);
......
......@@ -1135,10 +1135,10 @@ static int load_submodule_config_from_index(
const git_index_entry *entry;
if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
(error = git_iterator_for_index(&i, index)) < 0)
(error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0)
return error;
error = git_iterator_current(i, &entry);
error = git_iterator_current(&entry, i);
while (!error && entry != NULL) {
......@@ -1154,7 +1154,7 @@ static int load_submodule_config_from_index(
git_oid_cpy(gitmodules_oid, &entry->oid);
}
error = git_iterator_advance(i, &entry);
error = git_iterator_advance(&entry, i);
}
git_iterator_free(i);
......@@ -1173,12 +1173,12 @@ static int load_submodule_config_from_head(
if ((error = git_repository_head_tree(&head, repo)) < 0)
return error;
if ((error = git_iterator_for_tree(&i, head)) < 0) {
if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) {
git_tree_free(head);
return error;
}
error = git_iterator_current(i, &entry);
error = git_iterator_current(&entry, i);
while (!error && entry != NULL) {
......@@ -1195,7 +1195,7 @@ static int load_submodule_config_from_head(
git_oid_cpy(gitmodules_oid, &entry->oid);
}
error = git_iterator_advance(i, &entry);
error = git_iterator_advance(&entry, i);
}
git_iterator_free(i);
......
......@@ -24,7 +24,7 @@
#endif
static int binsearch(
void **dst, const void *x, size_t size, git__tsort_r_cmp cmp, void *payload)
void **dst, const void *x, size_t size, git__sort_r_cmp cmp, void *payload)
{
int l, c, r;
void *lx, *cx;
......@@ -71,7 +71,7 @@ static int binsearch(
/* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */
static void bisort(
void **dst, size_t start, size_t size, git__tsort_r_cmp cmp, void *payload)
void **dst, size_t start, size_t size, git__sort_r_cmp cmp, void *payload)
{
size_t i;
void *x;
......@@ -102,7 +102,7 @@ struct tsort_run {
struct tsort_store {
size_t alloc;
git__tsort_r_cmp cmp;
git__sort_r_cmp cmp;
void *payload;
void **storage;
};
......@@ -334,7 +334,7 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr,
while (0)
void git__tsort_r(
void **dst, size_t size, git__tsort_r_cmp cmp, void *payload)
void **dst, size_t size, git__sort_r_cmp cmp, void *payload)
{
struct tsort_store _store, *store = &_store;
struct tsort_run run_stack[128];
......
......@@ -8,6 +8,7 @@
#define INCLUDE_posix__w32_h__
#include <stdio.h>
#include <sys/param.h>
#define p_lstat(p,b) lstat(p,b)
#define p_readlink(a, b, c) readlink(a, b, c)
......
......@@ -607,3 +607,55 @@ size_t git__unescape(char *str)
return (pos - str);
}
#if defined(GIT_WIN32) || defined(BSD)
typedef struct {
git__sort_r_cmp cmp;
void *payload;
} git__qsort_r_glue;
static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
void *payload, const void *a, const void *b)
{
git__qsort_r_glue *glue = payload;
return glue->cmp(a, b, glue->payload);
}
#endif
void git__qsort_r(
void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
{
#if defined(__MINGW32__)
git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
#elif defined(GIT_WIN32)
git__qsort_r_glue glue = { cmp, payload };
qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
#elif defined(BSD)
git__qsort_r_glue glue = { cmp, payload };
qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
#else
qsort_r(els, nel, elsize, cmp, payload);
#endif
}
void git__insertsort_r(
void *els, size_t nel, size_t elsize, void *swapel,
git__sort_r_cmp cmp, void *payload)
{
uint8_t *base = els, *end = els + nel * elsize;
uint8_t *i, *j;
bool freeswap = !swapel;
if (freeswap)
swapel = git__malloc(elsize);
for (i = base + elsize; i < end; i += elsize)
for (j = i; j > base && cmp(j, j - elsize, payload) < 0; j -= elsize) {
memcpy(swapel, j, elsize);
memcpy(j, j - elsize, elsize);
memcpy(j - elsize, swapel, elsize);
}
if (freeswap)
git__free(swapel);
}
......@@ -146,11 +146,17 @@ typedef int (*git__tsort_cmp)(const void *a, const void *b);
extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp);
typedef int (*git__tsort_r_cmp)(const void *a, const void *b, void *payload);
typedef int (*git__sort_r_cmp)(const void *a, const void *b, void *payload);
extern void git__tsort_r(
void **dst, size_t size, git__tsort_r_cmp cmp, void *payload);
void **dst, size_t size, git__sort_r_cmp cmp, void *payload);
extern void git__qsort_r(
void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload);
extern void git__insertsort_r(
void *els, size_t nel, size_t elsize, void *swapel,
git__sort_r_cmp cmp, void *payload);
/**
* @param position If non-NULL, this will be set to the position where the
......
......@@ -35,26 +35,26 @@ static void tree_iterator_test(
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
cl_git_pass(git_iterator_for_tree_range(
cl_git_pass(git_iterator_for_tree(
&i, t, GIT_ITERATOR_DONT_IGNORE_CASE, start, end));
/* test loop */
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
if (expected_values != NULL)
cl_assert_equal_s(expected_values[count], entry->path);
count++;
cl_git_pass(git_iterator_advance(i, &entry));
cl_git_pass(git_iterator_advance(&entry, i));
}
/* test reset */
cl_git_pass(git_iterator_reset(i, NULL, NULL));
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_current(&entry, i));
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(&entry, i));
}
git_iterator_free(i);
......@@ -261,30 +261,30 @@ static void check_tree_entry(
const git_tree *tree;
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_iterator_current_tree_entry(i, &te));
cl_git_pass(git_iterator_current_tree_entry(&te, i));
cl_assert(te);
cl_assert(git_oid_streq(&te->oid, oid) == 0);
cl_git_pass(git_iterator_current(i, &ie));
cl_git_pass(git_iterator_current(&ie, i));
cl_git_pass(git_buf_sets(&path, ie->path));
if (oid_p) {
git_buf_rtruncate_at_char(&path, '/');
cl_git_pass(git_iterator_current_parent_tree(i, path.ptr, &tree));
cl_git_pass(git_iterator_current_parent_tree(&tree, i, path.ptr));
cl_assert(tree);
cl_assert(git_oid_streq(git_tree_id(tree), oid_p) == 0);
}
if (oid_pp) {
git_buf_rtruncate_at_char(&path, '/');
cl_git_pass(git_iterator_current_parent_tree(i, path.ptr, &tree));
cl_git_pass(git_iterator_current_parent_tree(&tree, i, path.ptr));
cl_assert(tree);
cl_assert(git_oid_streq(git_tree_id(tree), oid_pp) == 0);
}
if (oid_ppp) {
git_buf_rtruncate_at_char(&path, '/');
cl_git_pass(git_iterator_current_parent_tree(i, path.ptr, &tree));
cl_git_pass(git_iterator_current_parent_tree(&tree, i, path.ptr));
cl_assert(tree);
cl_assert(git_oid_streq(git_tree_id(tree), oid_ppp) == 0);
}
......@@ -305,9 +305,9 @@ void test_diff_iterator__tree_special_functions(void)
repo, "24fa9a9fc4e202313e24b648087495441dab432b");
cl_assert(t != NULL);
cl_git_pass(git_iterator_for_tree_range(
cl_git_pass(git_iterator_for_tree(
&i, t, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
if (strcmp(entry->path, "sub/file") == 0) {
......@@ -339,7 +339,7 @@ void test_diff_iterator__tree_special_functions(void)
rootoid, NULL);
}
cl_git_pass(git_iterator_advance(i, &entry));
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_assert_equal_i(4, cases);
......@@ -364,8 +364,8 @@ static void index_iterator_test(
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_iterator_for_index_range(&i, index, 0, start, end));
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_for_index(&i, index, 0, start, end));
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
if (expected_names != NULL)
......@@ -378,7 +378,7 @@ static void index_iterator_test(
}
count++;
cl_git_pass(git_iterator_advance(i, &entry));
cl_git_pass(git_iterator_advance(&entry, i));
}
git_iterator_free(i);
......@@ -538,14 +538,15 @@ static void workdir_iterator_test(
int count = 0, count_all = 0, count_all_post_reset = 0;
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_git_pass(git_iterator_for_workdir_range(&i, repo, 0, start, end));
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_for_workdir(
&i, repo, GIT_ITERATOR_DONT_AUTOEXPAND, start, end));
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
int ignored = git_iterator_current_is_ignored(i);
if (S_ISDIR(entry->mode)) {
cl_git_pass(git_iterator_advance_into_directory(i, &entry));
cl_git_pass(git_iterator_advance_into(&entry, i));
continue;
}
......@@ -559,22 +560,22 @@ static void workdir_iterator_test(
count++;
count_all++;
cl_git_pass(git_iterator_advance(i, &entry));
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_git_pass(git_iterator_reset(i, NULL, NULL));
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_current(&entry, i));
while (entry != NULL) {
if (S_ISDIR(entry->mode)) {
cl_git_pass(git_iterator_advance_into_directory(i, &entry));
cl_git_pass(git_iterator_advance_into(&entry, i));
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));
cl_git_pass(git_iterator_advance(&entry, i));
}
git_iterator_free(i);
......@@ -735,9 +736,9 @@ void test_diff_iterator__workdir_builtin_ignores(void)
cl_git_pass(p_mkdir("attr/sub/sub/.git", 0777));
cl_git_mkfile("attr/sub/.git", "whatever");
cl_git_pass(
git_iterator_for_workdir_range(&i, repo, 0, "dir", "sub/sub/file"));
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_for_workdir(
&i, repo, GIT_ITERATOR_DONT_AUTOEXPAND, "dir", "sub/sub/file"));
cl_git_pass(git_iterator_current(&entry, i));
for (idx = 0; entry != NULL; ++idx) {
int ignored = git_iterator_current_is_ignored(i);
......@@ -746,9 +747,9 @@ void test_diff_iterator__workdir_builtin_ignores(void)
cl_assert_(ignored == expected[idx].ignored, expected[idx].path);
if (!ignored && S_ISDIR(entry->mode))
cl_git_pass(git_iterator_advance_into_directory(i, &entry));
cl_git_pass(git_iterator_advance_into(&entry, i));
else
cl_git_pass(git_iterator_advance(i, &entry));
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_assert(expected[idx].path == NULL);
......@@ -764,17 +765,14 @@ static void check_wd_first_through_third_range(
int idx;
static const char *expected[] = { "FIRST", "second", "THIRD", NULL };
cl_git_pass(git_iterator_for_workdir_range(
cl_git_pass(git_iterator_for_workdir(
&i, repo, GIT_ITERATOR_IGNORE_CASE, start, end));
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_current(&entry, i));
for (idx = 0; entry != NULL; ++idx) {
cl_assert_equal_s(expected[idx], entry->path);
if (S_ISDIR(entry->mode))
cl_git_pass(git_iterator_advance_into_directory(i, &entry));
else
cl_git_pass(git_iterator_advance(i, &entry));
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_assert(expected[idx] == NULL);
......@@ -817,16 +815,16 @@ static void check_tree_range(
cl_git_pass(git_repository_head_tree(&head, repo));
cl_git_pass(git_iterator_for_tree_range(
cl_git_pass(git_iterator_for_tree(
&i, head,
ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE,
start, end));
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_current(&entry, i));
for (count = 0; entry != NULL; ) {
++count;
cl_git_pass(git_iterator_advance(i, &entry));
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_assert_equal_i(expected_count, count);
......@@ -882,15 +880,15 @@ static void check_index_range(
if (ignore_case != is_ignoring_case)
cl_git_pass(git_index_set_caps(index, caps ^ GIT_INDEXCAP_IGNORE_CASE));
cl_git_pass(git_iterator_for_index_range(&i, index, 0, start, end));
cl_git_pass(git_iterator_for_index(&i, index, 0, start, end));
cl_assert(git_iterator_ignore_case(i) == ignore_case);
cl_git_pass(git_iterator_current(i, &entry));
cl_git_pass(git_iterator_current(&entry, i));
for (count = 0; entry != NULL; ) {
++count;
cl_git_pass(git_iterator_advance(i, &entry));
cl_git_pass(git_iterator_advance(&entry, i));
}
cl_assert_equal_i(expected_count, count);
......
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = false
Unnamed repository; edit this file 'description' to name the repository.
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
0000000000000000000000000000000000000000 76d6e1d231b1085fcce151427e9899335de74be6 Russell Belfer <rb@github.com> 1359157123 -0800 commit (initial): initial commit
0000000000000000000000000000000000000000 76d6e1d231b1085fcce151427e9899335de74be6 Russell Belfer <rb@github.com> 1359157123 -0800 commit (initial): initial commit
76d6e1d231b1085fcce151427e9899335de74be6
......@@ -274,6 +274,7 @@ void test_status_worktree__issue_592(void)
repo = cl_git_sandbox_init("issue_592");
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "l.txt"));
cl_git_pass(p_unlink(git_buf_cstr(&path)));
cl_assert(!git_path_exists("issue_592/l.txt"));
cl_git_pass(git_status_foreach(repo, cb_status__check_592, "l.txt"));
......@@ -288,6 +289,7 @@ void test_status_worktree__issue_592_2(void)
repo = cl_git_sandbox_init("issue_592");
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c/a.txt"));
cl_git_pass(p_unlink(git_buf_cstr(&path)));
cl_assert(!git_path_exists("issue_592/c/a.txt"));
cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
......@@ -303,6 +305,7 @@ void test_status_worktree__issue_592_3(void)
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c"));
cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_exists("issue_592/c/a.txt"));
cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
......
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