From ed4f95e5d9f2f59dc5d5a4b76a696b0595b6175d Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 5 Mar 2013 11:47:07 -0800 Subject: [PATCH 01/11] Add const to some buffer functions --- src/buffer.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/buffer.h b/src/buffer.h index 6e73895b4..5402f3827 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -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 */ From 169dc61607b69726c6012ab70758692637928a85 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 5 Mar 2013 16:10:05 -0800 Subject: [PATCH 02/11] Make iterator APIs consistent with standards The iterator APIs are not currently consistent with the parameter ordering of the rest of the codebase. This rearranges the order of parameters, simplifies the naming of a number of functions, and makes somewhat better use of macros internally to clean up the iterator code. This also expands the test coverage of iterator functionality, making sure that case sensitive range-limited iteration works correctly. --- src/checkout.c | 39 +-- src/diff.c | 41 ++- src/index.c | 51 ---- src/index.h | 3 - src/iterator.c | 259 ++++++++++-------- src/iterator.h | 97 +++---- src/notes.c | 6 +- src/submodule.c | 12 +- tests-clar/diff/iterator.c | 74 ++--- tests-clar/repo/iterator.c | 210 ++++++++++++++ tests-clar/resources/icase/.gitted/HEAD | 1 + tests-clar/resources/icase/.gitted/config | 7 + .../resources/icase/.gitted/description | 1 + tests-clar/resources/icase/.gitted/index | Bin 0 -> 1392 bytes .../resources/icase/.gitted/info/exclude | 6 + tests-clar/resources/icase/.gitted/logs/HEAD | 1 + .../icase/.gitted/logs/refs/heads/master | 1 + .../3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce | Bin 0 -> 114 bytes .../4d/d6027d083575c7431396dc2a3174afeb393c93 | Bin 0 -> 61 bytes .../62/e0af52c199ec731fe4ad230041cd3286192d49 | Bin 0 -> 19 bytes .../76/d6e1d231b1085fcce151427e9899335de74be6 | 3 + .../d4/4e18fb93b7107b5cd1b95d601591d77869a1b6 | Bin 0 -> 21 bytes .../resources/icase/.gitted/refs/heads/master | 1 + tests-clar/resources/icase/B | 1 + tests-clar/resources/icase/D | 1 + tests-clar/resources/icase/F | 1 + tests-clar/resources/icase/H | 1 + tests-clar/resources/icase/J | 1 + tests-clar/resources/icase/L/1 | 1 + tests-clar/resources/icase/L/B | 1 + tests-clar/resources/icase/L/D | 1 + tests-clar/resources/icase/L/a | 1 + tests-clar/resources/icase/L/c | 1 + tests-clar/resources/icase/a | 1 + tests-clar/resources/icase/c | 1 + tests-clar/resources/icase/e | 1 + tests-clar/resources/icase/g | 1 + tests-clar/resources/icase/i | 1 + tests-clar/resources/icase/k/1 | 1 + tests-clar/resources/icase/k/B | 1 + tests-clar/resources/icase/k/D | 1 + tests-clar/resources/icase/k/a | 1 + tests-clar/resources/icase/k/c | 1 + tests-clar/status/worktree.c | 3 + 44 files changed, 525 insertions(+), 311 deletions(-) create mode 100644 tests-clar/repo/iterator.c create mode 100644 tests-clar/resources/icase/.gitted/HEAD create mode 100644 tests-clar/resources/icase/.gitted/config create mode 100644 tests-clar/resources/icase/.gitted/description create mode 100644 tests-clar/resources/icase/.gitted/index create mode 100644 tests-clar/resources/icase/.gitted/info/exclude create mode 100644 tests-clar/resources/icase/.gitted/logs/HEAD create mode 100644 tests-clar/resources/icase/.gitted/logs/refs/heads/master create mode 100644 tests-clar/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce create mode 100644 tests-clar/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93 create mode 100644 tests-clar/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49 create mode 100644 tests-clar/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6 create mode 100644 tests-clar/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6 create mode 100644 tests-clar/resources/icase/.gitted/refs/heads/master create mode 100644 tests-clar/resources/icase/B create mode 100644 tests-clar/resources/icase/D create mode 100644 tests-clar/resources/icase/F create mode 100644 tests-clar/resources/icase/H create mode 100644 tests-clar/resources/icase/J create mode 100644 tests-clar/resources/icase/L/1 create mode 100644 tests-clar/resources/icase/L/B create mode 100644 tests-clar/resources/icase/L/D create mode 100644 tests-clar/resources/icase/L/a create mode 100644 tests-clar/resources/icase/L/c create mode 100644 tests-clar/resources/icase/a create mode 100644 tests-clar/resources/icase/c create mode 100644 tests-clar/resources/icase/e create mode 100644 tests-clar/resources/icase/g create mode 100644 tests-clar/resources/icase/i create mode 100644 tests-clar/resources/icase/k/1 create mode 100644 tests-clar/resources/icase/k/B create mode 100644 tests-clar/resources/icase/k/D create mode 100644 tests-clar/resources/icase/k/a create mode 100644 tests-clar/resources/icase/k/c diff --git a/src/checkout.c b/src/checkout.c index 19ac913d3..0be87b4a3 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -456,7 +456,7 @@ static int checkout_action( while (1) { if (!wd) return checkout_action_no_wd(data, delta); - + cmp = strcomp(wd->path, delta->old_file.path); /* 1. wd before delta ("a/a" before "a/b") @@ -473,9 +473,9 @@ 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; continue; } @@ -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,16 +1134,17 @@ 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 */ if ((error = git_repository_index(&data->index, data->repo)) < 0 || (error = git_index_read(data->index)) < 0) goto cleanup; - + /* clear the REUC when doing a tree or commit checkout */ git_index_reuc_clear(data->index); } @@ -1241,9 +1242,9 @@ 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( + (error = git_iterator_for_workdir( &workdir, data.repo, iterflags, data.pfx, data.pfx)) < 0 || - (error = git_iterator_for_tree_range( + (error = git_iterator_for_tree( &baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0) goto cleanup; @@ -1321,7 +1322,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 +1349,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 +1370,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); diff --git a/src/diff.c b/src/diff.c index 0861b13eb..c0f8ee689 100644 --- a/src/diff.c +++ b/src/diff.c @@ -630,8 +630,8 @@ int git_diff__from_iterators( 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 */ @@ -663,12 +663,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; } @@ -696,7 +696,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; @@ -710,7 +710,7 @@ 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) + if (git_iterator_advance_into(&nitem, new_iter) < 0) goto fail; continue; @@ -733,7 +733,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 */ } @@ -762,7 +762,7 @@ int git_diff__from_iterators( } } - if (git_iterator_advance(new_iter, &nitem) < 0) + if (git_iterator_advance(&nitem, new_iter) < 0) goto fail; } @@ -772,11 +772,10 @@ 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) - goto fail; + 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; } } @@ -814,8 +813,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; @@ -836,8 +835,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; @@ -857,8 +856,8 @@ 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, 0, pfx, pfx) ); return error; @@ -876,8 +875,8 @@ 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, 0, pfx, pfx) ); return error; diff --git a/src/index.c b/src/index.c index 4deafd77f..1ca3b16b2 100644 --- a/src/index.c +++ b/src/index.c @@ -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 -} diff --git a/src/index.h b/src/index.h index 2beaa6375..9498907b6 100644 --- a/src/index.h +++ b/src/index.h @@ -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 diff --git a/src/iterator.c b/src/iterator.c index 8ad639d6b..2832e4ac2 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -14,13 +14,16 @@ #define ITERATOR_SET_CB(P,NAME_LC) do { \ (P)->cb.current = NAME_LC ## _iterator__current; \ - (P)->cb.at_end = NAME_LC ## _iterator__at_end; \ (P)->cb.advance = NAME_LC ## _iterator__advance; \ (P)->cb.seek = NAME_LC ## _iterator__seek; \ (P)->cb.reset = NAME_LC ## _iterator__reset; \ + (P)->cb.at_end = NAME_LC ## _iterator__at_end; \ (P)->cb.free = NAME_LC ## _iterator__free; \ } while (0) +#define ITERATOR_CASE_FLAGS \ + (GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE) + #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \ (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \ GITERR_CHECK_ALLOC(P); \ @@ -32,8 +35,17 @@ if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \ git__free(P); return -1; } \ (P)->base.prefixcomp = git__prefixcmp; \ + (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \ } while (0) +#define iterator__flag(I,F) ((((git_iterator *)(I))->flags & (F)) != 0) +#define iterator__ignore_case(I) iterator__flag(I,GIT_ITERATOR_IGNORE_CASE) + +#define iterator__end(I) ((git_iterator *)(I))->end +#define iterator__past_end(I,PATH) \ + (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0) + + static int iterator__reset_range( git_iterator *iter, const char *start, const char *end) { @@ -82,18 +94,13 @@ static int iterator_update_ignore_case( return error; } -static int empty_iterator__no_item( - git_iterator *iter, const git_index_entry **entry) -{ - GIT_UNUSED(iter); - *entry = NULL; - return 0; -} -static int empty_iterator__at_end(git_iterator *iter) +static int empty_iterator__noop( + const git_index_entry **entry, git_iterator *iter) { GIT_UNUSED(iter); - return 1; + if (entry) *entry = NULL; + return 0; } static int empty_iterator__reset( @@ -109,6 +116,12 @@ static int empty_iterator__seek(git_iterator *iter, const char *prefix) return -1; } +static int empty_iterator__at_end(git_iterator *iter) +{ + GIT_UNUSED(iter); + return 1; +} + static void empty_iterator__free(git_iterator *iter) { GIT_UNUSED(iter); @@ -119,20 +132,22 @@ typedef struct { git_iterator_callbacks cb; } empty_iterator; -int git_iterator_for_nothing(git_iterator **iter, git_iterator_flag_t flags) +int git_iterator_for_nothing( + git_iterator **iter, + git_iterator_flag_t flags, + const char *start, + const char *end) { empty_iterator *i = git__calloc(1, sizeof(empty_iterator)); GITERR_CHECK_ALLOC(i); - i->base.type = GIT_ITERATOR_TYPE_EMPTY; - i->base.cb = &i->cb; - i->base.flags = flags; - i->cb.current = empty_iterator__no_item; - i->cb.at_end = empty_iterator__at_end; - i->cb.advance = empty_iterator__no_item; - i->cb.seek = empty_iterator__seek; - i->cb.reset = empty_iterator__reset; - i->cb.free = empty_iterator__free; +#define empty_iterator__current empty_iterator__noop +#define empty_iterator__advance empty_iterator__noop + + ITERATOR_BASE_INIT(i, empty, EMPTY); + + if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0) + i->base.flags |= GIT_ITERATOR_IGNORE_CASE; *iter = (git_iterator *)i; @@ -144,9 +159,10 @@ typedef struct tree_iterator_frame tree_iterator_frame; struct tree_iterator_frame { tree_iterator_frame *next, *prev; git_tree *tree; - char *start; + const char *start; size_t startlen; size_t index; + /* secondary tree index for case-insensitive sort */ void **icase_map; void *icase_data[GIT_FLEX_ARRAY]; }; @@ -163,12 +179,15 @@ typedef struct { GIT_INLINE(const git_tree_entry *)tree_iterator__tree_entry(tree_iterator *ti) { tree_iterator_frame *tf = ti->stack; + size_t entries = git_tree_entrycount(tf->tree), idx = tf->index; - if (tf->index >= git_tree_entrycount(tf->tree)) + if (idx >= entries) return NULL; - return git_tree_entry_byindex( - tf->tree, tf->icase_map ? (size_t)tf->icase_map[tf->index] : tf->index); + if (tf->icase_map) + idx = (size_t)tf->icase_map[idx]; + + return git_tree_entry_byindex(tf->tree, idx); } static char *tree_iterator__current_filename( @@ -218,7 +237,7 @@ static int tree_iterator__to_end(tree_iterator *ti) } static int tree_iterator__current( - git_iterator *self, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *self) { tree_iterator *ti = (tree_iterator *)self; const git_tree_entry *te = tree_iterator__tree_entry(ti); @@ -236,7 +255,7 @@ static int tree_iterator__current( if (ti->entry.path == NULL) return -1; - if (ti->base.end && ti->base.prefixcomp(ti->entry.path, ti->base.end) > 0) + if (iterator__past_end(ti, ti->entry.path)) return tree_iterator__to_end(ti); if (entry) @@ -290,26 +309,35 @@ static void tree_iterator__frame_seek_start(tree_iterator_frame *tf) } } -static tree_iterator_frame *tree_iterator__alloc_frame( - tree_iterator *ti, git_tree *tree, char *start) +static int tree_iterator__push_frame( + tree_iterator *ti, git_tree *tree, const char *start) { size_t i, max_i = git_tree_entrycount(tree); tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame) + max_i * sizeof(void *)); - if (!tf) - return NULL; + GITERR_CHECK_ALLOC(tf); - tf->tree = tree; + tf->tree = tree; + + tf->next = ti->stack; + ti->stack = tf; + if (tf->next) + tf->next->prev = tf; + else + ti->tail = tf; if (start && *start) { - tf->start = start; + tf->start = start; tf->startlen = strlen(start); } - if (!max_i) - return tf; + ti->path_has_filename = false; - if ((ti->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0) { + if (!max_i) + return 0; + + /* build secondary index if iterator is case-insensitive */ + if (iterator__ignore_case(ti)) { tf->icase_map = tf->icase_data; for (i = 0; i < max_i; ++i) @@ -321,7 +349,7 @@ static tree_iterator_frame *tree_iterator__alloc_frame( tree_iterator__frame_seek_start(tf); - return tf; + return 0; } static int tree_iterator__expand_tree(tree_iterator *ti) @@ -329,16 +357,13 @@ static int tree_iterator__expand_tree(tree_iterator *ti) int error; git_tree *subtree; const git_tree_entry *te = tree_iterator__tree_entry(ti); - tree_iterator_frame *tf; - char *relpath; + const char *relpath; while (te != NULL && git_tree_entry__is_tree(te)) { - if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0) - return -1; + relpath = tree_iterator__current_filename(ti, te); /* check that we have not passed the range end */ - if (ti->base.end != NULL && - ti->base.prefixcomp(ti->path.ptr, ti->base.end) > 0) + if (iterator__past_end(ti, relpath)) return tree_iterator__to_end(ti); if ((error = git_tree_lookup(&subtree, ti->base.repo, &te->oid)) < 0) @@ -354,12 +379,8 @@ static int tree_iterator__expand_tree(tree_iterator *ti) relpath = ti->stack->start + te->filename_len + 1; } - if ((tf = tree_iterator__alloc_frame(ti, subtree, relpath)) == NULL) - return -1; - - tf->next = ti->stack; - ti->stack = tf; - tf->next->prev = tf; + if ((error = tree_iterator__push_frame(ti, subtree, relpath)) < 0) + return error; te = tree_iterator__tree_entry(ti); } @@ -368,9 +389,8 @@ static int tree_iterator__expand_tree(tree_iterator *ti) } static int tree_iterator__advance( - git_iterator *self, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *self) { - int error = 0; tree_iterator *ti = (tree_iterator *)self; const git_tree_entry *te = NULL; @@ -394,13 +414,13 @@ static int tree_iterator__advance( git_buf_rtruncate_at_char(&ti->path, '/'); } - if (te && git_tree_entry__is_tree(te)) - error = tree_iterator__expand_tree(ti); + if (te && git_tree_entry__is_tree(te)) { + int error = tree_iterator__expand_tree(ti); + if (error < 0) + return error; + } - if (!error) - error = tree_iterator__current(self, entry); - - return error; + return tree_iterator__current(entry, self); } static int tree_iterator__seek(git_iterator *self, const char *prefix) @@ -444,7 +464,7 @@ static int tree_iterator__reset( return tree_iterator__expand_tree(ti); } -int git_iterator_for_tree_range( +int git_iterator_for_tree( git_iterator **iter, git_tree *tree, git_iterator_flag_t flags, @@ -455,7 +475,7 @@ int git_iterator_for_tree_range( tree_iterator *ti; if (tree == NULL) - return git_iterator_for_nothing(iter, flags); + return git_iterator_for_nothing(iter, flags, start, end); if ((error = git_tree__dup(&tree, tree)) < 0) return error; @@ -464,11 +484,10 @@ int git_iterator_for_tree_range( 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 || + (error = tree_iterator__push_frame(ti, tree, ti->base.start)) < 0) goto fail; - ti->stack = ti->tail = tree_iterator__alloc_frame(ti, tree, ti->base.start); - if ((error = tree_iterator__expand_tree(ti)) < 0) goto fail; @@ -489,7 +508,7 @@ typedef struct { } index_iterator; static int index_iterator__current( - git_iterator *self, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *self) { index_iterator *ii = (index_iterator *)self; const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); @@ -506,18 +525,15 @@ static int index_iterator__at_end(git_iterator *self) return (ii->current >= git_index_entrycount(ii->index)); } -static void index_iterator__skip_conflicts( - index_iterator *ii) +static void index_iterator__skip_conflicts(index_iterator *ii) { size_t entrycount = git_index_entrycount(ii->index); - const git_index_entry *ie; + const git_index_entry *ie = NULL; while (ii->current < entrycount) { ie = git_index_get_byindex(ii->index, ii->current); - if (ie == NULL || - (ii->base.end != NULL && - ii->base.prefixcomp(ie->path, ii->base.end) > 0)) { + if (ie != NULL && iterator__past_end(ii, ie->path)) { ii->current = entrycount; break; } @@ -530,7 +546,7 @@ static void index_iterator__skip_conflicts( } static int index_iterator__advance( - git_iterator *self, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *self) { index_iterator *ii = (index_iterator *)self; @@ -539,7 +555,7 @@ static int index_iterator__advance( index_iterator__skip_conflicts(ii); - return index_iterator__current(self, entry); + return index_iterator__current(entry, self); } static int index_iterator__seek(git_iterator *self, const char *prefix) @@ -554,11 +570,15 @@ static int index_iterator__reset( git_iterator *self, const char *start, const char *end) { index_iterator *ii = (index_iterator *)self; + if (iterator__reset_range(self, start, end) < 0) return -1; + ii->current = ii->base.start ? git_index__prefix_position(ii->index, ii->base.start) : 0; + index_iterator__skip_conflicts(ii); + return 0; } @@ -569,7 +589,7 @@ static void index_iterator__free(git_iterator *self) ii->index = NULL; } -int git_iterator_for_index_range( +int git_iterator_for_index( git_iterator **iter, git_index *index, git_iterator_flag_t flags, @@ -583,10 +603,12 @@ int git_iterator_for_index_range( ITERATOR_BASE_INIT(ii, index, INDEX); ii->base.repo = git_index_owner(index); + if (index->ignore_case) { ii->base.flags |= GIT_ITERATOR_IGNORE_CASE; ii->base.prefixcomp = git__prefixcmp_icase; } + ii->index = index; GIT_REFCOUNT_INC(index); @@ -643,7 +665,7 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame( { workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame)); git_vector_cmp entry_compare = CASESELECT( - (wi->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0, + iterator__ignore_case(wi), git_path_with_stat_cmp_icase, git_path_with_stat_cmp); if (wf == NULL) @@ -706,7 +728,7 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi) error = git_path_dirload_with_stat( wi->path.ptr, wi->root_len, - (wi->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0, + iterator__ignore_case(wi), wi->base.start, wi->base.end, &wf->entries); if (error < 0 || wf->entries.length == 0) { @@ -729,10 +751,11 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi) } static int workdir_iterator__current( - git_iterator *self, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *self) { workdir_iterator *wi = (workdir_iterator *)self; - *entry = (wi->entry.path == NULL) ? NULL : &wi->entry; + if (entry) + *entry = (wi->entry.path == NULL) ? NULL : &wi->entry; return 0; } @@ -742,7 +765,7 @@ static int workdir_iterator__at_end(git_iterator *self) } static int workdir_iterator__advance( - git_iterator *self, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *self) { int error; workdir_iterator *wi = (workdir_iterator *)self; @@ -781,7 +804,7 @@ static int workdir_iterator__advance( error = workdir_iterator__update_entry(wi); if (!error && entry != NULL) - error = workdir_iterator__current(self, entry); + error = workdir_iterator__current(entry, self); return error; } @@ -832,6 +855,7 @@ static void workdir_iterator__free(git_iterator *self) static int workdir_iterator__update_entry(workdir_iterator *wi) { + int error = 0; git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index); @@ -841,19 +865,18 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) if (!ps) return 0; + /* skip over .git entries */ + if (path_is_dotgit(ps)) + return workdir_iterator__advance(NULL, (git_iterator *)wi); + if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0) return -1; - if (wi->base.end && - wi->base.prefixcomp(wi->path.ptr + wi->root_len, wi->base.end) > 0) + if (iterator__past_end(wi, wi->path.ptr + wi->root_len)) return 0; wi->entry.path = ps->path; - /* skip over .git entries */ - if (path_is_dotgit(ps)) - return workdir_iterator__advance((git_iterator *)wi, NULL); - wi->is_ignored = -1; git_index_entry__init_from_stat(&wi->entry, &ps->st); @@ -867,26 +890,28 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) return 0; } - /* detect submodules */ - if (S_ISDIR(wi->entry.mode)) { - int res = git_submodule_lookup(NULL, wi->base.repo, wi->entry.path); - bool is_submodule = (res == 0); - if (res == GIT_ENOTFOUND) - giterr_clear(); + /* if this isn't a tree, then we're done */ + if (wi->entry.mode != GIT_FILEMODE_TREE) + return 0; - /* if submodule, mark as GITLINK and remove trailing slash */ - if (is_submodule) { - size_t len = strlen(wi->entry.path); - assert(wi->entry.path[len - 1] == '/'); - wi->entry.path[len - 1] = '\0'; - wi->entry.mode = S_IFGITLINK; - } + /* detect submodules */ + + error = git_submodule_lookup(NULL, wi->base.repo, wi->entry.path); + if (error == GIT_ENOTFOUND) + giterr_clear(); + + /* if submodule, mark as GITLINK and remove trailing slash */ + if (!error) { + size_t len = strlen(wi->entry.path); + assert(wi->entry.path[len - 1] == '/'); + wi->entry.path[len - 1] = '\0'; + wi->entry.mode = S_IFGITLINK; } return 0; } -int git_iterator_for_workdir_range( +int git_iterator_for_workdir( git_iterator **iter, git_repository *repo, git_iterator_flag_t flags, @@ -917,7 +942,7 @@ int git_iterator_for_workdir_range( } wi->root_len = wi->path.size; - wi->entrycmp = (wi->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0 ? + wi->entrycmp = iterator__ignore_case(wi) ? workdir_iterator__entry_cmp_icase : workdir_iterator__entry_cmp_case; if ((error = workdir_iterator__expand_dir(wi)) < 0) { @@ -949,7 +974,7 @@ typedef struct { } spoolandsort_callbacks; static int spoolandsort_iterator__current( - git_iterator *self, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *self) { spoolandsort_callbacks *scb = (spoolandsort_callbacks *)self->cb; @@ -967,7 +992,7 @@ static int spoolandsort_iterator__at_end(git_iterator *self) } static int spoolandsort_iterator__advance( - git_iterator *self, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *self) { spoolandsort_callbacks *scb = (spoolandsort_callbacks *)self->cb; @@ -1053,7 +1078,7 @@ int git_iterator_spoolandsort_push(git_iterator *iter, bool ignore_case) if (git_vector_init(&scb->entries, 16, entrycomp) < 0 || git_pool_init(&scb->entry_pool, sizeof(git_index_entry), 0) < 0 || git_pool_init(&scb->string_pool, 1, 0) < 0 || - git_iterator_current(iter, &item) < 0) + git_iterator_current(&item, iter) < 0) goto fail; while (item) { @@ -1072,7 +1097,7 @@ int git_iterator_spoolandsort_push(git_iterator *iter, bool ignore_case) if (git_vector_insert(&scb->entries, clone) < 0) goto fail; - if (git_iterator_advance(iter, &item) < 0) + if (git_iterator_advance(&item, iter) < 0) goto fail; } @@ -1105,7 +1130,7 @@ void git_iterator_free(git_iterator *iter) git__free(iter); } -git_index *git_iterator_index_get_index(git_iterator *iter) +git_index *git_iterator_get_index(git_iterator *iter) { if (iter->type == GIT_ITERATOR_TYPE_INDEX) return ((index_iterator *)iter)->index; @@ -1126,7 +1151,7 @@ git_iterator_type_t git_iterator_inner_type(git_iterator *iter) } int git_iterator_current_tree_entry( - git_iterator *iter, const git_tree_entry **tree_entry) + const git_tree_entry **tree_entry, git_iterator *iter) { *tree_entry = (iter->type != GIT_ITERATOR_TYPE_TREE) ? NULL : tree_iterator__tree_entry((tree_iterator *)iter); @@ -1134,9 +1159,9 @@ int git_iterator_current_tree_entry( } int git_iterator_current_parent_tree( + const git_tree **tree_ptr, git_iterator *iter, - const char *parent_path, - const git_tree **tree_ptr) + const char *parent_path) { tree_iterator *ti = (tree_iterator *)iter; tree_iterator_frame *tf; @@ -1177,24 +1202,24 @@ notfound: return 0; } -int git_iterator_current_is_ignored(git_iterator *iter) +bool git_iterator_current_is_ignored(git_iterator *iter) { workdir_iterator *wi = (workdir_iterator *)iter; if (iter->type != GIT_ITERATOR_TYPE_WORKDIR) - return 0; + return false; if (wi->is_ignored != -1) - return wi->is_ignored; + return (bool)(wi->is_ignored != 0); if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0) - wi->is_ignored = 1; + wi->is_ignored = true; - return wi->is_ignored; + return (bool)wi->is_ignored; } -int git_iterator_advance_into_directory( - git_iterator *iter, const git_index_entry **entry) +int git_iterator_advance_into( + const git_index_entry **entry, git_iterator *iter) { workdir_iterator *wi = (workdir_iterator *)iter; @@ -1205,10 +1230,10 @@ int git_iterator_advance_into_directory( { if (workdir_iterator__expand_dir(wi) < 0) /* if error loading or if empty, skip the directory. */ - return workdir_iterator__advance(iter, entry); + return workdir_iterator__advance(entry, iter); } - return entry ? git_iterator_current(iter, entry) : 0; + return entry ? git_iterator_current(entry, iter) : 0; } int git_iterator_cmp(git_iterator *iter, const char *path_prefix) @@ -1216,7 +1241,7 @@ int git_iterator_cmp(git_iterator *iter, const char *path_prefix) const git_index_entry *entry; /* a "done" iterator is after every prefix */ - if (git_iterator_current(iter, &entry) < 0 || + if (git_iterator_current(&entry, iter) < 0 || entry == NULL) return 1; @@ -1227,7 +1252,7 @@ int git_iterator_cmp(git_iterator *iter, const char *path_prefix) return iter->prefixcomp(entry->path, path_prefix); } -int git_iterator_current_workdir_path(git_iterator *iter, git_buf **path) +int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter) { workdir_iterator *wi = (workdir_iterator *)iter; diff --git a/src/iterator.h b/src/iterator.h index a9bccfca8..feb0c2271 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -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 { @@ -28,16 +23,18 @@ typedef enum { } 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), } 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 (*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,53 +49,41 @@ 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 @@ -109,29 +94,27 @@ 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); -} - -GIT_INLINE(int) git_iterator_at_end(git_iterator *iter) -{ - return iter->cb->at_end(iter); + return iter->cb->current(entry, iter); } GIT_INLINE(int) git_iterator_advance( - git_iterator *iter, const git_index_entry **entry) + const git_index_entry **entry, git_iterator *iter) { - return iter->cb->advance(iter, entry); + return iter->cb->advance(entry, iter); } GIT_INLINE(int) git_iterator_seek( @@ -146,6 +129,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; @@ -167,15 +155,15 @@ GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter) } extern int git_iterator_current_tree_entry( - git_iterator *iter, const git_tree_entry **tree_entry); + const git_tree_entry **tree_entry, 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_ptr, git_iterator *iter, const char *parent_path); -extern int git_iterator_current_is_ignored(git_iterator *iter); +extern bool git_iterator_current_is_ignored(git_iterator *iter); /** - * Iterate into a workdir directory. + * Iterate into a directory. * * Workdir iterators do not automatically descend into directories (so that * when comparing two iterator entries you can detect a newly created @@ -191,21 +179,22 @@ extern int git_iterator_current_is_ignored(git_iterator *iter); * 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 int git_iterator_advance_into( + const git_index_entry **entry, 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); + git_buf **path, git_iterator *iter); - -extern git_index *git_iterator_index_get_index(git_iterator *iter); +/* Return index pointer if index iterator, else NULL */ +extern git_index *git_iterator_get_index(git_iterator *iter); extern git_iterator_type_t git_iterator_inner_type(git_iterator *iter); diff --git a/src/notes.c b/src/notes.c index a1a47d989..ef48ac88e 100644 --- a/src/notes.c +++ b/src/notes.c @@ -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; } diff --git a/src/submodule.c b/src/submodule.c index 359306498..c02061376 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -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); diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index efdadbf1f..546f68abe 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -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,14 @@ 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, 0, 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 +559,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); @@ -736,8 +736,8 @@ void test_diff_iterator__workdir_builtin_ignores(void) 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)); + git_iterator_for_workdir(&i, repo, 0, "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 +746,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 +764,17 @@ 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)); + 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] == NULL); @@ -817,16 +817,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 +882,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); diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c new file mode 100644 index 000000000..9a3815a03 --- /dev/null +++ b/tests-clar/repo/iterator.c @@ -0,0 +1,210 @@ +#include "clar_libgit2.h" +#include "iterator.h" +#include "repository.h" + +static git_repository *g_repo; + +void test_repo_iterator__initialize(void) +{ + g_repo = cl_git_sandbox_init("icase"); +} + +void test_repo_iterator__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +static void expect_iterator_items( + git_iterator *i, int expected_flat, int expected_total) +{ + const git_index_entry *entry; + int count; + + count = 0; + cl_git_pass(git_iterator_current(&entry, i)); + + while (entry != NULL) { + count++; + + cl_git_pass(git_iterator_advance(&entry, i)); + + if (count > expected_flat) + break; + } + + cl_assert_equal_i(expected_flat, count); + + cl_git_pass(git_iterator_reset(i, NULL, NULL)); + + count = 0; + cl_git_pass(git_iterator_current(&entry, i)); + + while (entry != NULL) { + count++; + + if (entry->mode == GIT_FILEMODE_TREE) + cl_git_pass(git_iterator_advance_into(&entry, i)); + else + cl_git_pass(git_iterator_advance(&entry, i)); + + if (count > expected_total) + break; + } + + cl_assert_equal_i(expected_total, count); +} + +/* Index contents (including pseudotrees): + * + * 0: a 5: F 10: k/ 16: L/ + * 1: B 6: g 11: k/1 17: L/1 + * 2: c 7: H 12: k/a 18: L/a + * 3: D 8: i 13: k/B 19: L/B + * 4: e 9: J 14: k/c 20: L/c + * 15: k/D 21: L/D + * + * 0: B 5: L/ 11: a 16: k/ + * 1: D 6: L/1 12: c 17: k/1 + * 2: F 7: L/B 13: e 18: k/B + * 3: H 8: L/D 14: g 19: k/D + * 4: J 9: L/a 15: i 20: k/a + * 10: L/c 21: k/c + */ + +void test_repo_iterator__index(void) +{ + git_iterator *i; + git_index *index; + + cl_git_pass(git_repository_index(&index, g_repo)); + + /* normal index iteration */ + cl_git_pass(git_iterator_for_index(&i, index, 0, NULL, NULL)); + expect_iterator_items(i, 20, 20); + git_iterator_free(i); + + git_index_free(index); +} + +void test_repo_iterator__index_icase(void) +{ + git_iterator *i; + git_index *index; + unsigned int caps; + + cl_git_pass(git_repository_index(&index, g_repo)); + caps = git_index_caps(index); + + /* force case sensitivity */ + cl_git_pass(git_index_set_caps(index, caps & ~GIT_INDEXCAP_IGNORE_CASE)); + + /* normal index iteration with range */ + cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D")); + expect_iterator_items(i, 7, 7); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z")); + expect_iterator_items(i, 3, 3); + git_iterator_free(i); + + /* force case insensitivity */ + cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE)); + + /* normal index iteration with range */ + cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D")); + expect_iterator_items(i, 13, 13); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z")); + expect_iterator_items(i, 5, 5); + git_iterator_free(i); + + cl_git_pass(git_index_set_caps(index, caps)); + git_index_free(index); +} + +void test_repo_iterator__tree(void) +{ + git_iterator *i; + git_tree *head; + + cl_git_pass(git_repository_head_tree(&head, g_repo)); + + /* normal tree iteration */ + cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL)); + expect_iterator_items(i, 20, 20); + git_iterator_free(i); + + git_tree_free(head); +} + +void test_repo_iterator__tree_icase(void) +{ + git_iterator *i; + git_tree *head; + git_iterator_flag_t flag; + + cl_git_pass(git_repository_head_tree(&head, g_repo)); + + flag = GIT_ITERATOR_DONT_IGNORE_CASE; + + /* normal tree iteration with range */ + cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D")); + expect_iterator_items(i, 7, 7); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z")); + expect_iterator_items(i, 3, 3); + git_iterator_free(i); + + flag = GIT_ITERATOR_IGNORE_CASE; + + /* normal tree iteration with range */ + cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D")); + expect_iterator_items(i, 13, 13); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z")); + expect_iterator_items(i, 5, 5); + git_iterator_free(i); +} + +void test_repo_iterator__workdir(void) +{ + git_iterator *i; + + /* normal workdir iteration uses explicit tree expansion */ + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, 0, NULL, NULL)); + expect_iterator_items(i, 12, 22); + git_iterator_free(i); +} + +void test_repo_iterator__workdir_icase(void) +{ + git_iterator *i; + git_iterator_flag_t flag; + + flag = GIT_ITERATOR_DONT_IGNORE_CASE; + + /* normal workdir iteration with range */ + cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D")); + expect_iterator_items(i, 5, 8); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z")); + expect_iterator_items(i, 1, 4); + git_iterator_free(i); + + flag = GIT_ITERATOR_IGNORE_CASE; + + /* normal workdir iteration with range */ + cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D")); + expect_iterator_items(i, 9, 14); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z")); + expect_iterator_items(i, 1, 6); + git_iterator_free(i); +} diff --git a/tests-clar/resources/icase/.gitted/HEAD b/tests-clar/resources/icase/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/tests-clar/resources/icase/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests-clar/resources/icase/.gitted/config b/tests-clar/resources/icase/.gitted/config new file mode 100644 index 000000000..bb4d11c1f --- /dev/null +++ b/tests-clar/resources/icase/.gitted/config @@ -0,0 +1,7 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = false diff --git a/tests-clar/resources/icase/.gitted/description b/tests-clar/resources/icase/.gitted/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/tests-clar/resources/icase/.gitted/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/tests-clar/resources/icase/.gitted/index b/tests-clar/resources/icase/.gitted/index new file mode 100644 index 0000000000000000000000000000000000000000..f8288ec137c4bdcb5c92955d588688424ed7b8a9 GIT binary patch literal 1392 zcmZ?q402{*U|<4bkw9i~79h<4qxl#ZSeQ7s++bj6T*AP>_!XoEh}o|AN&KF?U7$MV z;?CFv(TUe9G8b-RU~~fMV}t4k(@_0)iPP@_($5Li52m5|?-Qrr4Wyq3svk^4^*Wn#^7f*6C#993e?alvnn60zna;x|WySaXsQ<}iVnz;FW7(0ICrW)6;Y znh4Uz3e^v$q55wUr#~5_p988NOhfhGBTj!RNIw@;KbVH 1359157123 -0800 commit (initial): initial commit diff --git a/tests-clar/resources/icase/.gitted/logs/refs/heads/master b/tests-clar/resources/icase/.gitted/logs/refs/heads/master new file mode 100644 index 000000000..3b16bd163 --- /dev/null +++ b/tests-clar/resources/icase/.gitted/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 76d6e1d231b1085fcce151427e9899335de74be6 Russell Belfer 1359157123 -0800 commit (initial): initial commit diff --git a/tests-clar/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce b/tests-clar/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce new file mode 100644 index 0000000000000000000000000000000000000000..10691c788ef81beafc66d471e9968e9580300bbb GIT binary patch literal 114 zcmV-&0FD260V^p=O;s>9F=H?^FfcPQQE+0o;wSNY@^*phn2S4O6GSIougF}u4Jzq^ zPtpyaqz67pFRYR#20)G&iwu}VT5n~l}60DF**HrlT7F=a3`FfcPQQ7~jkdayp|;LJD0@=w+(GdP|#YLnFUgi1Q$ TlXSr+nTSs^8LK1!g`+qnv|Si2 literal 0 HcmV?d00001 diff --git a/tests-clar/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49 b/tests-clar/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49 new file mode 100644 index 0000000000000000000000000000000000000000..e264aeab3bf5d3810397302ce55ba3f006b5484a GIT binary patch literal 19 acmbmd_^oD+*408P6E_5c6? literal 0 HcmV?d00001 diff --git a/tests-clar/resources/icase/.gitted/refs/heads/master b/tests-clar/resources/icase/.gitted/refs/heads/master new file mode 100644 index 000000000..37410ec2a --- /dev/null +++ b/tests-clar/resources/icase/.gitted/refs/heads/master @@ -0,0 +1 @@ +76d6e1d231b1085fcce151427e9899335de74be6 diff --git a/tests-clar/resources/icase/B b/tests-clar/resources/icase/B new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/B @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/D b/tests-clar/resources/icase/D new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/D @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/F b/tests-clar/resources/icase/F new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/F @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/H b/tests-clar/resources/icase/H new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/H @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/J b/tests-clar/resources/icase/J new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/J @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/L/1 b/tests-clar/resources/icase/L/1 new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/L/1 @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/L/B b/tests-clar/resources/icase/L/B new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/L/B @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/L/D b/tests-clar/resources/icase/L/D new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/L/D @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/L/a b/tests-clar/resources/icase/L/a new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/L/a @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/L/c b/tests-clar/resources/icase/L/c new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/L/c @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/a b/tests-clar/resources/icase/a new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/a @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/c b/tests-clar/resources/icase/c new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/c @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/e b/tests-clar/resources/icase/e new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/e @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/g b/tests-clar/resources/icase/g new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/g @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/i b/tests-clar/resources/icase/i new file mode 100644 index 000000000..d44e18fb9 --- /dev/null +++ b/tests-clar/resources/icase/i @@ -0,0 +1 @@ +start diff --git a/tests-clar/resources/icase/k/1 b/tests-clar/resources/icase/k/1 new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/k/1 @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/k/B b/tests-clar/resources/icase/k/B new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/k/B @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/k/D b/tests-clar/resources/icase/k/D new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/k/D @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/k/a b/tests-clar/resources/icase/k/a new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/k/a @@ -0,0 +1 @@ +sub diff --git a/tests-clar/resources/icase/k/c b/tests-clar/resources/icase/k/c new file mode 100644 index 000000000..62e0af52c --- /dev/null +++ b/tests-clar/resources/icase/k/c @@ -0,0 +1 @@ +sub diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index e1fc8dff8..b5449f6f1 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -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")); From cc216a01ee512a41320056efc9b588daf9129f7a Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 5 Mar 2013 16:29:04 -0800 Subject: [PATCH 03/11] Retire spoolandsort iterator Since the case sensitivity is moved into the respective iterators, this removes the spoolandsort iterator code. --- src/checkout.c | 6 +- src/diff.c | 9 +-- src/iterator.c | 189 ++++++------------------------------------------- src/iterator.h | 13 +--- 4 files changed, 27 insertions(+), 190 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 0be87b4a3..41de0d7d4 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1248,10 +1248,8 @@ int git_checkout_iterator( &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. diff --git a/src/diff.c b/src/diff.c index c0f8ee689..766361938 100644 --- a/src/diff.c +++ b/src/diff.c @@ -620,13 +620,8 @@ 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; } diff --git a/src/iterator.c b/src/iterator.c index 2832e4ac2..e28f20e12 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -960,161 +960,6 @@ fail: } -typedef struct { - /* replacement callbacks */ - git_iterator_callbacks cb; - /* original iterator values */ - git_iterator_callbacks *orig; - git_iterator_type_t orig_type; - /* spoolandsort data */ - git_vector entries; - git_pool entry_pool; - git_pool string_pool; - size_t position; -} spoolandsort_callbacks; - -static int spoolandsort_iterator__current( - const git_index_entry **entry, git_iterator *self) -{ - spoolandsort_callbacks *scb = (spoolandsort_callbacks *)self->cb; - - *entry = (const git_index_entry *) - git_vector_get(&scb->entries, scb->position); - - return 0; -} - -static int spoolandsort_iterator__at_end(git_iterator *self) -{ - spoolandsort_callbacks *scb = (spoolandsort_callbacks *)self->cb; - - return 0 == scb->entries.length || scb->entries.length - 1 <= scb->position; -} - -static int spoolandsort_iterator__advance( - const git_index_entry **entry, git_iterator *self) -{ - spoolandsort_callbacks *scb = (spoolandsort_callbacks *)self->cb; - - *entry = (const git_index_entry *) - git_vector_get(&scb->entries, ++scb->position); - - return 0; -} - -static int spoolandsort_iterator__seek(git_iterator *self, const char *prefix) -{ - GIT_UNUSED(self); - GIT_UNUSED(prefix); - - return -1; -} - -static int spoolandsort_iterator__reset( - git_iterator *self, const char *start, const char *end) -{ - spoolandsort_callbacks *scb = (spoolandsort_callbacks *)self->cb; - - GIT_UNUSED(start); GIT_UNUSED(end); - - scb->position = 0; - - return 0; -} - -static void spoolandsort_iterator__free_callbacks(spoolandsort_callbacks *scb) -{ - git_pool_clear(&scb->string_pool); - git_pool_clear(&scb->entry_pool); - git_vector_free(&scb->entries); - git__free(scb); -} - -void git_iterator_spoolandsort_pop(git_iterator *self) -{ - spoolandsort_callbacks *scb = (spoolandsort_callbacks *)self->cb; - - if (self->type != GIT_ITERATOR_TYPE_SPOOLANDSORT) - return; - - self->cb = scb->orig; - self->type = scb->orig_type; - self->flags ^= GIT_ITERATOR_IGNORE_CASE; - - spoolandsort_iterator__free_callbacks(scb); -} - -static void spoolandsort_iterator__free(git_iterator *self) -{ - git_iterator_spoolandsort_pop(self); - self->cb->free(self); -} - -int git_iterator_spoolandsort_push(git_iterator *iter, bool ignore_case) -{ - const git_index_entry *item; - spoolandsort_callbacks *scb; - int (*entrycomp)(const void *a, const void *b); - - if (((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) == (ignore_case != 0)) - return 0; - - if (iter->type == GIT_ITERATOR_TYPE_EMPTY) { - iter->flags = (iter->flags ^ GIT_ITERATOR_IGNORE_CASE); - return 0; - } - - scb = git__calloc(1, sizeof(spoolandsort_callbacks)); - GITERR_CHECK_ALLOC(scb); - - ITERATOR_SET_CB(scb,spoolandsort); - - scb->orig = iter->cb; - scb->orig_type = iter->type; - scb->position = 0; - - entrycomp = ignore_case ? git_index_entry__cmp_icase : git_index_entry__cmp; - - if (git_vector_init(&scb->entries, 16, entrycomp) < 0 || - git_pool_init(&scb->entry_pool, sizeof(git_index_entry), 0) < 0 || - git_pool_init(&scb->string_pool, 1, 0) < 0 || - git_iterator_current(&item, iter) < 0) - goto fail; - - while (item) { - git_index_entry *clone = git_pool_malloc(&scb->entry_pool, 1); - if (!clone) - goto fail; - - memcpy(clone, item, sizeof(git_index_entry)); - - if (item->path) { - clone->path = git_pool_strdup(&scb->string_pool, item->path); - if (!clone->path) - goto fail; - } - - if (git_vector_insert(&scb->entries, clone) < 0) - goto fail; - - if (git_iterator_advance(&item, iter) < 0) - goto fail; - } - - git_vector_sort(&scb->entries); - - iter->cb = (git_iterator_callbacks *)scb; - iter->type = GIT_ITERATOR_TYPE_SPOOLANDSORT; - iter->flags ^= GIT_ITERATOR_IGNORE_CASE; - - return 0; - -fail: - spoolandsort_iterator__free_callbacks(scb); - return -1; -} - - void git_iterator_free(git_iterator *iter) { if (iter == NULL) @@ -1130,26 +975,35 @@ void git_iterator_free(git_iterator *iter) git__free(iter); } +int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case) +{ + bool desire_ignore_case = (ignore_case != 0); + + if (iterator__ignore_case(iter) == desire_ignore_case) + return 0; + + if (iter->type == GIT_ITERATOR_TYPE_EMPTY) { + if (desire_ignore_case) + iter->flags |= GIT_ITERATOR_IGNORE_CASE; + else + iter->flags &= ~GIT_ITERATOR_IGNORE_CASE; + } else { + giterr_set(GITERR_INVALID, + "Cannot currently set ignore case on non-empty iterators"); + return -1; + } + + return 0; +} + git_index *git_iterator_get_index(git_iterator *iter) { if (iter->type == GIT_ITERATOR_TYPE_INDEX) return ((index_iterator *)iter)->index; - if (iter->type == GIT_ITERATOR_TYPE_SPOOLANDSORT && - ((spoolandsort_callbacks *)iter->cb)->orig_type == GIT_ITERATOR_TYPE_INDEX) - return ((index_iterator *)iter)->index; - return NULL; } -git_iterator_type_t git_iterator_inner_type(git_iterator *iter) -{ - if (iter->type == GIT_ITERATOR_TYPE_SPOOLANDSORT) - return ((spoolandsort_callbacks *)iter->cb)->orig_type; - - return iter->type; -} - int git_iterator_current_tree_entry( const git_tree_entry **tree_entry, git_iterator *iter) { @@ -1263,4 +1117,3 @@ int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter) return 0; } - diff --git a/src/iterator.h b/src/iterator.h index feb0c2271..24c7b7765 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -19,7 +19,6 @@ 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 { @@ -86,14 +85,6 @@ extern int git_iterator_for_workdir( 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); - /* Return a git_index_entry structure for the current value the iterator * is looking at or NULL if the iterator is at the end. * @@ -154,6 +145,8 @@ 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( const git_tree_entry **tree_entry, git_iterator *iter); @@ -196,6 +189,4 @@ extern int git_iterator_current_workdir_path( /* Return index pointer if index iterator, else NULL */ extern git_index *git_iterator_get_index(git_iterator *iter); -extern git_iterator_type_t git_iterator_inner_type(git_iterator *iter); - #endif From 9bea03ce776ed864b0556815d94d71d300ac1da3 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 6 Mar 2013 15:16:34 -0800 Subject: [PATCH 04/11] Add INCLUDE_TREES, DONT_AUTOEXPAND iterator flags This standardizes iterator behavior across all three iterators (index, tree, and working directory). Previously the working directory iterator behaved differently from the other two. Each iterator can now operate in one of three modes: 1. *No tree results, auto expand trees* means that only non- tree items will be returned and when a tree/directory is encountered, we will automatically descend into it. 2. *Tree results, auto expand trees* means that results will be given for every item found, including trees, but you only need to call normal git_iterator_advance to yield every item (i.e. trees returned with pre-order iteration). 3. *Tree results, no auto expand* means that calling the normal git_iterator_advance when looking at a tree will not descend into the tree, but will skip over it to the next entry in the parent. Previously, behavior 1 was the only option for index and tree iterators, and behavior 3 was the only option for workdir. The main public API implications of this are that the `git_iterator_advance_into()` call is now valid for all iterators, not just working directory iterators, and all the existing uses of working directory iterators explicitly use the GIT_ITERATOR_DONT_AUTOEXPAND (for now). Interestingly, the majority of the implementation was in the index iterator, since there are no tree entries there and now have to fake them. The tree and working directory iterators only required small modifications. --- src/checkout.c | 3 +- src/diff.c | 24 ++- src/iterator.c | 327 +++++++++++++++++++++++++++++-------- src/iterator.h | 57 ++++--- tests-clar/diff/iterator.c | 12 +- tests-clar/repo/iterator.c | 197 ++++++++++++++++++++-- 6 files changed, 504 insertions(+), 116 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 41de0d7d4..68ebbe31d 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1243,7 +1243,8 @@ int git_checkout_iterator( if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_workdir( - &workdir, data.repo, iterflags, data.pfx, data.pfx)) < 0 || + &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; diff --git a/src/diff.c b/src/diff.c index 766361938..0a51e573b 100644 --- a/src/diff.c +++ b/src/diff.c @@ -705,9 +705,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(&nitem, new_iter) < 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; } } @@ -791,7 +801,7 @@ fail: git_iterator *a = NULL, *b = NULL; \ char *pfx = opts ? git_pathspec_prefix(&opts->pathspec) : NULL; \ GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \ - if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ + if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ error = git_diff__from_iterators(diff, repo, a, b, opts); \ git__free(pfx); git_iterator_free(a); git_iterator_free(b); \ } while (0) @@ -831,7 +841,7 @@ int git_diff_tree_to_index( DIFF_FROM_ITERATORS( git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), - git_iterator_for_index(&b, index, 0, pfx, pfx) + git_iterator_for_index(&b, index, 0, pfx, pfx) ); return error; @@ -852,7 +862,8 @@ int git_diff_index_to_workdir( DIFF_FROM_ITERATORS( git_iterator_for_index(&a, index, 0, pfx, pfx), - git_iterator_for_workdir(&b, repo, 0, pfx, pfx) + git_iterator_for_workdir( + &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) ); return error; @@ -871,7 +882,8 @@ int git_diff_tree_to_workdir( DIFF_FROM_ITERATORS( git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), - git_iterator_for_workdir(&b, repo, 0, pfx, pfx) + git_iterator_for_workdir( + &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) ); return error; diff --git a/src/iterator.c b/src/iterator.c index e28f20e12..6c7764736 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -15,6 +15,7 @@ #define ITERATOR_SET_CB(P,NAME_LC) do { \ (P)->cb.current = NAME_LC ## _iterator__current; \ (P)->cb.advance = NAME_LC ## _iterator__advance; \ + (P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \ (P)->cb.seek = NAME_LC ## _iterator__seek; \ (P)->cb.reset = NAME_LC ## _iterator__reset; \ (P)->cb.at_end = NAME_LC ## _iterator__at_end; \ @@ -36,12 +37,17 @@ git__free(P); return -1; } \ (P)->base.prefixcomp = git__prefixcmp; \ (P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \ + if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \ + (P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \ } while (0) -#define iterator__flag(I,F) ((((git_iterator *)(I))->flags & (F)) != 0) -#define iterator__ignore_case(I) iterator__flag(I,GIT_ITERATOR_IGNORE_CASE) +#define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0) +#define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE) +#define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES) +#define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND) +#define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND) -#define iterator__end(I) ((git_iterator *)(I))->end +#define iterator__end(I) ((git_iterator *)(I))->end #define iterator__past_end(I,PATH) \ (iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0) @@ -103,6 +109,12 @@ static int empty_iterator__noop( return 0; } +static int empty_iterator__seek(git_iterator *iter, const char *prefix) +{ + GIT_UNUSED(iter); GIT_UNUSED(prefix); + return -1; +} + static int empty_iterator__reset( git_iterator *iter, const char *start, const char *end) { @@ -110,12 +122,6 @@ static int empty_iterator__reset( return 0; } -static int empty_iterator__seek(git_iterator *iter, const char *prefix) -{ - GIT_UNUSED(iter); GIT_UNUSED(prefix); - return -1; -} - static int empty_iterator__at_end(git_iterator *iter) { GIT_UNUSED(iter); @@ -143,6 +149,7 @@ int git_iterator_for_nothing( #define empty_iterator__current empty_iterator__noop #define empty_iterator__advance empty_iterator__noop +#define empty_iterator__advance_into empty_iterator__noop ITERATOR_BASE_INIT(i, empty, EMPTY); @@ -196,6 +203,10 @@ static char *tree_iterator__current_filename( if (!ti->path_has_filename) { if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0) return NULL; + + if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0) + return NULL; + ti->path_has_filename = true; } @@ -382,21 +393,49 @@ static int tree_iterator__expand_tree(tree_iterator *ti) if ((error = tree_iterator__push_frame(ti, subtree, relpath)) < 0) return error; + /* if including trees, then one expansion is always enough */ + if (iterator__include_trees(ti)) + break; + te = tree_iterator__tree_entry(ti); } return 0; } +static int tree_iterator__advance_into( + const git_index_entry **entry, git_iterator *self) +{ + int error = 0; + tree_iterator *ti = (tree_iterator *)self; + const git_tree_entry *te = tree_iterator__tree_entry(ti); + + if (entry) + *entry = NULL; + + /* if DONT_AUTOEXPAND is off, the following will always be false */ + if (te && git_tree_entry__is_tree(te)) + error = tree_iterator__expand_tree(ti); + + if (!error && entry) + error = tree_iterator__current(entry, self); + + return error; +} + static int tree_iterator__advance( const git_index_entry **entry, git_iterator *self) { tree_iterator *ti = (tree_iterator *)self; - const git_tree_entry *te = NULL; + const git_tree_entry *te = tree_iterator__tree_entry(ti); if (entry != NULL) *entry = NULL; + /* given include_trees & autoexpand, we might have to go into a tree */ + if (te && git_tree_entry__is_tree(te) && iterator__do_autoexpand(ti)) + return tree_iterator__advance_into(entry, self); + if (ti->path_has_filename) { git_buf_rtruncate_at_char(&ti->path, '/'); ti->path_has_filename = false; @@ -414,11 +453,8 @@ static int tree_iterator__advance( git_buf_rtruncate_at_char(&ti->path, '/'); } - if (te && git_tree_entry__is_tree(te)) { - int error = tree_iterator__expand_tree(ti); - if (error < 0) - return error; - } + if (te && git_tree_entry__is_tree(te) && !iterator__include_trees(ti)) + return tree_iterator__advance_into(entry, self); return tree_iterator__current(entry, self); } @@ -461,7 +497,10 @@ static int tree_iterator__reset( git_buf_clear(&ti->path); ti->path_has_filename = false; - return tree_iterator__expand_tree(ti); + if (iterator__do_autoexpand(ti) && !iterator__include_trees(ti)) + return tree_iterator__expand_tree(ti); + + return 0; } int git_iterator_for_tree( @@ -488,7 +527,8 @@ int git_iterator_for_tree( (error = tree_iterator__push_frame(ti, tree, ti->base.start)) < 0) goto fail; - if ((error = tree_iterator__expand_tree(ti)) < 0) + if (iterator__do_autoexpand(ti) && !iterator__include_trees(ti) && + (error = tree_iterator__expand_tree(ti)) < 0) goto fail; *iter = (git_iterator *)ti; @@ -505,14 +545,95 @@ typedef struct { git_iterator_callbacks cb; git_index *index; size_t current; + /* when not in autoexpand mode, use these to represent "tree" state */ + git_buf partial; + size_t partial_pos; + char restore_terminator; + git_index_entry tree_entry; } index_iterator; +static const git_index_entry *index_iterator__index_entry(index_iterator *ii) +{ + const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); + + if (ie != NULL && iterator__past_end(ii, ie->path)) { + ii->current = git_index_entrycount(ii->index); + ie = NULL; + } + + return ie; +} + +static const git_index_entry *index_iterator__skip_conflicts(index_iterator *ii) +{ + const git_index_entry *ie; + + while ((ie = index_iterator__index_entry(ii)) != NULL && + git_index_entry_stage(ie) != 0) + ii->current++; + + return ie; +} + +static void index_iterator__next_prefix_tree(index_iterator *ii) +{ + const char *slash; + + if (!iterator__include_trees(ii)) + return; + + slash = strchr(&ii->partial.ptr[ii->partial_pos], '/'); + + if (slash != NULL) { + ii->partial_pos = (slash - ii->partial.ptr) + 1; + ii->restore_terminator = ii->partial.ptr[ii->partial_pos]; + ii->partial.ptr[ii->partial_pos] = '\0'; + } else { + ii->partial_pos = ii->partial.size; + } + + if (index_iterator__index_entry(ii) == NULL) + ii->partial_pos = ii->partial.size; +} + +static int index_iterator__first_prefix_tree(index_iterator *ii) +{ + const git_index_entry *ie = index_iterator__skip_conflicts(ii); + const char *scan, *prior, *slash; + + if (!ie || !iterator__include_trees(ii)) + return 0; + + /* find longest common prefix with prior index entry */ + + for (scan = slash = ie->path, prior = ii->partial.ptr; + *scan && *scan == *prior; ++scan, ++prior) + if (*scan == '/') + slash = scan; + + if (git_buf_sets(&ii->partial, ie->path) < 0) + return -1; + + ii->partial_pos = (slash - ie->path) + 1; + index_iterator__next_prefix_tree(ii); + + return 0; +} + +#define index_iterator__at_tree(I) \ + (iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size) + static int index_iterator__current( const git_index_entry **entry, git_iterator *self) { index_iterator *ii = (index_iterator *)self; const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); + if (ie != NULL && index_iterator__at_tree(ii)) { + ii->tree_entry.path = ii->partial.ptr; + ie = &ii->tree_entry; + } + if (entry) *entry = ie; @@ -525,35 +646,54 @@ static int index_iterator__at_end(git_iterator *self) return (ii->current >= git_index_entrycount(ii->index)); } -static void index_iterator__skip_conflicts(index_iterator *ii) -{ - size_t entrycount = git_index_entrycount(ii->index); - const git_index_entry *ie = NULL; - - while (ii->current < entrycount) { - ie = git_index_get_byindex(ii->index, ii->current); - - if (ie != NULL && iterator__past_end(ii, ie->path)) { - ii->current = entrycount; - break; - } - - if (git_index_entry_stage(ie) == 0) - break; - - ii->current++; - } -} - static int index_iterator__advance( const git_index_entry **entry, git_iterator *self) { index_iterator *ii = (index_iterator *)self; + size_t entrycount = git_index_entrycount(ii->index); + const git_index_entry *ie; - if (ii->current < git_index_entrycount(ii->index)) - ii->current++; + if (index_iterator__at_tree(ii)) { + if (iterator__do_autoexpand(ii)) { + ii->partial.ptr[ii->partial_pos] = ii->restore_terminator; + index_iterator__next_prefix_tree(ii); + } else { + /* advance to sibling tree (i.e. until we find entry that does + * not share this prefix) + */ + while (ii->current < entrycount) { + ii->current++; - index_iterator__skip_conflicts(ii); + if (!(ie = git_index_get_byindex(ii->index, ii->current)) || + ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0) + break; + } + + if (index_iterator__first_prefix_tree(ii) < 0) + return -1; + } + } else { + if (ii->current < entrycount) + ii->current++; + + if (index_iterator__first_prefix_tree(ii) < 0) + return -1; + } + + return index_iterator__current(entry, self); +} + +static int index_iterator__advance_into( + const git_index_entry **entry, git_iterator *self) +{ + index_iterator *ii = (index_iterator *)self; + const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); + + if (ie != NULL && index_iterator__at_tree(ii)) { + if (ii->restore_terminator) + ii->partial.ptr[ii->partial_pos] = ii->restore_terminator; + index_iterator__next_prefix_tree(ii); + } return index_iterator__current(entry, self); } @@ -570,6 +710,7 @@ static int index_iterator__reset( git_iterator *self, const char *start, const char *end) { index_iterator *ii = (index_iterator *)self; + const git_index_entry *ie; if (iterator__reset_range(self, start, end) < 0) return -1; @@ -577,7 +718,22 @@ static int index_iterator__reset( ii->current = ii->base.start ? git_index__prefix_position(ii->index, ii->base.start) : 0; - index_iterator__skip_conflicts(ii); + if ((ie = index_iterator__skip_conflicts(ii)) == NULL) + return 0; + + if (git_buf_sets(&ii->partial, ie->path) < 0) + return -1; + + ii->partial_pos = 0; + + if (ii->base.start) { + size_t startlen = strlen(ii->base.start); + + ii->partial_pos = (startlen > ii->partial.size) ? + ii->partial.size : startlen; + } + + index_iterator__next_prefix_tree(ii); return 0; } @@ -587,6 +743,8 @@ static void index_iterator__free(git_iterator *self) index_iterator *ii = (index_iterator *)self; git_index_free(ii->index); ii->index = NULL; + + git_buf_free(&ii->partial); } int git_iterator_for_index( @@ -598,8 +756,6 @@ int git_iterator_for_index( { index_iterator *ii; - GIT_UNUSED(flags); - ITERATOR_BASE_INIT(ii, index, INDEX); ii->base.repo = git_index_owner(index); @@ -612,6 +768,9 @@ int git_iterator_for_index( ii->index = index; GIT_REFCOUNT_INC(index); + git_buf_init(&ii->partial, 0); + ii->tree_entry.mode = GIT_FILEMODE_TREE; + index_iterator__reset((git_iterator *)ii, NULL, NULL); *iter = (git_iterator *)ii; @@ -620,6 +779,8 @@ int git_iterator_for_index( } +#define WORKDIR_MAX_DEPTH 100 + typedef struct workdir_iterator_frame workdir_iterator_frame; struct workdir_iterator_frame { workdir_iterator_frame *next; @@ -637,6 +798,7 @@ typedef struct { git_buf path; size_t root_len; int is_ignored; + int depth; } workdir_iterator; GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps) @@ -723,7 +885,14 @@ static void workdir_iterator__seek_frame_start( static int workdir_iterator__expand_dir(workdir_iterator *wi) { int error; - workdir_iterator_frame *wf = workdir_iterator__alloc_frame(wi); + workdir_iterator_frame *wf; + + if (++(wi->depth) > WORKDIR_MAX_DEPTH) { + giterr_set(GITERR_REPOSITORY, "Working directory is too deep"); + return -1; + } + + wf = workdir_iterator__alloc_frame(wi); GITERR_CHECK_ALLOC(wf); error = git_path_dirload_with_stat( @@ -764,21 +933,57 @@ static int workdir_iterator__at_end(git_iterator *self) return (((workdir_iterator *)self)->entry.path == NULL); } +static int workdir_iterator__advance_into( + const git_index_entry **entry, git_iterator *iter) +{ + int error = 0; + workdir_iterator *wi = (workdir_iterator *)iter; + + if (entry) + *entry = NULL; + + /* workdir iterator will allow you to explicitly advance into a + * commit/submodule (as well as a tree) to avoid some cases where an + * entry is mislabeled as a submodule in the working directory + */ + if (wi->entry.path != NULL && + (wi->entry.mode == GIT_FILEMODE_TREE || + wi->entry.mode == GIT_FILEMODE_COMMIT)) + /* returns GIT_ENOTFOUND if the directory is empty */ + error = workdir_iterator__expand_dir(wi); + + if (!error && entry) + error = workdir_iterator__current(entry, iter); + + return error; +} + static int workdir_iterator__advance( const git_index_entry **entry, git_iterator *self) { - int error; + int error = 0; workdir_iterator *wi = (workdir_iterator *)self; workdir_iterator_frame *wf; git_path_with_stat *next; + /* given include_trees & autoexpand, we might have to go into a tree */ + if (iterator__do_autoexpand(wi) && + wi->entry.path != NULL && + wi->entry.mode == GIT_FILEMODE_TREE) + { + error = workdir_iterator__advance_into(entry, self); + + /* continue silently past empty directories if autoexpanding */ + if (error != GIT_ENOTFOUND) + return error; + giterr_clear(); + error = 0; + } + if (entry != NULL) *entry = NULL; - if (wi->entry.path == NULL) - return 0; - - while (1) { + while (wi->entry.path != NULL) { wf = wi->stack; next = git_vector_get(&wf->entries, ++wf->index); @@ -906,9 +1111,13 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) assert(wi->entry.path[len - 1] == '/'); wi->entry.path[len - 1] = '\0'; wi->entry.mode = S_IFGITLINK; + return 0; } - return 0; + if (iterator__include_trees(wi)) + return 0; + + return workdir_iterator__advance_into(NULL, (git_iterator *)wi); } int git_iterator_for_workdir( @@ -1072,24 +1281,6 @@ bool git_iterator_current_is_ignored(git_iterator *iter) return (bool)wi->is_ignored; } -int git_iterator_advance_into( - const git_index_entry **entry, git_iterator *iter) -{ - workdir_iterator *wi = (workdir_iterator *)iter; - - if (iter->type == GIT_ITERATOR_TYPE_WORKDIR && - wi->entry.path && - (wi->entry.mode == GIT_FILEMODE_TREE || - wi->entry.mode == GIT_FILEMODE_COMMIT)) - { - if (workdir_iterator__expand_dir(wi) < 0) - /* if error loading or if empty, skip the directory. */ - return workdir_iterator__advance(entry, iter); - } - - return entry ? git_iterator_current(entry, iter) : 0; -} - int git_iterator_cmp(git_iterator *iter, const char *path_prefix) { const git_index_entry *entry; diff --git a/src/iterator.h b/src/iterator.h index 24c7b7765..4a4e6a9d8 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -26,11 +26,16 @@ typedef enum { 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)(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 *); @@ -102,12 +107,40 @@ GIT_INLINE(int) git_iterator_current( return iter->cb->current(entry, 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->advance(entry, iter); } +/** + * 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_into(entry, iter); +} + GIT_INLINE(int) git_iterator_seek( git_iterator *iter, const char *prefix) { @@ -148,33 +181,13 @@ GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter) extern int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case); extern int git_iterator_current_tree_entry( - const git_tree_entry **tree_entry, git_iterator *iter); + const git_tree_entry **entry_out, git_iterator *iter); extern int git_iterator_current_parent_tree( - const git_tree **tree_ptr, git_iterator *iter, const char *parent_path); + const git_tree **tree_out, git_iterator *iter, const char *parent_path); extern bool git_iterator_current_is_ignored(git_iterator *iter); -/** - * Iterate into a 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( - const git_index_entry **entry, git_iterator *iter); - extern int git_iterator_cmp( git_iterator *iter, const char *path_prefix); diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index 546f68abe..f1efdfbba 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -538,7 +538,8 @@ 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(&i, repo, 0, start, end)); + 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) { @@ -735,8 +736,8 @@ 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(&i, repo, 0, "dir", "sub/sub/file")); + 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) { @@ -771,10 +772,7 @@ static void check_wd_first_through_third_range( 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(&entry, i)); - else - cl_git_pass(git_iterator_advance(&entry, i)); + cl_git_pass(git_iterator_advance(&entry, i)); } cl_assert(expected[idx] == NULL); diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 9a3815a03..27ab4fea4 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -20,11 +20,15 @@ static void expect_iterator_items( { const git_index_entry *entry; int count; + int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES); count = 0; cl_git_pass(git_iterator_current(&entry, i)); while (entry != NULL) { + if (no_trees) + cl_assert(entry->mode != GIT_FILEMODE_TREE); + count++; cl_git_pass(git_iterator_advance(&entry, i)); @@ -41,6 +45,9 @@ static void expect_iterator_items( cl_git_pass(git_iterator_current(&entry, i)); while (entry != NULL) { + if (no_trees) + cl_assert(entry->mode != GIT_FILEMODE_TREE); + count++; if (entry->mode == GIT_FILEMODE_TREE) @@ -79,11 +86,23 @@ void test_repo_iterator__index(void) cl_git_pass(git_repository_index(&index, g_repo)); - /* normal index iteration */ + /* autoexpand with no tree entries for index */ cl_git_pass(git_iterator_for_index(&i, index, 0, NULL, NULL)); expect_iterator_items(i, 20, 20); git_iterator_free(i); + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 22, 22); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + expect_iterator_items(i, 12, 22); + git_iterator_free(i); + git_index_free(index); } @@ -99,7 +118,7 @@ void test_repo_iterator__index_icase(void) /* force case sensitivity */ cl_git_pass(git_index_set_caps(index, caps & ~GIT_INDEXCAP_IGNORE_CASE)); - /* normal index iteration with range */ + /* autoexpand with no tree entries over range */ cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D")); expect_iterator_items(i, 7, 7); git_iterator_free(i); @@ -108,10 +127,31 @@ void test_repo_iterator__index_icase(void) expect_iterator_items(i, 3, 3); git_iterator_free(i); + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + expect_iterator_items(i, 8, 8); + git_iterator_free(i); + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + expect_iterator_items(i, 4, 4); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + expect_iterator_items(i, 5, 8); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + expect_iterator_items(i, 1, 4); + git_iterator_free(i); + /* force case insensitivity */ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE)); - /* normal index iteration with range */ + /* autoexpand with no tree entries over range */ cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D")); expect_iterator_items(i, 13, 13); git_iterator_free(i); @@ -120,6 +160,28 @@ void test_repo_iterator__index_icase(void) expect_iterator_items(i, 5, 5); git_iterator_free(i); + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + expect_iterator_items(i, 14, 14); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + expect_iterator_items(i, 6, 6); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + expect_iterator_items(i, 9, 14); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_index( + &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + expect_iterator_items(i, 1, 6); + git_iterator_free(i); + cl_git_pass(git_index_set_caps(index, caps)); git_index_free(index); } @@ -131,11 +193,23 @@ void test_repo_iterator__tree(void) cl_git_pass(git_repository_head_tree(&head, g_repo)); - /* normal tree iteration */ + /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL)); expect_iterator_items(i, 20, 20); git_iterator_free(i); + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_tree( + &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 22, 22); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_tree( + &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + expect_iterator_items(i, 12, 22); + git_iterator_free(i); + git_tree_free(head); } @@ -149,7 +223,7 @@ void test_repo_iterator__tree_icase(void) flag = GIT_ITERATOR_DONT_IGNORE_CASE; - /* normal tree iteration with range */ + /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D")); expect_iterator_items(i, 7, 7); git_iterator_free(i); @@ -158,9 +232,31 @@ void test_repo_iterator__tree_icase(void) expect_iterator_items(i, 3, 3); git_iterator_free(i); + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_tree( + &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + expect_iterator_items(i, 8, 8); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + expect_iterator_items(i, 4, 4); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_tree( + &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + expect_iterator_items(i, 5, 8); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + expect_iterator_items(i, 1, 4); + git_iterator_free(i); + flag = GIT_ITERATOR_IGNORE_CASE; - /* normal tree iteration with range */ + /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D")); expect_iterator_items(i, 13, 13); git_iterator_free(i); @@ -168,15 +264,48 @@ void test_repo_iterator__tree_icase(void) cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z")); expect_iterator_items(i, 5, 5); git_iterator_free(i); + + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_tree( + &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + expect_iterator_items(i, 14, 14); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + expect_iterator_items(i, 6, 6); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_tree( + &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + expect_iterator_items(i, 9, 14); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + expect_iterator_items(i, 1, 6); + git_iterator_free(i); } void test_repo_iterator__workdir(void) { git_iterator *i; - /* normal workdir iteration uses explicit tree expansion */ + /* auto expand with no tree entries */ + cl_git_pass(git_iterator_for_workdir(&i, g_repo, 0, NULL, NULL)); + expect_iterator_items(i, 20, 20); + git_iterator_free(i); + + /* auto expand with tree entries */ cl_git_pass(git_iterator_for_workdir( - &i, g_repo, 0, NULL, NULL)); + &i, g_repo, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 22, 22); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); expect_iterator_items(i, 12, 22); git_iterator_free(i); } @@ -188,23 +317,67 @@ void test_repo_iterator__workdir_icase(void) flag = GIT_ITERATOR_DONT_IGNORE_CASE; - /* normal workdir iteration with range */ + /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D")); - expect_iterator_items(i, 5, 8); + expect_iterator_items(i, 7, 7); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z")); + expect_iterator_items(i, 3, 3); + git_iterator_free(i); + + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + expect_iterator_items(i, 8, 8); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + expect_iterator_items(i, 4, 4); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + expect_iterator_items(i, 5, 8); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); expect_iterator_items(i, 1, 4); git_iterator_free(i); flag = GIT_ITERATOR_IGNORE_CASE; - /* normal workdir iteration with range */ + /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D")); - expect_iterator_items(i, 9, 14); + expect_iterator_items(i, 13, 13); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z")); + expect_iterator_items(i, 5, 5); + git_iterator_free(i); + + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + expect_iterator_items(i, 14, 14); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + expect_iterator_items(i, 6, 6); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + expect_iterator_items(i, 9, 14); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_workdir( + &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); expect_iterator_items(i, 1, 6); git_iterator_free(i); } From e40f1c2d23c3758968df94a17831ec5432ae6988 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Fri, 8 Mar 2013 16:39:57 -0800 Subject: [PATCH 05/11] Make tree iterator handle icase equivalence There is a serious bug in the previous tree iterator implementation. If case insensitivity resulted in member elements being equivalent to one another, and those member elements were trees, then the children of the colliding elements would be processed in sequence instead of in a single flattened list. This meant that the tree iterator was not truly acting like a case-insensitive list. This completely reworks the tree iterator to manage lists with case insensitive equivalence classes and advance through the items in a unified manner in a single sorted frame. It is possible that at a future date we might want to update this to separate the case insensitive and case sensitive tree iterators so that the case sensitive one could be a minimal amount of code and the insensitive one would always know what it needed to do without checking flags. But there would be so much shared code between the two, that I'm not sure it that's a win. For now, this gets what we need. More tests are needed, though. --- src/hashsig.c | 21 +- src/iterator.c | 529 ++++++++++++++++++++----------------- src/posix.h | 3 +- src/unix/posix.h | 1 + src/util.c | 30 +++ src/util.h | 4 + tests-clar/repo/iterator.c | 210 +++++++++++---- 7 files changed, 488 insertions(+), 310 deletions(-) diff --git a/src/hashsig.c b/src/hashsig.c index e9c5164a4..3a75aaaed 100644 --- a/src/hashsig.c +++ b/src/hashsig.c @@ -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; diff --git a/src/iterator.c b/src/iterator.c index 6c7764736..84664c0f8 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -94,7 +94,7 @@ static int iterator_update_ignore_case( else if (ignore_case == 0) iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE); - iter->prefixcomp = ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) ? + iter->prefixcomp = iterator__ignore_case(iter) ? git__prefixcmp_icase : git__prefixcmp; return error; @@ -162,41 +162,38 @@ int git_iterator_for_nothing( } + +typedef struct { + size_t parent_entry_index; /* index in parent entries array */ + size_t parent_tree_index; /* index in parent entry tree */ + git_tree *tree; /* this tree if this is tree (only valid while current) */ +} tree_iterator_entry; + typedef struct tree_iterator_frame tree_iterator_frame; struct tree_iterator_frame { - tree_iterator_frame *next, *prev; - git_tree *tree; + tree_iterator_frame *parent, *child; + + size_t n_entries; /* items in this frame */ + size_t current; /* start of currently active range in frame */ + size_t next; /* start of next range in frame */ + const char *start; size_t startlen; - size_t index; - /* secondary tree index for case-insensitive sort */ - void **icase_map; - void *icase_data[GIT_FLEX_ARRAY]; + + tree_iterator_entry entries[GIT_FLEX_ARRAY]; }; typedef struct { git_iterator base; git_iterator_callbacks cb; - tree_iterator_frame *stack, *tail; + tree_iterator_frame *head, *top; git_index_entry entry; git_buf path; 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; -GIT_INLINE(const git_tree_entry *)tree_iterator__tree_entry(tree_iterator *ti) -{ - tree_iterator_frame *tf = ti->stack; - size_t entries = git_tree_entrycount(tf->tree), idx = tf->index; - - if (idx >= entries) - return NULL; - - if (tf->icase_map) - idx = (size_t)tf->icase_map[idx]; - - return git_tree_entry_byindex(tf->tree, idx); -} - static char *tree_iterator__current_filename( tree_iterator *ti, const git_tree_entry *te) { @@ -213,50 +210,214 @@ static char *tree_iterator__current_filename( return ti->path.ptr; } -static void tree_iterator__free_frame(tree_iterator_frame *tf) +static int tree_iterator__create_top_frame(tree_iterator *ti, git_tree *tree) { - if (!tf) - return; + size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry); + tree_iterator_frame *top = git__calloc(sz, sizeof(char)); + GITERR_CHECK_ALLOC(top); - git_tree_free(tf->tree); - tf->tree = NULL; + 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; - git__free(tf); + ti->head = ti->top = top; + + return 0; +} + +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 int tree_iterator__entry_cmp(const void *a, const void *b, void *p) +{ + tree_iterator_frame *tf = p; + const git_tree_entry *ae = tree_iterator__get_tree_entry(tf, a, 0); + const git_tree_entry *be = tree_iterator__get_tree_entry(tf, b, 0); + size_t common_len = min(ae->filename_len, be->filename_len); + int cmp = git__strncasecmp(ae->filename, be->filename, common_len); + + if (!cmp) { + char a_next = ae->filename[common_len]; + char b_next = be->filename[common_len]; + + if (!a_next && ae->attr == GIT_FILEMODE_TREE) + a_next = '/'; + if (!b_next && be->attr == GIT_FILEMODE_TREE) + b_next = '/'; + + cmp = (int)a_next - (int)b_next; + } + + return cmp; +} + +static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) +{ + /* find next and load trees for current range */ + int error = 0; + const git_tree_entry *te, *last_te = NULL; + + tf->next = tf->current; + + while (tf->next < tf->n_entries) { + if (!(te = tree_iterator__get_tree_entry(tf, 0, tf->next)) || + (last_te && ti->strcomp(last_te->filename, te->filename) != 0)) + break; + + if (git_tree_entry__is_tree(te) && + (error = git_tree_lookup( + &tf->entries[tf->next].tree, ti->base.repo, &te->oid)) < 0) + break; + + tf->next++; + last_te = te; + } + + if (last_te && !tree_iterator__current_filename(ti, last_te)) + return -1; + + return error; +} + +GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti) +{ + return (ti->head->current < ti->head->n_entries && + ti->head->entries[ti->head->current].tree != NULL); +} + +static int tree_iterator__push_frame(tree_iterator *ti) +{ + int error = 0; + tree_iterator_frame *tf = ti->head, *new_tf = NULL; + size_t i, n_entries = 0, sz = sizeof(tree_iterator_frame); + const git_tree_entry *te; + + /* if current item in head is not a tree, do nothing */ + if (tf->current >= tf->n_entries || !tf->entries[tf->current].tree) + return 0; + + /* build frame - sum tree entries from parent range */ + for (i = tf->current; i < tf->next; ++i) + n_entries += git_tree_entrycount(tf->entries[i].tree); + sz += n_entries * sizeof(tree_iterator_entry); + new_tf = git__calloc(sz, sizeof(char)); + GITERR_CHECK_ALLOC(new_tf); + + /* populate frame and entries */ + new_tf->parent = tf; + new_tf->n_entries = n_entries; + + for (i = tf->current, n_entries = 0; i < tf->next; ++i) { + git_tree *tree = tf->entries[i].tree; + size_t j, max_j = git_tree_entrycount(tree); + + for (j = 0; j < max_j; ++j) { + new_tf->entries[n_entries].parent_entry_index = i; + new_tf->entries[n_entries].parent_tree_index = j; + n_entries++; + } + } + + /* if ignore_case, sort entries case insensitively */ + if (iterator__ignore_case(ti)) + git__qsort_r( + new_tf->entries, new_tf->n_entries, sizeof(tree_iterator_entry), + tree_iterator__entry_cmp, new_tf); + + /* pick new_tf->current based on "start" (or start at zero) */ + if (tf->startlen > 0) { + /* find first item >= start */ + for (i = 0; i < new_tf->n_entries; ++i) { + if (!(te = tree_iterator__get_tree_entry(new_tf, NULL, i))) + break; + sz = min(tf->startlen, te->filename_len); + if (ti->strncomp(tf->start, te->filename, sz) <= 0 && + (tf->startlen <= te->filename_len || + tf->start[te->filename_len] == '/')) + break; + } + new_tf->current = i; + + if ((new_tf->start = strchr(tf->start, '/')) != NULL) { + new_tf->start++; + new_tf->startlen = strlen(new_tf->start); + } + } + + ti->path_has_filename = false; + + /* find next and load trees for current range */ + if ((error = tree_iterator__set_next(ti, new_tf)) < 0) + return error; + + tf->child = new_tf; + ti->head = new_tf; + + if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti)) + return tree_iterator__push_frame(ti); + + return 0; +} + +static bool tree_iterator__move_to_next(tree_iterator_frame *tf) +{ + while (tf->current < tf->next) { + if (tf->parent && tf->entries[tf->current].tree) { + git_tree_free(tf->entries[tf->current].tree); + tf->entries[tf->current].tree = NULL; + } + tf->current++; + } + + return (tf->current < tf->n_entries); } static bool tree_iterator__pop_frame(tree_iterator *ti) { - tree_iterator_frame *tf = ti->stack; + tree_iterator_frame *tf = ti->head; - /* don't free the initial tree/frame */ - if (!tf->next) + if (!tf->parent) return false; - ti->stack = tf->next; - ti->stack->prev = NULL; + tree_iterator__move_to_next(tf); - tree_iterator__free_frame(tf); + ti->head = tf->parent; + ti->head->child = NULL; + git__free(tf); + + git_buf_rtruncate_at_char(&ti->path, '/'); return true; } -static int tree_iterator__to_end(tree_iterator *ti) -{ - while (tree_iterator__pop_frame(ti)) /* pop all */; - ti->stack->index = git_tree_entrycount(ti->stack->tree); - return 0; -} - static int tree_iterator__current( const git_index_entry **entry, git_iterator *self) { tree_iterator *ti = (tree_iterator *)self; - const git_tree_entry *te = tree_iterator__tree_entry(ti); + tree_iterator_frame *tf = ti->head; + const git_tree_entry *te; if (entry) *entry = NULL; - if (te == NULL) + if (!(te = tree_iterator__get_tree_entry(tf, NULL, tf->current))) return 0; ti->entry.mode = te->attr; @@ -266,8 +427,11 @@ static int tree_iterator__current( if (ti->entry.path == NULL) return -1; - if (iterator__past_end(ti, ti->entry.path)) - return tree_iterator__to_end(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; + return 0; + } if (entry) *entry = &ti->entry; @@ -275,149 +439,17 @@ static int tree_iterator__current( return 0; } -static int tree_iterator__at_end(git_iterator *self) -{ - return (tree_iterator__tree_entry((tree_iterator *)self) == NULL); -} - -static int tree_iterator__icase_map_cmp(const void *a, const void *b, void *data) -{ - git_tree *tree = data; - const git_tree_entry *te1 = git_tree_entry_byindex(tree, (size_t)a); - const git_tree_entry *te2 = git_tree_entry_byindex(tree, (size_t)b); - - return te1 ? (te2 ? git_tree_entry_icmp(te1, te2) : 1) : -1; -} - -static int tree_iterator__frame_start_icmp(const void *key, const void *el) -{ - const tree_iterator_frame *tf = (const tree_iterator_frame *)key; - const git_tree_entry *te = git_tree_entry_byindex(tf->tree, (size_t)el); - size_t minlen = min(tf->startlen, te->filename_len); - - return git__strncasecmp(tf->start, te->filename, minlen); -} - -static void tree_iterator__frame_seek_start(tree_iterator_frame *tf) -{ - if (!tf->start) - tf->index = 0; - else if (!tf->icase_map) - tf->index = git_tree__prefix_position(tf->tree, tf->start); - else { - if (!git__bsearch( - tf->icase_map, git_tree_entrycount(tf->tree), - tf, tree_iterator__frame_start_icmp, &tf->index)) - { - while (tf->index > 0) { - /* move back while previous entry is still prefixed */ - if (tree_iterator__frame_start_icmp( - tf, (const void *)(tf->index - 1))) - break; - tf->index--; - } - } - } -} - -static int tree_iterator__push_frame( - tree_iterator *ti, git_tree *tree, const char *start) -{ - size_t i, max_i = git_tree_entrycount(tree); - tree_iterator_frame *tf = - git__calloc(1, sizeof(tree_iterator_frame) + max_i * sizeof(void *)); - GITERR_CHECK_ALLOC(tf); - - tf->tree = tree; - - tf->next = ti->stack; - ti->stack = tf; - if (tf->next) - tf->next->prev = tf; - else - ti->tail = tf; - - if (start && *start) { - tf->start = start; - tf->startlen = strlen(start); - } - - ti->path_has_filename = false; - - if (!max_i) - return 0; - - /* build secondary index if iterator is case-insensitive */ - if (iterator__ignore_case(ti)) { - tf->icase_map = tf->icase_data; - - for (i = 0; i < max_i; ++i) - tf->icase_map[i] = (void *)i; - - git__tsort_r( - tf->icase_map, max_i, tree_iterator__icase_map_cmp, tf->tree); - } - - tree_iterator__frame_seek_start(tf); - - return 0; -} - -static int tree_iterator__expand_tree(tree_iterator *ti) -{ - int error; - git_tree *subtree; - const git_tree_entry *te = tree_iterator__tree_entry(ti); - const char *relpath; - - while (te != NULL && git_tree_entry__is_tree(te)) { - relpath = tree_iterator__current_filename(ti, te); - - /* check that we have not passed the range end */ - if (iterator__past_end(ti, relpath)) - return tree_iterator__to_end(ti); - - if ((error = git_tree_lookup(&subtree, ti->base.repo, &te->oid)) < 0) - return error; - - relpath = NULL; - - /* apply range start to new frame if relevant */ - if (ti->stack->start && - ti->base.prefixcomp(ti->stack->start, te->filename) == 0) - { - if (ti->stack->start[te->filename_len] == '/') - relpath = ti->stack->start + te->filename_len + 1; - } - - if ((error = tree_iterator__push_frame(ti, subtree, relpath)) < 0) - return error; - - /* if including trees, then one expansion is always enough */ - if (iterator__include_trees(ti)) - break; - - te = tree_iterator__tree_entry(ti); - } - - return 0; -} - static int tree_iterator__advance_into( const git_index_entry **entry, git_iterator *self) { int error = 0; tree_iterator *ti = (tree_iterator *)self; - const git_tree_entry *te = tree_iterator__tree_entry(ti); if (entry) *entry = NULL; - /* if DONT_AUTOEXPAND is off, the following will always be false */ - if (te && git_tree_entry__is_tree(te)) - error = tree_iterator__expand_tree(ti); - - if (!error && entry) + if (tree_iterator__at_tree(ti) && + !(error = tree_iterator__push_frame(ti))) error = tree_iterator__current(entry, self); return error; @@ -426,14 +458,18 @@ static int tree_iterator__advance_into( static int tree_iterator__advance( const git_index_entry **entry, git_iterator *self) { + int error; tree_iterator *ti = (tree_iterator *)self; - const git_tree_entry *te = tree_iterator__tree_entry(ti); + tree_iterator_frame *tf = ti->head; - if (entry != NULL) + if (entry) *entry = NULL; - /* given include_trees & autoexpand, we might have to go into a tree */ - if (te && git_tree_entry__is_tree(te) && iterator__do_autoexpand(ti)) + if (tf->current > tf->n_entries) + return 0; + + if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) && + tree_iterator__at_tree(ti)) return tree_iterator__advance_into(entry, self); if (ti->path_has_filename) { @@ -441,19 +477,16 @@ static int tree_iterator__advance( ti->path_has_filename = false; } - while (1) { - ++ti->stack->index; + /* scan forward and up, advancing in frame or popping frame when done */ + while (!tree_iterator__move_to_next(tf) && tree_iterator__pop_frame(ti)) + tf = ti->head; - if ((te = tree_iterator__tree_entry(ti)) != NULL) - break; + /* find next and load trees */ + if ((error = tree_iterator__set_next(ti, tf)) < 0) + return error; - if (!tree_iterator__pop_frame(ti)) - break; /* no frames left to pop */ - - git_buf_rtruncate_at_char(&ti->path, '/'); - } - - if (te && git_tree_entry__is_tree(te) && !iterator__include_trees(ti)) + /* deal with include_trees / auto_expand as needed */ + if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti)) return tree_iterator__advance_into(entry, self); return tree_iterator__current(entry, self); @@ -463,44 +496,45 @@ static int tree_iterator__seek(git_iterator *self, const char *prefix) { GIT_UNUSED(self); GIT_UNUSED(prefix); - /* pop stack until matches prefix */ - /* seek item in current frame matching prefix */ - /* push stack which matches prefix */ return -1; } -static void tree_iterator__free(git_iterator *self) -{ - tree_iterator *ti = (tree_iterator *)self; - - while (tree_iterator__pop_frame(ti)) /* pop all */; - - tree_iterator__free_frame(ti->stack); - ti->stack = ti->tail = NULL; - - git_buf_free(&ti->path); -} - static int tree_iterator__reset( git_iterator *self, const char *start, const char *end) { tree_iterator *ti = (tree_iterator *)self; - while (tree_iterator__pop_frame(ti)) /* pop all */; + while (tree_iterator__pop_frame(ti)) /* pop to top */; + ti->top->current = 0; if (iterator__reset_range(self, start, end) < 0) return -1; - - /* reset start position */ - tree_iterator__frame_seek_start(ti->stack); - git_buf_clear(&ti->path); - ti->path_has_filename = false; - if (iterator__do_autoexpand(ti) && !iterator__include_trees(ti)) - return tree_iterator__expand_tree(ti); + return tree_iterator__push_frame(ti); /* re-expand top tree */ +} - return 0; +static int tree_iterator__at_end(git_iterator *self) +{ + tree_iterator *ti = (tree_iterator *)self; + return (ti->head->current >= ti->head->n_entries); +} + +static void tree_iterator__free(git_iterator *self) +{ + tree_iterator *ti = (tree_iterator *)self; + + while (tree_iterator__pop_frame(ti)) /* pop to top */; + + /* free base frame */ + if (ti->head) { + git_tree_free(ti->head->entries[0].tree); + ti->head->entries[0].tree = NULL; + git__free(ti->head); + } + ti->head = ti->top = NULL; + + git_buf_free(&ti->path); } int git_iterator_for_tree( @@ -520,15 +554,19 @@ int git_iterator_for_tree( return error; ITERATOR_BASE_INIT(ti, tree, TREE); - ti->base.repo = git_tree_owner(tree); - if ((error = iterator_update_ignore_case((git_iterator *)ti, flags)) < 0 || - (error = tree_iterator__push_frame(ti, tree, ti->base.start)) < 0) + if ((error = iterator_update_ignore_case((git_iterator *)ti, flags)) < 0) goto fail; - if (iterator__do_autoexpand(ti) && !iterator__include_trees(ti) && - (error = tree_iterator__expand_tree(ti)) < 0) + if (iterator__ignore_case(ti)) { + ti->strcomp = git__strcasecmp; ti->strncomp = git__strncasecmp; + } else { + ti->strcomp = git__strcmp; ti->strncomp = git__strncmp; + } + + if ((error = tree_iterator__create_top_frame(ti, tree)) < 0 || + (error = tree_iterator__push_frame(ti)) < 0) /* expand top right now */ goto fail; *iter = (git_iterator *)ti; @@ -1216,8 +1254,14 @@ git_index *git_iterator_get_index(git_iterator *iter) int git_iterator_current_tree_entry( const git_tree_entry **tree_entry, git_iterator *iter) { - *tree_entry = (iter->type != GIT_ITERATOR_TYPE_TREE) ? NULL : - tree_iterator__tree_entry((tree_iterator *)iter); + if (iter->type != GIT_ITERATOR_TYPE_TREE) + *tree_entry = NULL; + else { + tree_iterator *ti = (tree_iterator *)iter; + *tree_entry = + tree_iterator__get_tree_entry(ti->head, NULL, ti->head->current); + } + return 0; } @@ -1229,39 +1273,28 @@ int git_iterator_current_parent_tree( tree_iterator *ti = (tree_iterator *)iter; tree_iterator_frame *tf; const char *scan = parent_path; - int (*strncomp)(const char *a, const char *b, size_t sz); + const git_tree_entry *te; - if (iter->type != GIT_ITERATOR_TYPE_TREE || ti->stack == NULL) - goto notfound; + *tree_ptr = NULL; - strncomp = ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) ? - git__strncasecmp : git__strncmp; + if (iter->type != GIT_ITERATOR_TYPE_TREE) + return 0; - for (tf = ti->tail; tf != NULL; tf = tf->prev) { - const git_tree_entry *te; + tf = ti->top; - if (!*scan) { - *tree_ptr = tf->tree; + while (*scan) { + /* get entry of this parent that child is currently on */ + if (!(tf = tf->child) || + !(te = tree_iterator__get_tree_entry(tf, NULL, tf->current)) || + ti->strncomp(scan, te->filename, te->filename_len) != 0) return 0; - } - - te = git_tree_entry_byindex(tf->tree, - tf->icase_map ? (size_t)tf->icase_map[tf->index] : tf->index); - - if (strncomp(scan, te->filename, te->filename_len) != 0) - goto notfound; scan += te->filename_len; - - if (*scan) { - if (*scan != '/') - goto notfound; + if (*scan == '/') scan++; - } } -notfound: - *tree_ptr = NULL; + *tree_ptr = tf->entries[tf->current].tree; return 0; } diff --git a/src/posix.h b/src/posix.h index 9dd0c94d3..719c8a04c 100644 --- a/src/posix.h +++ b/src/posix.h @@ -32,14 +32,13 @@ typedef int git_file; * Standard POSIX Methods * * All the methods starting with the `p_` prefix are - * direct ports of the standard POSIX methods. + * direct ports of the standard POSIX methods. * * Some of the methods are slightly wrapped to provide * saner defaults. Some of these methods are emulated * 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); diff --git a/src/unix/posix.h b/src/unix/posix.h index c738b531d..f4886c5d1 100644 --- a/src/unix/posix.h +++ b/src/unix/posix.h @@ -8,6 +8,7 @@ #define INCLUDE_posix__w32_h__ #include +#include #define p_lstat(p,b) lstat(p,b) #define p_readlink(a, b, c) readlink(a, b, c) diff --git a/src/util.c b/src/util.c index 059108ece..561288f72 100644 --- a/src/util.c +++ b/src/util.c @@ -607,3 +607,33 @@ size_t git__unescape(char *str) return (pos - str); } + +#if defined(GIT_WIN32) || defined(BSD) +typedef struct { + git__qsort_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__qsort_r_cmp cmp, void *payload) +{ +#if defined(GIT_WIN32) + git__qsort_r_glue glue = { cmp, payload }; + qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue); +#else +#if 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 +#endif +} diff --git a/src/util.h b/src/util.h index 9dbcb6a4f..e77f17efc 100644 --- a/src/util.h +++ b/src/util.h @@ -151,6 +151,10 @@ typedef int (*git__tsort_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); +typedef int (*git__qsort_r_cmp)(const void *a, const void *b, void *payload); + +extern void git__qsort_r( + void *els, size_t nel, size_t elsize, git__qsort_r_cmp cmp, void *payload); /** * @param position If non-NULL, this will be set to the position where the diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 27ab4fea4..3d72d7a49 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -6,7 +6,6 @@ static git_repository *g_repo; void test_repo_iterator__initialize(void) { - g_repo = cl_git_sandbox_init("icase"); } void test_repo_iterator__cleanup(void) @@ -16,7 +15,11 @@ void test_repo_iterator__cleanup(void) } static void expect_iterator_items( - git_iterator *i, int expected_flat, int expected_total) + git_iterator *i, + int expected_flat, + const char **expected_flat_paths, + int expected_total, + const char **expected_total_paths) { const git_index_entry *entry; int count; @@ -29,11 +32,21 @@ static void expect_iterator_items( if (no_trees) cl_assert(entry->mode != GIT_FILEMODE_TREE); - count++; + if (expected_flat_paths) { + const char *expect_path = expected_flat_paths[count]; + size_t expect_len = strlen(expect_path); + + cl_assert_equal_s(expect_path, entry->path); + + if (expect_path[expect_len - 1] == '/') + cl_assert_equal_i(GIT_FILEMODE_TREE, entry->mode); + else + cl_assert(entry->mode != GIT_FILEMODE_TREE); + } cl_git_pass(git_iterator_advance(&entry, i)); - if (count > expected_flat) + if (++count > expected_flat) break; } @@ -48,14 +61,24 @@ static void expect_iterator_items( if (no_trees) cl_assert(entry->mode != GIT_FILEMODE_TREE); - count++; + if (expected_total_paths) { + const char *expect_path = expected_total_paths[count]; + size_t expect_len = strlen(expect_path); + + cl_assert_equal_s(expect_path, entry->path); + + if (expect_path[expect_len - 1] == '/') + cl_assert_equal_i(GIT_FILEMODE_TREE, entry->mode); + else + cl_assert(entry->mode != GIT_FILEMODE_TREE); + } if (entry->mode == GIT_FILEMODE_TREE) cl_git_pass(git_iterator_advance_into(&entry, i)); else cl_git_pass(git_iterator_advance(&entry, i)); - if (count > expected_total) + if (++count > expected_total) break; } @@ -84,23 +107,25 @@ void test_repo_iterator__index(void) git_iterator *i; git_index *index; + g_repo = cl_git_sandbox_init("icase"); + cl_git_pass(git_repository_index(&index, g_repo)); /* autoexpand with no tree entries for index */ cl_git_pass(git_iterator_for_index(&i, index, 0, NULL, NULL)); - expect_iterator_items(i, 20, 20); + expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); - expect_iterator_items(i, 22, 22); + expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); - expect_iterator_items(i, 12, 22); + expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); git_index_free(index); @@ -112,6 +137,8 @@ void test_repo_iterator__index_icase(void) git_index *index; unsigned int caps; + g_repo = cl_git_sandbox_init("icase"); + cl_git_pass(git_repository_index(&index, g_repo)); caps = git_index_caps(index); @@ -120,32 +147,32 @@ void test_repo_iterator__index_icase(void) /* autoexpand with no tree entries over range */ cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D")); - expect_iterator_items(i, 7, 7); + expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z")); - expect_iterator_items(i, 3, 3); + expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); - expect_iterator_items(i, 8, 8); + expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); - expect_iterator_items(i, 4, 4); + expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); - expect_iterator_items(i, 5, 8); + expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); - expect_iterator_items(i, 1, 4); + expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); /* force case insensitivity */ @@ -153,33 +180,33 @@ void test_repo_iterator__index_icase(void) /* autoexpand with no tree entries over range */ cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D")); - expect_iterator_items(i, 13, 13); + expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z")); - expect_iterator_items(i, 5, 5); + expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); - expect_iterator_items(i, 14, 14); + expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); - expect_iterator_items(i, 6, 6); + expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); - expect_iterator_items(i, 9, 14); + expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_index( &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); - expect_iterator_items(i, 1, 6); + expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); cl_git_pass(git_index_set_caps(index, caps)); @@ -191,23 +218,25 @@ void test_repo_iterator__tree(void) git_iterator *i; git_tree *head; + g_repo = cl_git_sandbox_init("icase"); + cl_git_pass(git_repository_head_tree(&head, g_repo)); /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL)); - expect_iterator_items(i, 20, 20); + expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_tree( &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); - expect_iterator_items(i, 22, 22); + expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_tree( &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); - expect_iterator_items(i, 12, 22); + expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); git_tree_free(head); @@ -219,94 +248,171 @@ void test_repo_iterator__tree_icase(void) git_tree *head; git_iterator_flag_t flag; + g_repo = cl_git_sandbox_init("icase"); + cl_git_pass(git_repository_head_tree(&head, g_repo)); flag = GIT_ITERATOR_DONT_IGNORE_CASE; /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D")); - expect_iterator_items(i, 7, 7); + expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z")); - expect_iterator_items(i, 3, 3); + expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_tree( &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); - expect_iterator_items(i, 8, 8); + expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_tree( &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); - expect_iterator_items(i, 4, 4); + expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_tree( &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); - expect_iterator_items(i, 5, 8); + expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_tree( &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); - expect_iterator_items(i, 1, 4); + expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); flag = GIT_ITERATOR_IGNORE_CASE; /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D")); - expect_iterator_items(i, 13, 13); + expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z")); - expect_iterator_items(i, 5, 5); + expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_tree( &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); - expect_iterator_items(i, 14, 14); + expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_tree( &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); - expect_iterator_items(i, 6, 6); + expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_tree( &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); - expect_iterator_items(i, 9, 14); + expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_tree( &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); - expect_iterator_items(i, 1, 6); + expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); } +void test_repo_iterator__tree_more(void) +{ + git_iterator *i; + git_tree *head; + static const char *expect_basic[] = { + "current_file", + "file_deleted", + "modified_file", + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "subdir.txt", + "subdir/current_file", + "subdir/deleted_file", + "subdir/modified_file", + NULL, + }; + static const char *expect_trees[] = { + "current_file", + "file_deleted", + "modified_file", + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "subdir.txt", + "subdir/", + "subdir/current_file", + "subdir/deleted_file", + "subdir/modified_file", + NULL, + }; + static const char *expect_noauto[] = { + "current_file", + "file_deleted", + "modified_file", + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "subdir.txt", + "subdir/", + NULL + }; + + g_repo = cl_git_sandbox_init("status"); + + cl_git_pass(git_repository_head_tree(&head, g_repo)); + + /* auto expand with no tree entries */ + cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL)); + expect_iterator_items(i, 12, expect_basic, 12, expect_basic); + git_iterator_free(i); + + /* auto expand with tree entries */ + cl_git_pass(git_iterator_for_tree( + &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 13, expect_trees, 13, expect_trees); + git_iterator_free(i); + + /* no auto expand (implies trees included) */ + cl_git_pass(git_iterator_for_tree( + &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + expect_iterator_items(i, 10, expect_noauto, 13, expect_trees); + git_iterator_free(i); + + git_tree_free(head); +} + void test_repo_iterator__workdir(void) { git_iterator *i; + g_repo = cl_git_sandbox_init("icase"); + /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_workdir(&i, g_repo, 0, NULL, NULL)); - expect_iterator_items(i, 20, 20); + expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_workdir( &i, g_repo, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); - expect_iterator_items(i, 22, 22); + expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_workdir( &i, g_repo, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); - expect_iterator_items(i, 12, 22); + expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); } @@ -315,69 +421,71 @@ void test_repo_iterator__workdir_icase(void) git_iterator *i; git_iterator_flag_t flag; + g_repo = cl_git_sandbox_init("icase"); + flag = GIT_ITERATOR_DONT_IGNORE_CASE; /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D")); - expect_iterator_items(i, 7, 7); + expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z")); - expect_iterator_items(i, 3, 3); + expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_workdir( &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); - expect_iterator_items(i, 8, 8); + expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir( &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); - expect_iterator_items(i, 4, 4); + expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_workdir( &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); - expect_iterator_items(i, 5, 8); + expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir( &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); - expect_iterator_items(i, 1, 4); + expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); flag = GIT_ITERATOR_IGNORE_CASE; /* auto expand with no tree entries */ cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D")); - expect_iterator_items(i, 13, 13); + expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z")); - expect_iterator_items(i, 5, 5); + expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_workdir( &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); - expect_iterator_items(i, 14, 14); + expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir( &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); - expect_iterator_items(i, 6, 6); + expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_workdir( &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); - expect_iterator_items(i, 9, 14); + expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir( &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); - expect_iterator_items(i, 1, 6); + expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); } From a03beb7ba6017181c29d77e64112e4730f690271 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sun, 10 Mar 2013 21:04:35 -0700 Subject: [PATCH 06/11] Add tests for case insensitive tree iterator This adds a test case for ci tree iteration when there is a name conflict. This points out a behavior quirk in the current version that I'd like to fix - namely, all tree entries get mapped to one version of the case pattern in the ci code - i.e. even if you have A/1.txt and a/2.txt, both will be reported as a/1.txt and a/2.txt because we only copy the name of a file at a given frame once. It would be nice to fix this, but I'm worried about how complex that is if you get a/B/c/1.txt and A/b/C/2.txt. It may require a walk up the frames whenever you advance to the next item in a blended equivalence class. --- tests-clar/repo/iterator.c | 93 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 3d72d7a49..63613e903 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -1,6 +1,7 @@ #include "clar_libgit2.h" #include "iterator.h" #include "repository.h" +#include static git_repository *g_repo; @@ -24,11 +25,19 @@ static void expect_iterator_items( const git_index_entry *entry; int count; int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES); + bool v = false; + + if (expected_flat < 0) { v = true; expected_flat = -expected_flat; } + if (expected_total < 0) { v = true; expected_total = -expected_total; } count = 0; cl_git_pass(git_iterator_current(&entry, i)); + if (v) fprintf(stderr, "== %s ==\n", no_trees ? "notrees" : "trees"); + while (entry != NULL) { + if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode); + if (no_trees) cl_assert(entry->mode != GIT_FILEMODE_TREE); @@ -57,7 +66,11 @@ static void expect_iterator_items( count = 0; cl_git_pass(git_iterator_current(&entry, i)); + if (v) fprintf(stderr, "-- %s --\n", no_trees ? "notrees" : "trees"); + while (entry != NULL) { + if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode); + if (no_trees) cl_assert(entry->mode != GIT_FILEMODE_TREE); @@ -392,6 +405,86 @@ void test_repo_iterator__tree_more(void) git_tree_free(head); } +/* "b=name,t=name", blob_id, tree_id */ +static void build_test_tree( + git_oid *out, git_repository *repo, const char *fmt, ...) +{ + git_oid *id; + git_treebuilder *builder; + const char *scan = fmt, *next; + char type, delimiter; + git_filemode_t mode; + git_buf name = GIT_BUF_INIT; + va_list arglist; + + cl_git_pass(git_treebuilder_create(&builder, NULL)); /* start builder */ + + va_start(arglist, fmt); + while (*scan) { + switch (type = *scan++) { + case 't': case 'T': mode = GIT_FILEMODE_TREE; break; + case 'b': case 'B': mode = GIT_FILEMODE_BLOB; break; + default: + cl_assert(type == 't' || type == 'T' || type == 'b' || type == 'B'); + } + + delimiter = *scan++; /* read and skip delimiter */ + for (next = scan; *next && *next != delimiter; ++next) + /* seek end */; + cl_git_pass(git_buf_set(&name, scan, (size_t)(next - scan))); + for (scan = next; *scan && (*scan == delimiter || *scan == ','); ++scan) + /* skip delimiter and optional comma */; + + id = va_arg(arglist, git_oid *); + + cl_git_pass(git_treebuilder_insert(NULL, builder, name.ptr, id, mode)); + } + va_end(arglist); + + cl_git_pass(git_treebuilder_write(out, repo, builder)); + + git_treebuilder_free(builder); + git_buf_free(&name); +} + +void test_repo_iterator__tree_case_conflicts(void) +{ + const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6"; + git_tree *tree; + git_oid blob_id, biga_id, littlea_id, tree_id; + git_iterator *i; + 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" }; + + g_repo = cl_git_sandbox_init("icase"); + + cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */ + + /* create tree with: A/1.file, A/3.file, a/2.file, a/4.file */ + build_test_tree( + &biga_id, g_repo, "b/1.file/,b/3.file/", &blob_id, &blob_id); + build_test_tree( + &littlea_id, g_repo, "b/2.file/,b/4.file/", &blob_id, &blob_id); + build_test_tree( + &tree_id, g_repo, "t/A/,t/a/", &biga_id, &littlea_id); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); + expect_iterator_items(i, 4, expect_cs, 4, expect_cs); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); + expect_iterator_items(i, 4, expect_ci, -4, expect_ci); + git_iterator_free(i); + + git_tree_free(tree); +} + void test_repo_iterator__workdir(void) { git_iterator *i; From 61c7b61e6fe2542deeb8d2aadbea90a8f5b3c9cd Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Sun, 10 Mar 2013 22:38:53 -0700 Subject: [PATCH 07/11] 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. --- src/iterator.c | 107 ++++++++++++++++++++++++------------- tests-clar/repo/iterator.c | 4 +- 2 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 84664c0f8..53ec6f61b 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -189,44 +189,12 @@ 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 char *tree_iterator__current_filename( - tree_iterator *ti, const git_tree_entry *te) -{ - if (!ti->path_has_filename) { - if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0) - return NULL; - - if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0) - return NULL; - - ti->path_has_filename = true; - } - - return ti->path.ptr; -} - -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; -} - static const git_tree_entry *tree_iterator__get_tree_entry( tree_iterator_frame *tf, const tree_iterator_entry *entry, size_t i) { @@ -245,6 +213,45 @@ static const git_tree_entry *tree_iterator__get_tree_entry( return git_tree_entry_byindex(tree, entry->parent_tree_index); } +static char *tree_iterator__current_filename( + tree_iterator *ti, const git_tree_entry *te) +{ + if (!ti->path_has_filename) { + if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0) + return NULL; + + if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0) + return NULL; + + ti->path_has_filename = true; + } + + return ti->path.ptr; +} + +static void tree_iterator__rewrite_filename(tree_iterator *ti) +{ + tree_iterator_frame *scan = ti->head; + size_t current = scan->current; + ssize_t strpos = ti->path.size; + const git_tree_entry *te; + + while (scan && scan->parent) { + tree_iterator_entry *entry = &scan->entries[current]; + + te = tree_iterator__get_tree_entry(scan, entry, 0); + if (!te) + break; + + strpos -= te->filename_len; + memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len); + strpos -= 1; /* separator */ + + current = entry->parent_entry_index; + scan = scan->parent; + } +} + static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) { tree_iterator_frame *tf = 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 63613e903..804bc8324 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); From 92028ea58541c9de69096bd6b1bbe664976c24c1 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 11 Mar 2013 09:53:49 -0700 Subject: [PATCH 08/11] 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. --- src/iterator.c | 111 +++++++++++++++++-------------------- tests-clar/repo/iterator.c | 16 ++++++ 2 files changed, 67 insertions(+), 60 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 53ec6f61b..273f0733f 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -25,12 +25,13 @@ #define ITERATOR_CASE_FLAGS \ (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)); \ GITERR_CHECK_ALLOC(P); \ (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \ - (P)->base.cb = &(P)->cb; \ + (P)->base.cb = &(P)->cb; \ ITERATOR_SET_CB(P,NAME_LC); \ + (P)->base.repo = (REPO); \ (P)->base.start = start ? git__strdup(start) : NULL; \ (P)->base.end = end ? git__strdup(end) : NULL; \ if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \ @@ -72,7 +73,7 @@ static int iterator__reset_range( return 0; } -static int iterator_update_ignore_case( +static int iterator__update_ignore_case( git_iterator *iter, git_iterator_flag_t flags) { @@ -100,37 +101,40 @@ static int iterator_update_ignore_case( return error; } - -static int empty_iterator__noop( - const git_index_entry **entry, git_iterator *iter) +GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry) { - GIT_UNUSED(iter); 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; } -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; } -static int empty_iterator__reset( - git_iterator *iter, const char *start, const char *end) +static int empty_iterator__reset(git_iterator *i, const char *s, const char *e) { - GIT_UNUSED(iter); GIT_UNUSED(start); GIT_UNUSED(end); + GIT_UNUSED(i); GIT_UNUSED(s); GIT_UNUSED(e); 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; } -static void empty_iterator__free(git_iterator *iter) +static void empty_iterator__free(git_iterator *i) { - GIT_UNUSED(iter); + GIT_UNUSED(i); } typedef struct { @@ -151,18 +155,16 @@ int git_iterator_for_nothing( #define empty_iterator__advance 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) i->base.flags |= GIT_ITERATOR_IGNORE_CASE; *iter = (git_iterator *)i; - return 0; } - typedef struct { size_t parent_entry_index; /* index in parent entries array */ 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; const git_tree_entry *te; + if (strpos && ti->path.ptr[strpos - 1] == '/') + strpos--; + while (scan && scan->parent) { tree_iterator_entry *entry = &scan->entries[current]; @@ -386,18 +391,23 @@ static int tree_iterator__push_frame(tree_iterator *ti) 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( 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); - tf->entries[tf->current].tree = NULL; - } - tf->current++; + for (; tf->current < tf->next; tf->current++) { + if (tf->parent) + tree_iterator__free_tree(&tf->entries[tf->current]); } return (tf->current < tf->n_entries); @@ -428,8 +438,7 @@ static int tree_iterator__current( tree_iterator_frame *tf = ti->head; const git_tree_entry *te; - if (entry) - *entry = NULL; + iterator__clear_entry(entry); if (!(te = tree_iterator__get_tree_entry(tf, NULL, tf->current))) return 0; @@ -462,8 +471,7 @@ static int tree_iterator__advance_into( int error = 0; tree_iterator *ti = (tree_iterator *)self; - if (entry) - *entry = NULL; + iterator__clear_entry(entry); if (tree_iterator__at_tree(ti) && !(error = tree_iterator__push_frame(ti))) @@ -479,8 +487,7 @@ static int tree_iterator__advance( tree_iterator *ti = (tree_iterator *)self; tree_iterator_frame *tf = ti->head; - if (entry) - *entry = NULL; + iterator__clear_entry(entry); if (tf->current > tf->n_entries) return 0; @@ -511,8 +518,7 @@ static int tree_iterator__advance( static int tree_iterator__seek(git_iterator *self, const char *prefix) { - GIT_UNUSED(self); - GIT_UNUSED(prefix); + GIT_UNUSED(self); GIT_UNUSED(prefix); return -1; } @@ -544,10 +550,8 @@ static void tree_iterator__free(git_iterator *self) while (tree_iterator__pop_frame(ti)) /* pop to top */; - /* free base frame */ if (ti->head) { - git_tree_free(ti->head->entries[0].tree); - ti->head->entries[0].tree = NULL; + tree_iterator__free_tree(&ti->head->entries[0]); git__free(ti->head); } ti->head = ti->top = NULL; @@ -588,10 +592,9 @@ int git_iterator_for_tree( if ((error = git_tree__dup(&tree, tree)) < 0) return error; - ITERATOR_BASE_INIT(ti, tree, TREE); - ti->base.repo = git_tree_owner(tree); + ITERATOR_BASE_INIT(ti, tree, TREE, 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; if (iterator__ignore_case(ti)) { @@ -678,7 +681,6 @@ static int index_iterator__first_prefix_tree(index_iterator *ii) return 0; /* find longest common prefix with prior index entry */ - for (scan = slash = ie->path, prior = ii->partial.ptr; *scan && *scan == *prior; ++scan, ++prior) if (*scan == '/') @@ -731,9 +733,7 @@ static int index_iterator__advance( ii->partial.ptr[ii->partial_pos] = ii->restore_terminator; index_iterator__next_prefix_tree(ii); } else { - /* advance to sibling tree (i.e. until we find entry that does - * not share this prefix) - */ + /* advance to sibling tree (i.e. find entry with new prefix) */ while (ii->current < entrycount) { ii->current++; @@ -773,9 +773,7 @@ static int index_iterator__advance_into( static int index_iterator__seek(git_iterator *self, const char *prefix) { - GIT_UNUSED(self); - GIT_UNUSED(prefix); - /* find last item before prefix */ + GIT_UNUSED(self); GIT_UNUSED(prefix); return -1; } @@ -829,9 +827,7 @@ int git_iterator_for_index( { index_iterator *ii; - ITERATOR_BASE_INIT(ii, index, INDEX); - - ii->base.repo = git_index_owner(index); + ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index)); if (index->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); error = git_path_dirload_with_stat( - wi->path.ptr, wi->root_len, - iterator__ignore_case(wi), + wi->path.ptr, wi->root_len, iterator__ignore_case(wi), wi->base.start, wi->base.end, &wf->entries); if (error < 0 || wf->entries.length == 0) { @@ -1012,8 +1007,7 @@ static int workdir_iterator__advance_into( int error = 0; workdir_iterator *wi = (workdir_iterator *)iter; - if (entry) - *entry = NULL; + iterator__clear_entry(entry); /* workdir iterator will allow you to explicitly advance into a * 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); if ((error = git_repository__ensure_not_bare( - repo, "scan working directory")) < 0) + repo, "scan working directory")) < 0) return error; - ITERATOR_BASE_INIT(wi, workdir, WORKDIR); - wi->base.repo = repo; + ITERATOR_BASE_INIT(wi, workdir, WORKDIR, 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; 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) return ((index_iterator *)iter)->index; - return NULL; } @@ -1354,8 +1346,7 @@ int git_iterator_cmp(git_iterator *iter, const char *path_prefix) const git_index_entry *entry; /* a "done" iterator is after every prefix */ - if (git_iterator_current(&entry, iter) < 0 || - entry == NULL) + if (git_iterator_current(&entry, iter) < 0 || entry == NULL) return 1; /* a NULL prefix is after any valid iterator */ diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 804bc8324..e7498f67d 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -457,6 +457,10 @@ void test_repo_iterator__tree_case_conflicts(void) "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" }; + 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"); @@ -482,6 +486,18 @@ void test_repo_iterator__tree_case_conflicts(void) expect_iterator_items(i, 4, expect_ci, 4, expect_ci); git_iterator_free(i); + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE | + GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 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); } From aec4f6633ccdd359a39d712a27f87e613f788f6c Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 11 Mar 2013 10:37:12 -0700 Subject: [PATCH 09/11] Fix tree iterator advance using wrong name compare Tree iterator advance was moving forward without taking the filemode of the entries into account, equating "a" and "a/". This makes the tree entry comparison code more easily reusable and fixes the problem. --- src/iterator.c | 82 ++++++++++++++++++++------------------ tests-clar/repo/iterator.c | 55 +++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 39 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 273f0733f..fb76085cd 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -193,26 +193,31 @@ typedef struct { 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) +static const git_tree_entry *tree_iterator__tree_entry( + tree_iterator_frame *tf, const tree_iterator_entry *entry) +{ + git_tree *tree = tf->parent->entries[entry->parent_entry_index].tree; + if (!tree) + return NULL; + return git_tree_entry_byindex(tree, entry->parent_tree_index); +} + +static const git_tree_entry *tree_iterator__tree_entry_by_index( + tree_iterator_frame *tf, size_t i) { git_tree *tree; - if (!entry) { - if (i >= tf->n_entries) - return NULL; - entry = &tf->entries[i]; - } + if (i >= tf->n_entries) + return NULL; - tree = tf->parent->entries[entry->parent_entry_index].tree; + tree = tf->parent->entries[tf->entries[i].parent_entry_index].tree; if (!tree) return NULL; - return git_tree_entry_byindex(tree, entry->parent_tree_index); + return git_tree_entry_byindex(tree, tf->entries[i].parent_tree_index); } static char *tree_iterator__current_filename( @@ -244,8 +249,7 @@ static void tree_iterator__rewrite_filename(tree_iterator *ti) while (scan && scan->parent) { tree_iterator_entry *entry = &scan->entries[current]; - te = tree_iterator__get_tree_entry(scan, entry, 0); - if (!te) + if (!(te = tree_iterator__tree_entry(scan, entry))) break; strpos -= te->filename_len; @@ -257,21 +261,20 @@ static void tree_iterator__rewrite_filename(tree_iterator *ti) } } -static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) +static int tree_iterator__tree_entry_cmp( + const git_tree_entry *a, + const git_tree_entry *b, + int (*strncomp)(const char *, const char *, size_t)) { - tree_iterator_frame *tf = p; - const git_tree_entry *ae = tree_iterator__get_tree_entry(tf, a, 0); - const git_tree_entry *be = tree_iterator__get_tree_entry(tf, b, 0); - size_t common_len = min(ae->filename_len, be->filename_len); - int cmp = git__strncasecmp(ae->filename, be->filename, common_len); + size_t common = min(a->filename_len, b->filename_len); + int cmp = strncomp(a->filename, b->filename, common); if (!cmp) { - char a_next = ae->filename[common_len]; - char b_next = be->filename[common_len]; + char a_next = a->filename[common], b_next = b->filename[common]; - if (!a_next && ae->attr == GIT_FILEMODE_TREE) + if (!a_next && a->attr == GIT_FILEMODE_TREE) a_next = '/'; - if (!b_next && be->attr == GIT_FILEMODE_TREE) + if (!b_next && b->attr == GIT_FILEMODE_TREE) b_next = '/'; cmp = (int)a_next - (int)b_next; @@ -280,17 +283,24 @@ static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) return cmp; } +static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) +{ + return tree_iterator__tree_entry_cmp( + tree_iterator__tree_entry(p, a), tree_iterator__tree_entry(p, b), + git__strncasecmp); +} + static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) { /* find next and load trees for current range */ int error = 0; - const git_tree_entry *te, *last_te = NULL; + const git_tree_entry *te, *last = NULL; tf->next = tf->current; while (tf->next < tf->n_entries) { - if (!(te = tree_iterator__get_tree_entry(tf, 0, tf->next)) || - (last_te && ti->strcomp(last_te->filename, te->filename) != 0)) + if (!(te = tree_iterator__tree_entry_by_index(tf, tf->next)) || + (last && tree_iterator__tree_entry_cmp(last, te, ti->strncomp))) break; if (git_tree_entry__is_tree(te) && @@ -299,13 +309,13 @@ static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) break; tf->next++; - last_te = te; + last = te; } if (tf->next > tf->current + 1) ti->path_ambiguities++; - if (last_te && !tree_iterator__current_filename(ti, last_te)) + if (last && !tree_iterator__current_filename(ti, last)) return -1; return error; @@ -360,7 +370,7 @@ static int tree_iterator__push_frame(tree_iterator *ti) if (tf->startlen > 0) { /* find first item >= start */ for (i = 0; i < new_tf->n_entries; ++i) { - if (!(te = tree_iterator__get_tree_entry(new_tf, NULL, i))) + if (!(te = tree_iterator__tree_entry_by_index(new_tf, i))) break; sz = min(tf->startlen, te->filename_len); if (ti->strncomp(tf->start, te->filename, sz) <= 0 && @@ -440,7 +450,7 @@ static int tree_iterator__current( iterator__clear_entry(entry); - if (!(te = tree_iterator__get_tree_entry(tf, NULL, tf->current))) + if (!(te = tree_iterator__tree_entry_by_index(tf, tf->current))) return 0; ti->entry.mode = te->attr; @@ -596,12 +606,7 @@ int git_iterator_for_tree( if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0) goto fail; - - if (iterator__ignore_case(ti)) { - ti->strcomp = git__strcasecmp; ti->strncomp = git__strncasecmp; - } else { - ti->strcomp = git__strcmp; ti->strncomp = git__strncmp; - } + ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp; if ((error = tree_iterator__create_top_frame(ti, tree)) < 0 || (error = tree_iterator__push_frame(ti)) < 0) /* expand top right now */ @@ -1284,9 +1289,8 @@ int git_iterator_current_tree_entry( if (iter->type != GIT_ITERATOR_TYPE_TREE) *tree_entry = NULL; else { - tree_iterator *ti = (tree_iterator *)iter; - *tree_entry = - tree_iterator__get_tree_entry(ti->head, NULL, ti->head->current); + tree_iterator_frame *tf = ((tree_iterator *)iter)->head; + *tree_entry = tree_iterator__tree_entry_by_index(tf, tf->current); } return 0; @@ -1312,7 +1316,7 @@ int git_iterator_current_parent_tree( while (*scan) { /* get entry of this parent that child is currently on */ if (!(tf = tf->child) || - !(te = tree_iterator__get_tree_entry(tf, NULL, tf->current)) || + !(te = tree_iterator__tree_entry_by_index(tf, tf->current)) || ti->strncomp(scan, te->filename, te->filename_len) != 0) return 0; diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index e7498f67d..44016bb59 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -501,6 +501,61 @@ void test_repo_iterator__tree_case_conflicts(void) git_tree_free(tree); } +void test_repo_iterator__tree_case_conflicts_2(void) +{ + const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6"; + git_tree *tree; + git_oid blob_id, Ab_id, biga_id, littlea_id, tree_id; + git_iterator *i; + const char *expect_cs[] = { + "A/a", "A/b/1", "A/c", "a/C", "a/a", "a/b" }; + const char *expect_ci[] = { + "A/a", "a/b", "A/b/1", "A/c" }; + const char *expect_cs_trees[] = { + "A/", "A/a", "A/b/", "A/b/1", "A/c", "a/", "a/C", "a/a", "a/b" }; + const char *expect_ci_trees[] = { + "A/", "A/a", "a/b", "A/b/", "A/b/1", "A/c" }; + + g_repo = cl_git_sandbox_init("icase"); + + cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */ + + /* create: A/a A/b/1 A/c a/a a/b a/C */ + build_test_tree(&Ab_id, g_repo, "b/1/", &blob_id); + build_test_tree( + &biga_id, g_repo, "b/a/,t/b/,b/c/", &blob_id, &Ab_id, &blob_id); + build_test_tree( + &littlea_id, g_repo, "b/a/,b/b/,b/C/", &blob_id, &blob_id, &blob_id); + build_test_tree( + &tree_id, g_repo, "t/A/,t/a/", &biga_id, &littlea_id); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); + expect_iterator_items(i, 6, expect_cs, 6, expect_cs); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); + expect_iterator_items(i, 4, expect_ci, 4, expect_ci); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE | + GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 9, expect_cs_trees, 9, expect_cs_trees); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_IGNORE_CASE | + GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + expect_iterator_items(i, 6, expect_ci_trees, 6, expect_ci_trees); + git_iterator_free(i); + + git_tree_free(tree); +} + void test_repo_iterator__workdir(void) { git_iterator *i; From a5eea2d7b760ebef0d2f397d763ec8eff32f38cd Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 11 Mar 2013 11:31:50 -0700 Subject: [PATCH 10/11] Stabilize order for equiv tree iterator entries Given a group of case-insensitively equivalent tree iterator entries, this ensures that the case-sensitively first trees will be used as the representative items. I.e. if you have conflicting entries "A/B/x", "a/b/x", and "A/b/x", this change ensures that the earliest entry "A/B/x" will be returned. The actual choice is not that important, but it is nice to have it stable and to have it been either the first or last item, as opposed to a random item from within the equivalent span. --- src/iterator.c | 18 +++++- tests-clar/repo/iterator.c | 110 ++++++++++++++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 12 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index fb76085cd..e6e0ea481 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -285,9 +285,21 @@ static int tree_iterator__tree_entry_cmp( static int tree_iterator__entry_cmp(const void *a, const void *b, void *p) { - return tree_iterator__tree_entry_cmp( - tree_iterator__tree_entry(p, a), tree_iterator__tree_entry(p, b), - git__strncasecmp); + const tree_iterator_entry *ae = a, *be = b; + const git_tree_entry *ate = tree_iterator__tree_entry(p, ae); + const git_tree_entry *bte = tree_iterator__tree_entry(p, be); + int cmp = tree_iterator__tree_entry_cmp(ate, bte, git__strncasecmp); + + /* stabilize sort order among equivalent names */ + if (!cmp) { + cmp = (ae->parent_entry_index < be->parent_entry_index) ? -1 : + (ae->parent_entry_index > be->parent_entry_index) ? 1 : 0; + if (!cmp) + cmp = (ae->parent_tree_index < be->parent_tree_index) ? -1 : + (ae->parent_tree_index > be->parent_tree_index) ? 1 : 0; + } + + return cmp; } static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf) diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 44016bb59..00123196b 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -447,7 +447,7 @@ static void build_test_tree( git_buf_free(&name); } -void test_repo_iterator__tree_case_conflicts(void) +void test_repo_iterator__tree_case_conflicts_0(void) { const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6"; git_tree *tree; @@ -468,11 +468,11 @@ void test_repo_iterator__tree_case_conflicts(void) /* create tree with: A/1.file, A/3.file, a/2.file, a/4.file */ build_test_tree( - &biga_id, g_repo, "b/1.file/,b/3.file/", &blob_id, &blob_id); + &biga_id, g_repo, "b|1.file|,b|3.file|", &blob_id, &blob_id); build_test_tree( - &littlea_id, g_repo, "b/2.file/,b/4.file/", &blob_id, &blob_id); + &littlea_id, g_repo, "b|2.file|,b|4.file|", &blob_id, &blob_id); build_test_tree( - &tree_id, g_repo, "t/A/,t/a/", &biga_id, &littlea_id); + &tree_id, g_repo, "t|A|,t|a|", &biga_id, &littlea_id); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); @@ -501,7 +501,7 @@ void test_repo_iterator__tree_case_conflicts(void) git_tree_free(tree); } -void test_repo_iterator__tree_case_conflicts_2(void) +void test_repo_iterator__tree_case_conflicts_1(void) { const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6"; git_tree *tree; @@ -521,13 +521,13 @@ void test_repo_iterator__tree_case_conflicts_2(void) cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */ /* create: A/a A/b/1 A/c a/a a/b a/C */ - build_test_tree(&Ab_id, g_repo, "b/1/", &blob_id); + build_test_tree(&Ab_id, g_repo, "b|1|", &blob_id); build_test_tree( - &biga_id, g_repo, "b/a/,t/b/,b/c/", &blob_id, &Ab_id, &blob_id); + &biga_id, g_repo, "b|a|,t|b|,b|c|", &blob_id, &Ab_id, &blob_id); build_test_tree( - &littlea_id, g_repo, "b/a/,b/b/,b/C/", &blob_id, &blob_id, &blob_id); + &littlea_id, g_repo, "b|a|,b|b|,b|C|", &blob_id, &blob_id, &blob_id); build_test_tree( - &tree_id, g_repo, "t/A/,t/a/", &biga_id, &littlea_id); + &tree_id, g_repo, "t|A|,t|a|", &biga_id, &littlea_id); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); @@ -556,6 +556,98 @@ void test_repo_iterator__tree_case_conflicts_2(void) git_tree_free(tree); } +void test_repo_iterator__tree_case_conflicts_2(void) +{ + const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6"; + git_tree *tree; + git_oid blob_id, d1, d2, c1, c2, b1, b2, a1, a2, tree_id; + git_iterator *i; + const char *expect_cs[] = { + "A/B/C/D/16", "A/B/C/D/foo", "A/B/C/d/15", "A/B/C/d/FOO", + "A/B/c/D/14", "A/B/c/D/foo", "A/B/c/d/13", "A/B/c/d/FOO", + "A/b/C/D/12", "A/b/C/D/foo", "A/b/C/d/11", "A/b/C/d/FOO", + "A/b/c/D/10", "A/b/c/D/foo", "A/b/c/d/09", "A/b/c/d/FOO", + "a/B/C/D/08", "a/B/C/D/foo", "a/B/C/d/07", "a/B/C/d/FOO", + "a/B/c/D/06", "a/B/c/D/foo", "a/B/c/d/05", "a/B/c/d/FOO", + "a/b/C/D/04", "a/b/C/D/foo", "a/b/C/d/03", "a/b/C/d/FOO", + "a/b/c/D/02", "a/b/c/D/foo", "a/b/c/d/01", "a/b/c/d/FOO", }; + const char *expect_ci[] = { + "a/b/c/d/01", "a/b/c/D/02", "a/b/C/d/03", "a/b/C/D/04", + "a/B/c/d/05", "a/B/c/D/06", "a/B/C/d/07", "a/B/C/D/08", + "A/b/c/d/09", "A/b/c/D/10", "A/b/C/d/11", "A/b/C/D/12", + "A/B/c/d/13", "A/B/c/D/14", "A/B/C/d/15", "A/B/C/D/16", + "A/B/C/D/foo", }; + const char *expect_ci_trees[] = { + "A/", "A/B/", "A/B/C/", "A/B/C/D/", + "a/b/c/d/01", "a/b/c/D/02", "a/b/C/d/03", "a/b/C/D/04", + "a/B/c/d/05", "a/B/c/D/06", "a/B/C/d/07", "a/B/C/D/08", + "A/b/c/d/09", "A/b/c/D/10", "A/b/C/d/11", "A/b/C/D/12", + "A/B/c/d/13", "A/B/c/D/14", "A/B/C/d/15", "A/B/C/D/16", + "A/B/C/D/foo", }; + + g_repo = cl_git_sandbox_init("icase"); + + cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */ + + build_test_tree(&d1, g_repo, "b|16|,b|foo|", &blob_id, &blob_id); + build_test_tree(&d2, g_repo, "b|15|,b|FOO|", &blob_id, &blob_id); + build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2); + build_test_tree(&d1, g_repo, "b|14|,b|foo|", &blob_id, &blob_id); + build_test_tree(&d2, g_repo, "b|13|,b|FOO|", &blob_id, &blob_id); + build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2); + build_test_tree(&b1, g_repo, "t|C|,t|c|", &c1, &c2); + + build_test_tree(&d1, g_repo, "b|12|,b|foo|", &blob_id, &blob_id); + build_test_tree(&d2, g_repo, "b|11|,b|FOO|", &blob_id, &blob_id); + build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2); + build_test_tree(&d1, g_repo, "b|10|,b|foo|", &blob_id, &blob_id); + build_test_tree(&d2, g_repo, "b|09|,b|FOO|", &blob_id, &blob_id); + build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2); + build_test_tree(&b2, g_repo, "t|C|,t|c|", &c1, &c2); + + build_test_tree(&a1, g_repo, "t|B|,t|b|", &b1, &b2); + + build_test_tree(&d1, g_repo, "b|08|,b|foo|", &blob_id, &blob_id); + build_test_tree(&d2, g_repo, "b|07|,b|FOO|", &blob_id, &blob_id); + build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2); + build_test_tree(&d1, g_repo, "b|06|,b|foo|", &blob_id, &blob_id); + build_test_tree(&d2, g_repo, "b|05|,b|FOO|", &blob_id, &blob_id); + build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2); + build_test_tree(&b1, g_repo, "t|C|,t|c|", &c1, &c2); + + build_test_tree(&d1, g_repo, "b|04|,b|foo|", &blob_id, &blob_id); + build_test_tree(&d2, g_repo, "b|03|,b|FOO|", &blob_id, &blob_id); + build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2); + build_test_tree(&d1, g_repo, "b|02|,b|foo|", &blob_id, &blob_id); + build_test_tree(&d2, g_repo, "b|01|,b|FOO|", &blob_id, &blob_id); + build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2); + build_test_tree(&b2, g_repo, "t|C|,t|c|", &c1, &c2); + + build_test_tree(&a2, g_repo, "t|B|,t|b|", &b1, &b2); + + build_test_tree(&tree_id, g_repo, "t/A/,t/a/", &a1, &a2); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); + expect_iterator_items(i, 32, expect_cs, 32, expect_cs); + git_iterator_free(i); + + cl_git_pass(git_iterator_for_tree( + &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL)); + expect_iterator_items(i, 17, expect_ci, 17, expect_ci); + 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, 21, expect_ci_trees, 21, expect_ci_trees); + git_iterator_free(i); + + git_tree_free(tree); +} + void test_repo_iterator__workdir(void) { git_iterator *i; From 62beacd300a6d3c62943723928f45ef852485e62 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Mon, 11 Mar 2013 16:43:58 -0700 Subject: [PATCH 11/11] Sorting function cleanup and MinGW fix Clean up some sorting function stuff including fixing qsort_r on MinGW, common function pointer type for comparison, and basic insertion sort implementation (which we, regrettably, fall back on for MinGW). --- src/tsort.c | 8 ++++---- src/util.c | 34 ++++++++++++++++++++++++++++------ src/util.h | 12 +++++++----- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/tsort.c b/src/tsort.c index 97473be91..4885e435b 100644 --- a/src/tsort.c +++ b/src/tsort.c @@ -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]; diff --git a/src/util.c b/src/util.c index 561288f72..102bbaeb3 100644 --- a/src/util.c +++ b/src/util.c @@ -610,7 +610,7 @@ size_t git__unescape(char *str) #if defined(GIT_WIN32) || defined(BSD) typedef struct { - git__qsort_r_cmp cmp; + git__sort_r_cmp cmp; void *payload; } git__qsort_r_glue; @@ -623,17 +623,39 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp( #endif void git__qsort_r( - void *els, size_t nel, size_t elsize, git__qsort_r_cmp cmp, void *payload) + void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload) { -#if defined(GIT_WIN32) +#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); -#else -#if defined(BSD) +#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 -#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); } diff --git a/src/util.h b/src/util.h index e77f17efc..c0f271997 100644 --- a/src/util.h +++ b/src/util.h @@ -146,15 +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); - -typedef int (*git__qsort_r_cmp)(const void *a, const void *b, 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__qsort_r_cmp cmp, void *payload); + 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