From bcbb1e201b77973a49011e1c85c53a5808f6f3a8 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sat, 5 Jan 2013 20:58:25 +0100 Subject: [PATCH 01/11] status: Enhance git_status_file() test coverage --- tests-clar/status/worktree.c | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 6786b91ff..62f3f3cf3 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -580,3 +580,92 @@ void test_status_worktree__conflicted_item(void) git_index_free(index); } +static void stage_and_commit(git_repository *repo, const char *path) +{ + git_oid tree_oid, commit_oid; + git_tree *tree; + git_signature *signature; + git_index *index; + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_add_from_workdir(index, path)); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_index_write_tree(&tree_oid, index)); + git_index_free(index); + + cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid)); + + cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); + + cl_git_pass(git_commit_create_v( + &commit_oid, + repo, + "HEAD", + signature, + signature, + NULL, + "Initial commit\n\0", + tree, + 0)); + + git_tree_free(tree); + git_signature_free(signature); +} + +static void assert_ignore_case( + bool should_ignore_case, + int expected_lower_cased_file_status, + int expected_camel_cased_file_status) +{ + git_config *config; + unsigned int status; + git_buf lower_case_path = GIT_BUF_INIT, + camel_case_path = GIT_BUF_INIT; + + git_repository *repo, *repo2; + + repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt"); + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_bool(config, "core.ignorecase", should_ignore_case)); + git_config_free(config); + + cl_git_pass(git_buf_joinpath(&lower_case_path, + git_repository_workdir(repo), "plop")); + + cl_git_mkfile(git_buf_cstr(&lower_case_path), ""); + + stage_and_commit(repo, "plop"); + + cl_git_pass(git_repository_open(&repo2, "./empty_standard_repo")); + + cl_git_pass(git_buf_joinpath(&camel_case_path, + git_repository_workdir(repo), "Plop")); + + cl_git_pass(git_status_file(&status, repo2, "plop")); + cl_assert_equal_i(GIT_STATUS_CURRENT, status); + + cl_git_pass(p_rename(git_buf_cstr(&lower_case_path), git_buf_cstr(&camel_case_path))); + + cl_git_pass(git_status_file(&status, repo2, "plop")); + cl_assert_equal_i(expected_lower_cased_file_status, status); + + cl_git_pass(git_status_file(&status, repo2, "Plop")); + cl_assert_equal_i(expected_camel_cased_file_status, status); + + git_repository_free(repo2); + git_buf_free(&lower_case_path); + git_buf_free(&camel_case_path); +} + +void test_status_worktree__file_status_honors_ignorecase_conf_setting_set_to_true(void) +{ + assert_ignore_case(true, GIT_STATUS_CURRENT, GIT_STATUS_CURRENT); +} + +void test_status_worktree__file_status_honors_ignorecase_conf_setting_set_to_false(void) +{ + assert_ignore_case(false, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_NEW); +} From facc0650b12655c9637732bb992d1053cd946057 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 8 Jan 2013 13:27:25 -0800 Subject: [PATCH 02/11] Simplify git_diff__paired_foreach icase handling --- src/diff_output.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/diff_output.c b/src/diff_output.c index e79bf30d2..d271e8a8e 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -1666,32 +1666,28 @@ int git_diff__paired_foreach( int cmp; git_diff_delta *i2h, *w2i; size_t i, j, i_max, j_max; - bool icase = false; + int (*strcomp)(const char *, const char *); i_max = idx2head ? idx2head->deltas.length : 0; j_max = wd2idx ? wd2idx->deltas.length : 0; - if (idx2head && wd2idx && - (0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) || - 0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE))) - { - /* Then use the ignore-case sorter... */ - icase = true; + /* Get appropriate strcmp function */ + strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL; - /* and assert that both are ignore-case sorted. If this function - * ever needs to support merge joining result sets that are not sorted - * by the same function, then it will need to be extended to do a spool - * and sort on one of the results before merge joining */ - assert(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) && - 0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)); - } + /* Assert both iterators use matching ignore-case. If this function ever + * supports merging diffs that are not sorted by the same function, then + * it will need to spool and sort on one of the results before merging + */ + if (idx2head && wd2idx) { + assert(idx2head->strcomp == wd2idx->strcomp); + } for (i = 0, j = 0; i < i_max || j < j_max; ) { i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL; w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL; cmp = !w2i ? -1 : !i2h ? 1 : - STRCMP_CASESELECT(icase, i2h->old_file.path, w2i->old_file.path); + strcomp(i2h->old_file.path, w2i->old_file.path); if (cmp < 0) { if (cb(i2h, NULL, payload)) From 4b181037553601a0747ad39ccdd85ebd3b184055 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 8 Jan 2013 13:39:15 -0800 Subject: [PATCH 03/11] Minor iterator API cleanups In preparation for further iterator changes, this cleans up a few small things in the iterator API: * removed the git_iterator_for_repo_index_range API * made git_iterator_free not be inlined * minor param name and test function name tweaks --- src/iterator.c | 30 +++++++++++---------- src/iterator.h | 52 +++++++++--------------------------- src/submodule.c | 4 ++- tests-clar/diff/iterator.c | 5 +++- tests-clar/status/worktree.c | 16 +++++------ 5 files changed, 42 insertions(+), 65 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index 08e2e79e4..3d75dd8b5 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -485,20 +485,6 @@ int git_iterator_for_index_range( return 0; } -int git_iterator_for_repo_index_range( - git_iterator **iter, - git_repository *repo, - const char *start, - const char *end) -{ - int error; - git_index *index; - - if ((error = git_repository_index__weakptr(&index, repo)) < 0) - return error; - - return git_iterator_for_index_range(iter, index, start, end); -} typedef struct workdir_iterator_frame workdir_iterator_frame; struct workdir_iterator_frame { @@ -988,6 +974,22 @@ fail: return -1; } + +void git_iterator_free(git_iterator *iter) +{ + if (iter == NULL) + return; + + iter->cb->free(iter); + + git__free(iter->start); + git__free(iter->end); + + memset(iter, 0, sizeof(*iter)); + + git__free(iter); +} + git_index *git_iterator_index_get_index(git_iterator *iter) { if (iter->type == GIT_ITERATOR_INDEX) diff --git a/src/iterator.h b/src/iterator.h index 8a4356e3e..727da97b3 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -44,47 +44,34 @@ struct git_iterator { bool ignore_case; }; -extern int git_iterator_for_nothing(git_iterator **iter); +extern int git_iterator_for_nothing(git_iterator **out); extern int git_iterator_for_tree_range( - git_iterator **iter, git_tree *tree, - const char *start, const char *end); + git_iterator **out, git_tree *tree, const char *start, const char *end); -GIT_INLINE(int) git_iterator_for_tree( - git_iterator **iter, git_tree *tree) +GIT_INLINE(int) git_iterator_for_tree(git_iterator **out, git_tree *tree) { - return git_iterator_for_tree_range(iter, tree, NULL, NULL); + return git_iterator_for_tree_range(out, tree, NULL, NULL); } extern int git_iterator_for_index_range( - git_iterator **iter, git_index *index, const char *start, const char *end); + git_iterator **out, git_index *index, const char *start, const char *end); -GIT_INLINE(int) git_iterator_for_index( - git_iterator **iter, git_index *index) +GIT_INLINE(int) git_iterator_for_index(git_iterator **out, git_index *index) { - return git_iterator_for_index_range(iter, index, NULL, NULL); -} - -extern int git_iterator_for_repo_index_range( - git_iterator **iter, git_repository *repo, - const char *start, const char *end); - -GIT_INLINE(int) git_iterator_for_repo_index( - git_iterator **iter, git_repository *repo) -{ - return git_iterator_for_repo_index_range(iter, repo, NULL, NULL); + return git_iterator_for_index_range(out, index, NULL, NULL); } extern int git_iterator_for_workdir_range( - git_iterator **iter, git_repository *repo, - const char *start, const char *end); + git_iterator **out, git_repository *repo, const char *start, const char *end); -GIT_INLINE(int) git_iterator_for_workdir( - git_iterator **iter, git_repository *repo) +GIT_INLINE(int) git_iterator_for_workdir(git_iterator **out, git_repository *repo) { - return git_iterator_for_workdir_range(iter, repo, NULL, NULL); + return git_iterator_for_workdir_range(out, repo, NULL, NULL); } +extern void git_iterator_free(git_iterator *iter); + /* Spool all iterator values, resort with alternative ignore_case value * and replace callbacks with spoolandsort alternates. */ @@ -130,21 +117,6 @@ GIT_INLINE(int) git_iterator_reset( return iter->cb->reset(iter, start, end); } -GIT_INLINE(void) git_iterator_free(git_iterator *iter) -{ - if (iter == NULL) - return; - - iter->cb->free(iter); - - git__free(iter->start); - git__free(iter->end); - - memset(iter, 0, sizeof(*iter)); - - git__free(iter); -} - GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter) { return iter->type; diff --git a/src/submodule.c b/src/submodule.c index a72326602..5283322f2 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1130,10 +1130,12 @@ static int load_submodule_config_from_index( git_repository *repo, git_oid *gitmodules_oid) { int error; + git_index *index; git_iterator *i; const git_index_entry *entry; - if ((error = git_iterator_for_repo_index(&i, repo)) < 0) + if ((error = git_repository_index__weakptr(&index, repo)) < 0 || + (error = git_iterator_for_index(&i, index)) < 0) return error; error = git_iterator_current(i, &entry); diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index b5790632d..de083ea36 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -355,12 +355,14 @@ static void index_iterator_test( const char **expected_names, const char **expected_oids) { + git_index *index; git_iterator *i; const git_index_entry *entry; int count = 0; git_repository *repo = cl_git_sandbox_init(sandbox); - cl_git_pass(git_iterator_for_repo_index_range(&i, repo, start, end)); + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_iterator_for_index_range(&i, index, start, end)); cl_git_pass(git_iterator_current(i, &entry)); while (entry != NULL) { @@ -378,6 +380,7 @@ static void index_iterator_test( } git_iterator_free(i); + git_index_free(index); cl_assert_equal_i(expected_count, count); } diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 62f3f3cf3..ead1bc734 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -620,11 +620,9 @@ static void assert_ignore_case( { git_config *config; unsigned int status; - git_buf lower_case_path = GIT_BUF_INIT, - camel_case_path = GIT_BUF_INIT; - + git_buf lower_case_path = GIT_BUF_INIT, camel_case_path = GIT_BUF_INIT; git_repository *repo, *repo2; - + repo = cl_git_sandbox_init("empty_standard_repo"); cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt"); @@ -641,12 +639,12 @@ static void assert_ignore_case( cl_git_pass(git_repository_open(&repo2, "./empty_standard_repo")); - cl_git_pass(git_buf_joinpath(&camel_case_path, - git_repository_workdir(repo), "Plop")); - cl_git_pass(git_status_file(&status, repo2, "plop")); cl_assert_equal_i(GIT_STATUS_CURRENT, status); + cl_git_pass(git_buf_joinpath(&camel_case_path, + git_repository_workdir(repo), "Plop")); + cl_git_pass(p_rename(git_buf_cstr(&lower_case_path), git_buf_cstr(&camel_case_path))); cl_git_pass(git_status_file(&status, repo2, "plop")); @@ -660,12 +658,12 @@ static void assert_ignore_case( git_buf_free(&camel_case_path); } -void test_status_worktree__file_status_honors_ignorecase_conf_setting_set_to_true(void) +void test_status_worktree__file_status_honors_core_ignorecase_true(void) { assert_ignore_case(true, GIT_STATUS_CURRENT, GIT_STATUS_CURRENT); } -void test_status_worktree__file_status_honors_ignorecase_conf_setting_set_to_false(void) +void test_status_worktree__file_status_honors_core_ignorecase_false(void) { assert_ignore_case(false, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_NEW); } From 5c8bb98ce9c4e5bb6527c8ffc274c8b3e0755fa7 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 8 Jan 2013 13:45:06 -0800 Subject: [PATCH 04/11] Fix err msg for ambiguous path in git_status_file Returning GIT_EAMBIGUOUS from inside the status callback gets overridden with GIT_EUSER. `git_status_file` accounted for this via the callback payload, but was allowing the error message to be cleared. Move the `giterr_set` call outside the callback to where the EUSER case was being dealt with. --- src/status.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/status.c b/src/status.c index 777d8502b..5b89e8c0d 100644 --- a/src/status.c +++ b/src/status.c @@ -208,9 +208,8 @@ static int get_one_status(const char *path, unsigned int status, void *data) if (sfi->count > 1 || (strcmp(sfi->expected, path) != 0 && - p_fnmatch(sfi->expected, path, 0) != 0)) { - giterr_set(GITERR_INVALID, - "Ambiguous path '%s' given to git_status_file", sfi->expected); + p_fnmatch(sfi->expected, path, 0) != 0)) + { sfi->ambiguous = true; return GIT_EAMBIGUOUS; } @@ -242,8 +241,11 @@ int git_status_file( error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi); - if (error < 0 && sfi.ambiguous) + if (error < 0 && sfi.ambiguous) { + giterr_set(GITERR_INVALID, + "Ambiguous path '%s' given to git_status_file", sfi.expected); error = GIT_EAMBIGUOUS; + } if (!error && !sfi.count) { git_buf full = GIT_BUF_INIT; From 134d8c918c3430b19b75f45b1e490ce2aae526ff Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 8 Jan 2013 15:53:13 -0800 Subject: [PATCH 05/11] Update iterator API with flags for ignore_case This changes the iterator API so that flags can be passed in to the constructor functions to control the ignore_case behavior. At this point, the flags are not supported on tree iterators (i.e. there is no functional change over the old API), but the API changes are all made to accomodate this. By the way, I went with a flags parameter because in the future I have a couple of other ideas for iterator flags that will make it easier to fix some diff/status/checkout bugs. --- src/checkout.c | 16 +++-- src/diff.c | 24 ++++---- src/diff_output.c | 10 ++-- src/iterator.c | 118 ++++++++++++++++++++++++------------- src/iterator.h | 66 ++++++++++++++++----- tests-clar/diff/iterator.c | 12 ++-- 6 files changed, 165 insertions(+), 81 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index d5a471d0c..411bf3be7 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -224,7 +224,7 @@ static int checkout_action_wd_only( if (!git_pathspec_match_path( pathspec, wd->path, (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0, - workdir->ignore_case)) + git_iterator_ignore_case(workdir))) return 0; /* check if item is tracked in the index but not in the checkout diff */ @@ -1130,7 +1130,7 @@ static int checkout_data_init( if ((error = git_config_refresh(cfg)) < 0) goto cleanup; - if (git_iterator_inner_type(target) == GIT_ITERATOR_INDEX) { + 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); GIT_REFCOUNT_INC(data->index); @@ -1208,6 +1208,7 @@ int git_checkout_iterator( git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT; uint32_t *actions = NULL; size_t *counts = NULL; + git_iterator_flag_t iterflags = 0; /* initialize structures and options */ error = checkout_data_init(&data, target, opts); @@ -1228,18 +1229,21 @@ int git_checkout_iterator( diff_opts.pathspec = data.opts.paths; /* set up iterators */ + + iterflags = git_iterator_ignore_case(target) ? + GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; + if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_workdir_range( - &workdir, data.repo, data.pfx, data.pfx)) < 0 || + &workdir, data.repo, iterflags, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_tree_range( - &baseline, data.opts.baseline, data.pfx, data.pfx)) < 0) + &baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0) goto cleanup; /* Handle case insensitivity for baseline if necessary */ - if (workdir->ignore_case && !baseline->ignore_case) { + if (git_iterator_ignore_case(workdir) != git_iterator_ignore_case(baseline)) if ((error = git_iterator_spoolandsort_push(baseline, true)) < 0) goto cleanup; - } /* 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 5e34b9221..4b60935f0 100644 --- a/src/diff.c +++ b/src/diff.c @@ -418,7 +418,7 @@ static int maybe_modified( git_delta_t status = GIT_DELTA_MODIFIED; unsigned int omode = oitem->mode; unsigned int nmode = nitem->mode; - bool new_is_workdir = (new_iter->type == GIT_ITERATOR_WORKDIR); + bool new_is_workdir = (new_iter->type == GIT_ITERATOR_TYPE_WORKDIR); GIT_UNUSED(old_iter); @@ -556,7 +556,9 @@ static int diff_list_init_from_iterators( /* Use case-insensitive compare if either iterator has * the ignore_case bit set */ - if (!old_iter->ignore_case && !new_iter->ignore_case) { + if (!git_iterator_ignore_case(old_iter) && + !git_iterator_ignore_case(new_iter)) + { diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE; diff->strcomp = git__strcmp; @@ -714,7 +716,7 @@ int git_diff__from_iterators( else if (git_iterator_current_is_ignored(new_iter)) delta_type = GIT_DELTA_IGNORED; - else if (new_iter->type != GIT_ITERATOR_WORKDIR) + else if (new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) delta_type = GIT_DELTA_ADDED; if (diff_delta__from_one(diff, delta_type, nitem) < 0) @@ -786,8 +788,8 @@ int git_diff_tree_to_tree( assert(diff && repo); DIFF_FROM_ITERATORS( - git_iterator_for_tree_range(&a, old_tree, pfx, pfx), - git_iterator_for_tree_range(&b, new_tree, pfx, pfx) + git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx), + git_iterator_for_tree_range(&b, new_tree, 0, pfx, pfx) ); return error; @@ -808,8 +810,8 @@ int git_diff_tree_to_index( return error; DIFF_FROM_ITERATORS( - git_iterator_for_tree_range(&a, old_tree, pfx, pfx), - git_iterator_for_index_range(&b, index, pfx, pfx) + git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx), + git_iterator_for_index_range(&b, index, 0, pfx, pfx) ); return error; @@ -829,8 +831,8 @@ int git_diff_index_to_workdir( return error; DIFF_FROM_ITERATORS( - git_iterator_for_index_range(&a, index, pfx, pfx), - git_iterator_for_workdir_range(&b, repo, pfx, pfx) + git_iterator_for_index_range(&a, index, 0, pfx, pfx), + git_iterator_for_workdir_range(&b, repo, 0, pfx, pfx) ); return error; @@ -848,8 +850,8 @@ int git_diff_tree_to_workdir( assert(diff && repo); DIFF_FROM_ITERATORS( - git_iterator_for_tree_range(&a, old_tree, pfx, pfx), - git_iterator_for_workdir_range(&b, repo, pfx, pfx) + git_iterator_for_tree_range(&a, old_tree, 0, pfx, pfx), + git_iterator_for_workdir_range(&b, repo, 0, pfx, pfx) ); return error; diff --git a/src/diff_output.c b/src/diff_output.c index d271e8a8e..8a7a7a2a1 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -495,7 +495,7 @@ static void diff_patch_init( patch->old_src = patch->diff->old_src; patch->new_src = patch->diff->new_src; } else { - patch->old_src = patch->new_src = GIT_ITERATOR_TREE; + patch->old_src = patch->new_src = GIT_ITERATOR_TYPE_TREE; } } @@ -578,7 +578,7 @@ static int diff_patch_load( */ if ((delta->old_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 && - patch->old_src == GIT_ITERATOR_WORKDIR) { + patch->old_src == GIT_ITERATOR_TYPE_WORKDIR) { if ((error = get_workdir_content( ctxt, delta, &delta->old_file, &patch->old_data)) < 0) goto cleanup; @@ -587,7 +587,7 @@ static int diff_patch_load( } if ((delta->new_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 && - patch->new_src == GIT_ITERATOR_WORKDIR) { + patch->new_src == GIT_ITERATOR_TYPE_WORKDIR) { if ((error = get_workdir_content( ctxt, delta, &delta->new_file, &patch->new_data)) < 0) goto cleanup; @@ -596,7 +596,7 @@ static int diff_patch_load( } if ((delta->old_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 && - patch->old_src != GIT_ITERATOR_WORKDIR) { + patch->old_src != GIT_ITERATOR_TYPE_WORKDIR) { if ((error = get_blob_content( ctxt, delta, &delta->old_file, &patch->old_data, &patch->old_blob)) < 0) @@ -606,7 +606,7 @@ static int diff_patch_load( } if ((delta->new_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 && - patch->new_src != GIT_ITERATOR_WORKDIR) { + patch->new_src != GIT_ITERATOR_TYPE_WORKDIR) { if ((error = get_blob_content( ctxt, delta, &delta->new_file, &patch->new_data, &patch->new_blob)) < 0) diff --git a/src/iterator.c b/src/iterator.c index 3d75dd8b5..a9df39bb8 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -24,12 +24,11 @@ #define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \ (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \ GITERR_CHECK_ALLOC(P); \ - (P)->base.type = GIT_ITERATOR_ ## NAME_UC; \ + (P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \ (P)->base.cb = &(P)->cb; \ ITERATOR_SET_CB(P,NAME_LC); \ (P)->base.start = start ? git__strdup(start) : NULL; \ (P)->base.end = end ? git__strdup(end) : NULL; \ - (P)->base.ignore_case = false; \ if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \ git__free(P); return -1; } \ } while (0) @@ -54,6 +53,31 @@ static int iterator__reset_range( return 0; } +static int iterator_update_ignore_case( + git_iterator *iter, + git_iterator_flag_t flags) +{ + int error = 0, ignore_case = -1; + + if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0) + ignore_case = true; + else if ((flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0) + ignore_case = false; + else { + git_index *index; + + if (!(error = git_repository_index__weakptr(&index, iter->repo))) + ignore_case = (index->ignore_case != false); + } + + if (ignore_case > 0) + iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE); + else if (ignore_case == 0) + iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE); + + return error; +} + static int empty_iterator__no_item( git_iterator *iter, const git_index_entry **entry) { @@ -91,13 +115,14 @@ typedef struct { git_iterator_callbacks cb; } empty_iterator; -int git_iterator_for_nothing(git_iterator **iter) +int git_iterator_for_nothing(git_iterator **iter, git_iterator_flag_t flags) { empty_iterator *i = git__calloc(1, sizeof(empty_iterator)); GITERR_CHECK_ALLOC(i); - i->base.type = GIT_ITERATOR_EMPTY; + 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; @@ -349,6 +374,7 @@ static int tree_iterator__reset( int git_iterator_for_tree_range( git_iterator **iter, git_tree *tree, + git_iterator_flag_t flags, const char *start, const char *end) { @@ -356,7 +382,7 @@ int git_iterator_for_tree_range( tree_iterator *ti; if (tree == NULL) - return git_iterator_for_nothing(iter); + return git_iterator_for_nothing(iter, flags); if ((error = git_tree__dup(&tree, tree)) < 0) return error; @@ -364,13 +390,23 @@ int git_iterator_for_tree_range( ITERATOR_BASE_INIT(ti, tree, TREE); ti->base.repo = git_tree_owner(tree); + + if ((error = iterator_update_ignore_case((git_iterator *)ti, flags)) < 0) + goto fail; + + /* TODO: implement icase support natively in tree iterators */ + ti->base.flags = (ti->base.flags & ~GIT_ITERATOR_IGNORE_CASE); + ti->stack = ti->tail = tree_iterator__alloc_frame(tree, ti->base.start); if ((error = tree_iterator__expand_tree(ti)) < 0) - git_iterator_free((git_iterator *)ti); - else - *iter = (git_iterator *)ti; + goto fail; + *iter = (git_iterator *)ti; + return 0; + +fail: + git_iterator_free((git_iterator *)ti); return error; } @@ -466,15 +502,19 @@ static void index_iterator__free(git_iterator *self) int git_iterator_for_index_range( git_iterator **iter, git_index *index, + git_iterator_flag_t flags, const char *start, const char *end) { index_iterator *ii; + GIT_UNUSED(flags); + ITERATOR_BASE_INIT(ii, index, INDEX); ii->base.repo = git_index_owner(index); - ii->base.ignore_case = index->ignore_case; + if (index->ignore_case) + ii->base.flags |= GIT_ITERATOR_IGNORE_CASE; ii->index = index; GIT_REFCOUNT_INC(index); @@ -530,7 +570,8 @@ static workdir_iterator_frame *workdir_iterator__alloc_frame( workdir_iterator *wi) { workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame)); - git_vector_cmp entry_compare = CASESELECT(wi->base.ignore_case, + git_vector_cmp entry_compare = CASESELECT( + (wi->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0, git_path_with_stat_cmp_icase, git_path_with_stat_cmp); if (wf == NULL) @@ -592,7 +633,8 @@ 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, wi->base.ignore_case, + wi->path.ptr, wi->root_len, + (wi->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0, wi->base.start, wi->base.end, &wf->entries); if (error < 0 || wf->entries.length == 0) { @@ -775,12 +817,12 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) int git_iterator_for_workdir_range( git_iterator **iter, git_repository *repo, + git_iterator_flag_t flags, const char *start, const char *end) { int error; workdir_iterator *wi; - git_index *index; assert(iter && repo); @@ -791,13 +833,8 @@ int git_iterator_for_workdir_range( ITERATOR_BASE_INIT(wi, workdir, WORKDIR); wi->base.repo = repo; - if ((error = git_repository_index__weakptr(&index, repo)) < 0) { - git_iterator_free((git_iterator *)wi); - return error; - } - - /* Match ignore_case flag for iterator to that of the index */ - wi->base.ignore_case = index->ignore_case; + if ((error = iterator_update_ignore_case((git_iterator *)wi, flags)) < 0) + goto fail; if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 || git_path_to_dir(&wi->path) < 0 || @@ -808,23 +845,24 @@ int git_iterator_for_workdir_range( } wi->root_len = wi->path.size; - wi->entrycmp = wi->base.ignore_case ? + wi->entrycmp = (wi->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0 ? workdir_iterator__entry_cmp_icase : workdir_iterator__entry_cmp_case; if ((error = workdir_iterator__expand_dir(wi)) < 0) { - if (error == GIT_ENOTFOUND) - error = 0; - else { - git_iterator_free((git_iterator *)wi); - wi = NULL; - } + if (error != GIT_ENOTFOUND) + goto fail; + giterr_clear(); } *iter = (git_iterator *)wi; + return 0; +fail: + git_iterator_free((git_iterator *)wi); return error; } + typedef struct { /* replacement callbacks */ git_iterator_callbacks cb; @@ -899,12 +937,12 @@ void git_iterator_spoolandsort_pop(git_iterator *self) { spoolandsort_callbacks *scb = (spoolandsort_callbacks *)self->cb; - if (self->type != GIT_ITERATOR_SPOOLANDSORT) + if (self->type != GIT_ITERATOR_TYPE_SPOOLANDSORT) return; self->cb = scb->orig; self->type = scb->orig_type; - self->ignore_case = !self->ignore_case; + self->flags ^= GIT_ITERATOR_IGNORE_CASE; spoolandsort_iterator__free_callbacks(scb); } @@ -921,7 +959,7 @@ int git_iterator_spoolandsort_push(git_iterator *iter, bool ignore_case) spoolandsort_callbacks *scb; int (*entrycomp)(const void *a, const void *b); - if (iter->ignore_case == ignore_case) + if (((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) == (ignore_case != 0)) return 0; scb = git__calloc(1, sizeof(spoolandsort_callbacks)); @@ -964,8 +1002,8 @@ int git_iterator_spoolandsort_push(git_iterator *iter, bool ignore_case) git_vector_sort(&scb->entries); iter->cb = (git_iterator_callbacks *)scb; - iter->type = GIT_ITERATOR_SPOOLANDSORT; - iter->ignore_case = !iter->ignore_case; + iter->type = GIT_ITERATOR_TYPE_SPOOLANDSORT; + iter->flags ^= GIT_ITERATOR_IGNORE_CASE; return 0; @@ -992,11 +1030,11 @@ void git_iterator_free(git_iterator *iter) git_index *git_iterator_index_get_index(git_iterator *iter) { - if (iter->type == GIT_ITERATOR_INDEX) + if (iter->type == GIT_ITERATOR_TYPE_INDEX) return ((index_iterator *)iter)->index; - if (iter->type == GIT_ITERATOR_SPOOLANDSORT && - ((spoolandsort_callbacks *)iter->cb)->orig_type == GIT_ITERATOR_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; @@ -1004,7 +1042,7 @@ git_index *git_iterator_index_get_index(git_iterator *iter) git_iterator_type_t git_iterator_inner_type(git_iterator *iter) { - if (iter->type == GIT_ITERATOR_SPOOLANDSORT) + if (iter->type == GIT_ITERATOR_TYPE_SPOOLANDSORT) return ((spoolandsort_callbacks *)iter->cb)->orig_type; return iter->type; @@ -1013,7 +1051,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) { - *tree_entry = (iter->type != GIT_ITERATOR_TREE) ? NULL : + *tree_entry = (iter->type != GIT_ITERATOR_TYPE_TREE) ? NULL : tree_iterator__tree_entry((tree_iterator *)iter); return 0; } @@ -1027,7 +1065,7 @@ int git_iterator_current_parent_tree( tree_iterator_frame *tf; const char *scan = parent_path; - if (iter->type != GIT_ITERATOR_TREE || ti->stack == NULL) + if (iter->type != GIT_ITERATOR_TYPE_TREE || ti->stack == NULL) goto notfound; for (tf = ti->tail; tf != NULL; tf = tf->prev) { @@ -1061,7 +1099,7 @@ int git_iterator_current_is_ignored(git_iterator *iter) { workdir_iterator *wi = (workdir_iterator *)iter; - if (iter->type != GIT_ITERATOR_WORKDIR) + if (iter->type != GIT_ITERATOR_TYPE_WORKDIR) return 0; if (wi->is_ignored != -1) @@ -1078,7 +1116,7 @@ int git_iterator_advance_into_directory( { workdir_iterator *wi = (workdir_iterator *)iter; - if (iter->type == GIT_ITERATOR_WORKDIR && + if (iter->type == GIT_ITERATOR_TYPE_WORKDIR && wi->entry.path && (wi->entry.mode == GIT_FILEMODE_TREE || wi->entry.mode == GIT_FILEMODE_COMMIT)) @@ -1112,7 +1150,7 @@ int git_iterator_current_workdir_path(git_iterator *iter, git_buf **path) { workdir_iterator *wi = (workdir_iterator *)iter; - if (iter->type != GIT_ITERATOR_WORKDIR || !wi->entry.path) + if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->entry.path) *path = NULL; else *path = &wi->path; diff --git a/src/iterator.h b/src/iterator.h index 727da97b3..67e8a42dd 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -12,20 +12,26 @@ #include "vector.h" #include "buffer.h" -#define ITERATOR_PREFIXCMP(ITER, STR, PREFIX) (((ITER).ignore_case) ? \ +#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 { - GIT_ITERATOR_EMPTY = 0, - GIT_ITERATOR_TREE = 1, - GIT_ITERATOR_INDEX = 2, - GIT_ITERATOR_WORKDIR = 3, - GIT_ITERATOR_SPOOLANDSORT = 4 + GIT_ITERATOR_TYPE_EMPTY = 0, + GIT_ITERATOR_TYPE_TREE = 1, + GIT_ITERATOR_TYPE_INDEX = 2, + GIT_ITERATOR_TYPE_WORKDIR = 3, + GIT_ITERATOR_TYPE_SPOOLANDSORT = 4 } git_iterator_type_t; +typedef enum { + GIT_ITERATOR_IGNORE_CASE = (1 << 0), /* ignore_case */ + GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1), /* force ignore_case off */ +} git_iterator_flag_t; + typedef struct { int (*current)(git_iterator *, const git_index_entry **); int (*at_end)(git_iterator *); @@ -41,33 +47,55 @@ struct git_iterator { git_repository *repo; char *start; char *end; - bool ignore_case; + unsigned int flags; }; -extern int git_iterator_for_nothing(git_iterator **out); +extern int git_iterator_for_nothing( + git_iterator **out, git_iterator_flag_t flags); +/* 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( - git_iterator **out, git_tree *tree, const char *start, const char *end); + 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, NULL, NULL); + 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( - git_iterator **out, git_index *index, const char *start, const char *end); + 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, NULL, NULL); + 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( - git_iterator **out, git_repository *repo, const char *start, const char *end); + 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, NULL, NULL); + return git_iterator_for_workdir_range(out, repo, 0, NULL, NULL); } extern void git_iterator_free(git_iterator *iter); @@ -127,6 +155,16 @@ GIT_INLINE(git_repository *) git_iterator_owner(git_iterator *iter) return iter->repo; } +GIT_INLINE(git_iterator_flag_t) git_iterator_flags(git_iterator *iter) +{ + return iter->flags; +} + +GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter) +{ + return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0); +} + extern int git_iterator_current_tree_entry( git_iterator *iter, const git_tree_entry **tree_entry); diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index de083ea36..6fc5730bc 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -35,7 +35,8 @@ 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(&i, t, start, end)); + cl_git_pass(git_iterator_for_tree_range( + &i, t, GIT_ITERATOR_DONT_IGNORE_CASE, start, end)); /* test loop */ cl_git_pass(git_iterator_current(i, &entry)); @@ -304,7 +305,8 @@ void test_diff_iterator__tree_special_functions(void) repo, "24fa9a9fc4e202313e24b648087495441dab432b"); cl_assert(t != NULL); - cl_git_pass(git_iterator_for_tree_range(&i, t, NULL, NULL)); + cl_git_pass(git_iterator_for_tree_range( + &i, t, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)); cl_git_pass(git_iterator_current(i, &entry)); while (entry != NULL) { @@ -362,7 +364,7 @@ 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, start, end)); + cl_git_pass(git_iterator_for_index_range(&i, index, 0, start, end)); cl_git_pass(git_iterator_current(i, &entry)); while (entry != NULL) { @@ -536,7 +538,7 @@ 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, start, end)); + cl_git_pass(git_iterator_for_workdir_range(&i, repo, 0, start, end)); cl_git_pass(git_iterator_current(i, &entry)); while (entry != NULL) { @@ -734,7 +736,7 @@ 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, "dir", "sub/sub/file")); + git_iterator_for_workdir_range(&i, repo, 0, "dir", "sub/sub/file")); cl_git_pass(git_iterator_current(i, &entry)); for (idx = 0; entry != NULL; ++idx) { From a49340c3e5de90ca551b46ea79573c428e3836b0 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Tue, 8 Jan 2013 15:56:11 -0800 Subject: [PATCH 06/11] Test for ignore_case ranges on workdir iterator This adds a test that confirms that the working directory iterator can actually correctly process ranges of files case insensitively with proper sorting and proper boundaries. --- tests-clar/diff/iterator.c | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index 6fc5730bc..846145abb 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -755,3 +755,52 @@ void test_diff_iterator__workdir_builtin_ignores(void) git_iterator_free(i); } + +static void check_first_through_third_range( + git_repository *repo, const char *start, const char *end) +{ + git_iterator *i; + const git_index_entry *entry; + int idx; + static const char *expected[] = { + "FIRST", "second", "THIRD", NULL + }; + + cl_git_pass(git_iterator_for_workdir_range( + &i, repo, GIT_IGNORE_CASE, start, end)); + cl_git_pass(git_iterator_current(i, &entry)); + + for (idx = 0; entry != NULL; ++idx) { + cl_assert_equal_s(expected[idx], entry->path); + + if (S_ISDIR(entry->mode)) + cl_git_pass(git_iterator_advance_into_directory(i, &entry)); + else + cl_git_pass(git_iterator_advance(i, &entry)); + } + + cl_assert(expected[idx] == NULL); + + git_iterator_free(i); +} + +void test_diff_iterator__workdir_handles_icase_range(void) +{ + git_repository *repo; + + repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt"); + + cl_git_mkfile("empty_standard_repo/before", "whatever\n"); + cl_git_mkfile("empty_standard_repo/FIRST", "whatever\n"); + cl_git_mkfile("empty_standard_repo/second", "whatever\n"); + cl_git_mkfile("empty_standard_repo/THIRD", "whatever\n"); + cl_git_mkfile("empty_standard_repo/zafter", "whatever\n"); + cl_git_mkfile("empty_standard_repo/Zlast", "whatever\n"); + + check_first_through_third_range(repo, "first", "third"); + check_first_through_third_range(repo, "FIRST", "THIRD"); + check_first_through_third_range(repo, "first", "THIRD"); + check_first_through_third_range(repo, "FIRST", "third"); + check_first_through_third_range(repo, "FirSt", "tHiRd"); +} From 851ad65081793bb5fd65052907bf1c3c4e7e5729 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 9 Jan 2013 16:00:16 -0800 Subject: [PATCH 07/11] Add payload "_r" versions of bsearch and tsort git__bsearch and git__tsort did not pass a payload through to the comparison function. This makes it impossible to implement sorted lists where the sort order depends on external data (e.g. building a secondary sort order for the entries in a tree). This commit adds git__bsearch_r and git__tsort_r versions that pass a third parameter to the cmp function of a user payload. --- src/tsort.c | 60 +++++++++++++++++++++++++++++++++-------------------- src/util.c | 33 ++++++++++++++++++++++++++++- src/util.h | 20 ++++++++++++++++-- 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/src/tsort.c b/src/tsort.c index 634fe2672..97473be91 100644 --- a/src/tsort.c +++ b/src/tsort.c @@ -23,9 +23,8 @@ # define MIN(x,y) (((x) < (y) ? (x) : (y))) #endif -typedef int (*cmp_ptr_t)(const void *, const void *); - -static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp) +static int binsearch( + void **dst, const void *x, size_t size, git__tsort_r_cmp cmp, void *payload) { int l, c, r; void *lx, *cx; @@ -38,12 +37,12 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp) lx = dst[l]; /* check for beginning conditions */ - if (cmp(x, lx) < 0) + if (cmp(x, lx, payload) < 0) return 0; - else if (cmp(x, lx) == 0) { + else if (cmp(x, lx, payload) == 0) { int i = 1; - while (cmp(x, dst[i]) == 0) + while (cmp(x, dst[i], payload) == 0) i++; return i; } @@ -51,7 +50,7 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp) /* guaranteed not to be >= rx */ cx = dst[c]; while (1) { - const int val = cmp(x, cx); + const int val = cmp(x, cx, payload); if (val < 0) { if (c - l <= 1) return c; r = c; @@ -62,7 +61,7 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp) } else { do { cx = dst[++c]; - } while (cmp(x, cx) == 0); + } while (cmp(x, cx, payload) == 0); return c; } c = l + ((r - l) >> 1); @@ -71,7 +70,8 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp) } /* 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, cmp_ptr_t cmp) +static void bisort( + void **dst, size_t start, size_t size, git__tsort_r_cmp cmp, void *payload) { size_t i; void *x; @@ -80,12 +80,12 @@ static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp) for (i = start; i < size; i++) { int j; /* If this entry is already correct, just move along */ - if (cmp(dst[i - 1], dst[i]) <= 0) + if (cmp(dst[i - 1], dst[i], payload) <= 0) continue; /* Else we need to find the right place, shift everything over, and squeeze in */ x = dst[i]; - location = binsearch(dst, x, i, cmp); + location = binsearch(dst, x, i, cmp, payload); for (j = (int)i - 1; j >= location; j--) { dst[j + 1] = dst[j]; } @@ -102,7 +102,8 @@ struct tsort_run { struct tsort_store { size_t alloc; - cmp_ptr_t cmp; + git__tsort_r_cmp cmp; + void *payload; void **storage; }; @@ -118,7 +119,8 @@ static void reverse_elements(void **dst, ssize_t start, ssize_t end) } } -static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store) +static ssize_t count_run( + void **dst, ssize_t start, ssize_t size, struct tsort_store *store) { ssize_t curr = start + 2; @@ -126,7 +128,7 @@ static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_s return 1; if (start >= size - 2) { - if (store->cmp(dst[size - 2], dst[size - 1]) > 0) { + if (store->cmp(dst[size - 2], dst[size - 1], store->payload) > 0) { void *tmp = dst[size - 1]; dst[size - 1] = dst[size - 2]; dst[size - 2] = tmp; @@ -135,13 +137,15 @@ static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_s return 2; } - if (store->cmp(dst[start], dst[start + 1]) <= 0) { - while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) <= 0) + if (store->cmp(dst[start], dst[start + 1], store->payload) <= 0) { + while (curr < size - 1 && + store->cmp(dst[curr - 1], dst[curr], store->payload) <= 0) curr++; return curr - start; } else { - while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) > 0) + while (curr < size - 1 && + store->cmp(dst[curr - 1], dst[curr], store->payload) > 0) curr++; /* reverse in-place */ @@ -219,7 +223,7 @@ static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, for (k = curr; k < curr + A + B; k++) { if ((i < A) && (j < curr + A + B)) { - if (store->cmp(storage[i], dst[j]) <= 0) + if (store->cmp(storage[i], dst[j], store->payload) <= 0) dst[k] = storage[i++]; else dst[k] = dst[j++]; @@ -235,7 +239,7 @@ static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, for (k = curr + A + B - 1; k >= curr; k--) { if ((i >= 0) && (j >= curr)) { - if (store->cmp(dst[j], storage[i]) > 0) + if (store->cmp(dst[j], storage[i], store->payload) > 0) dst[k] = dst[j--]; else dst[k] = storage[i--]; @@ -307,7 +311,7 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, if (run < minrun) run = minrun;\ if (run > (ssize_t)size - curr) run = size - curr;\ if (run > len) {\ - bisort(&dst[curr], len, run, cmp);\ + bisort(&dst[curr], len, run, cmp, payload);\ len = run;\ }\ run_stack[stack_curr].start = curr;\ @@ -329,7 +333,8 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, }\ while (0) -void git__tsort(void **dst, size_t size, cmp_ptr_t cmp) +void git__tsort_r( + void **dst, size_t size, git__tsort_r_cmp cmp, void *payload) { struct tsort_store _store, *store = &_store; struct tsort_run run_stack[128]; @@ -340,7 +345,7 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp) ssize_t minrun; if (size < 64) { - bisort(dst, 1, size, cmp); + bisort(dst, 1, size, cmp, payload); return; } @@ -351,6 +356,7 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp) store->alloc = 0; store->storage = NULL; store->cmp = cmp; + store->payload = payload; PUSH_NEXT(); PUSH_NEXT(); @@ -365,3 +371,13 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp) PUSH_NEXT(); } } + +static int tsort_r_cmp(const void *a, const void *b, void *payload) +{ + return ((git__tsort_cmp)payload)(a, b); +} + +void git__tsort(void **dst, size_t size, git__tsort_cmp cmp) +{ + git__tsort_r(dst, size, tsort_r_cmp, cmp); +} diff --git a/src/util.c b/src/util.c index 51173fa70..30c4dc6ce 100644 --- a/src/util.c +++ b/src/util.c @@ -462,7 +462,7 @@ uint32_t git__hash(const void *key, int len, uint32_t seed) * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - */ + */ int git__bsearch( void **array, size_t array_len, @@ -493,6 +493,37 @@ int git__bsearch( return (cmp == 0) ? 0 : -1; } +int git__bsearch_r( + void **array, + size_t array_len, + const void *key, + int (*compare_r)(const void *, const void *, void *), + void *payload, + size_t *position) +{ + unsigned int lim; + int cmp = -1; + void **part, **base = array; + + for (lim = (unsigned int)array_len; lim != 0; lim >>= 1) { + part = base + (lim >> 1); + cmp = (*compare_r)(key, *part, payload); + if (cmp == 0) { + base = part; + break; + } + if (cmp > 0) { /* key > p; take right partition */ + base = part + 1; + lim--; + } /* else take left partition */ + } + + if (position) + *position = (base - array); + + return (cmp == 0) ? 0 : -1; +} + /** * A strcmp wrapper * diff --git a/src/util.h b/src/util.h index ee0d0e3ed..9bcd3203e 100644 --- a/src/util.h +++ b/src/util.h @@ -119,7 +119,15 @@ GIT_INLINE(const char *) git__next_line(const char *s) return s; } -extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *)); +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); + +extern void git__tsort_r( + void **dst, size_t size, git__tsort_r_cmp cmp, void *payload); + /** * @param position If non-NULL, this will be set to the position where the @@ -130,7 +138,15 @@ extern int git__bsearch( void **array, size_t array_len, const void *key, - int (*compare)(const void *, const void *), + int (*compare)(const void *key, const void *element), + size_t *position); + +extern int git__bsearch_r( + void **array, + size_t array_len, + const void *key, + int (*compare_r)(const void *key, const void *element, void *payload), + void *payload, size_t *position); extern int git__strcmp_cb(const void *a, const void *b); From 23594c1dae08c9a53f571dbf9de7ff0b6a6a0d45 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 9 Jan 2013 16:02:42 -0800 Subject: [PATCH 08/11] Add git_path_icmp to case-insensitive path cmp This adds git_path_icmp to complement git_path_cmp. --- src/path.c | 24 ++++++++++++++++++++++++ src/path.h | 7 ++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/path.c b/src/path.c index 0fd367eaf..5de58cce7 100644 --- a/src/path.c +++ b/src/path.c @@ -701,6 +701,30 @@ int git_path_cmp( return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; } +int git_path_icmp( + const char *name1, size_t len1, int isdir1, + const char *name2, size_t len2, int isdir2) +{ + unsigned char c1, c2; + size_t len = len1 < len2 ? len1 : len2; + int cmp; + + cmp = strncasecmp(name1, name2, len); + if (cmp) + return cmp; + + c1 = name1[len]; + c2 = name2[len]; + + if (c1 == '\0' && isdir1) + c1 = '/'; + + if (c2 == '\0' && isdir2) + c2 = '/'; + + return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; +} + int git_path_direach( git_buf *path, int (*fn)(void *, git_buf *), diff --git a/src/path.h b/src/path.h index de0a40b53..feefd65d1 100644 --- a/src/path.h +++ b/src/path.h @@ -261,12 +261,17 @@ extern int git_path_direach( void *state); /** - * Sort function to order two paths. + * Sort function to order two paths */ extern int git_path_cmp( const char *name1, size_t len1, int isdir1, const char *name2, size_t len2, int isdir2); +/** Path sort function that is case insensitive */ +extern int git_path_icmp( + const char *name1, size_t len1, int isdir1, + const char *name2, size_t len2, int isdir2); + /** * Invoke callback up path directory by directory until the ceiling is * reached (inclusive of a final call at the root_path). From 98527b5b241ce9d240537f60e87aa9dd084c0f36 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 9 Jan 2013 16:03:35 -0800 Subject: [PATCH 09/11] Add git_tree_entry_cmp and git_tree_entry_icmp This adds a new external API git_tree_entry_cmp and a new internal API git_tree_entry_icmp for sorting tree entries. The case insensitive one is internal only because general users should never be seeing case-insensitively sorted trees. --- include/git2/tree.h | 9 +++++++++ src/tree.c | 21 +++++++++++++++------ src/tree.h | 2 ++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/include/git2/tree.h b/include/git2/tree.h index 7726a6599..3861102d9 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -208,6 +208,15 @@ GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry); */ GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry); +/** + * Compare two tree entries + * + * @param e1 first tree entry + * @param e2 second tree entry + * @return <0 if e1 is before e2, 0 if e1 == e2, >0 if e1 is after e2 + */ +GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2); + /** * Convert a tree entry to the git_object it points too. * diff --git a/src/tree.c b/src/tree.c index cd1cd6076..c34e9b940 100644 --- a/src/tree.c +++ b/src/tree.c @@ -55,14 +55,23 @@ static int valid_entry_name(const char *filename) strcmp(filename, DOT_GIT) != 0)); } +int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2) +{ + return git_path_cmp( + e1->filename, e1->filename_len, git_tree_entry__is_tree(e1), + e2->filename, e2->filename_len, git_tree_entry__is_tree(e2)); +} + +int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2) +{ + return git_path_icmp( + e1->filename, e1->filename_len, git_tree_entry__is_tree(e1), + e2->filename, e2->filename_len, git_tree_entry__is_tree(e2)); +} + static int entry_sort_cmp(const void *a, const void *b) { - const git_tree_entry *entry_a = (const git_tree_entry *)(a); - const git_tree_entry *entry_b = (const git_tree_entry *)(b); - - return git_path_cmp( - entry_a->filename, entry_a->filename_len, git_tree_entry__is_tree(entry_a), - entry_b->filename, entry_b->filename_len, git_tree_entry__is_tree(entry_b)); + return git_tree_entry_cmp((const git_tree_entry *)a, (const git_tree_entry *)b); } static git_tree_entry *alloc_entry(const char *filename) diff --git a/src/tree.h b/src/tree.h index 6f05f5a7a..27afd4fd4 100644 --- a/src/tree.h +++ b/src/tree.h @@ -39,6 +39,8 @@ GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e) return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr)); } +extern int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2); + void git_tree__free(git_tree *tree); int git_tree__parse(git_tree *tree, git_odb_object *obj); From 25423d03b826f9647e72a56b14e08967d92ae479 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Wed, 9 Jan 2013 16:07:54 -0800 Subject: [PATCH 10/11] Support case insensitive tree iterators and status This makes tree iterators directly support case insensitivity by using a secondary index that can be sorted by icase. Also, this fixes the ambiguity check in the git_status_file API to also be case insensitive. Lastly, this adds new test cases for case insensitive range boundary checking for all types of iterators. With this change, it should be possible to deprecate the spool and sort iterator, but I haven't done that yet. --- src/iterator.c | 132 +++++++++++++++++++++++++++++-------- src/iterator.h | 1 + src/status.c | 14 +++- tests-clar/diff/iterator.c | 123 +++++++++++++++++++++++++++++++--- 4 files changed, 229 insertions(+), 41 deletions(-) diff --git a/src/iterator.c b/src/iterator.c index a9df39bb8..0b6a4fc15 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -31,6 +31,7 @@ (P)->base.end = end ? git__strdup(end) : NULL; \ if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \ git__free(P); return -1; } \ + (P)->base.prefixcomp = git__prefixcmp; \ } while (0) static int iterator__reset_range( @@ -75,6 +76,9 @@ 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) ? + git__prefixcmp_icase : git__prefixcmp; + return error; } @@ -141,7 +145,10 @@ struct tree_iterator_frame { tree_iterator_frame *next, *prev; git_tree *tree; char *start; + size_t startlen; size_t index; + void **icase_map; + void *icase_data[GIT_FLEX_ARRAY]; }; typedef struct { @@ -155,7 +162,13 @@ typedef struct { GIT_INLINE(const git_tree_entry *)tree_iterator__tree_entry(tree_iterator *ti) { - return git_tree_entry_byindex(ti->stack->tree, ti->stack->index); + tree_iterator_frame *tf = ti->stack; + + if (tf->index >= git_tree_entrycount(tf->tree)) + return NULL; + + return git_tree_entry_byindex( + tf->tree, tf->icase_map ? (size_t)tf->icase_map[tf->index] : tf->index); } static char *tree_iterator__current_filename( @@ -174,7 +187,10 @@ static void tree_iterator__free_frame(tree_iterator_frame *tf) { if (!tf) return; + git_tree_free(tf->tree); + tf->tree = NULL; + git__free(tf); } @@ -220,7 +236,7 @@ static int tree_iterator__current( if (ti->entry.path == NULL) return -1; - if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0) + if (ti->base.end && ti->base.prefixcomp(ti->entry.path, ti->base.end) > 0) return tree_iterator__to_end(ti); if (entry) @@ -234,10 +250,50 @@ static int tree_iterator__at_end(git_iterator *self) return (tree_iterator__tree_entry((tree_iterator *)self) == NULL); } -static tree_iterator_frame *tree_iterator__alloc_frame( - git_tree *tree, char *start) +static int tree_iterator__icase_map_cmp(const void *a, const void *b, void *data) { - tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame)); + 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 *element) +{ + const tree_iterator_frame *tf = (const tree_iterator_frame *)key; + const git_tree_entry *te = git_tree_entry_byindex(tf->tree, (size_t)element); + + return memcmp(tf->start, te->filename, min(tf->startlen, te->filename_len)); +} + +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 tree_iterator_frame *tree_iterator__alloc_frame( + tree_iterator *ti, git_tree *tree, 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; @@ -245,9 +301,24 @@ static tree_iterator_frame *tree_iterator__alloc_frame( if (start && *start) { tf->start = start; - tf->index = git_tree__prefix_position(tree, start); + tf->startlen = strlen(start); } + if (!max_i) + return tf; + + if ((ti->base.flags & GIT_ITERATOR_IGNORE_CASE) != 0) { + 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 tf; } @@ -265,7 +336,7 @@ static int tree_iterator__expand_tree(tree_iterator *ti) /* check that we have not passed the range end */ if (ti->base.end != NULL && - git__prefixcmp(ti->path.ptr, ti->base.end) > 0) + ti->base.prefixcomp(ti->path.ptr, ti->base.end) > 0) return tree_iterator__to_end(ti); if ((error = git_tree_lookup(&subtree, ti->base.repo, &te->oid)) < 0) @@ -275,14 +346,13 @@ static int tree_iterator__expand_tree(tree_iterator *ti) /* apply range start to new frame if relevant */ if (ti->stack->start && - git__prefixcmp(ti->stack->start, te->filename) == 0) + ti->base.prefixcomp(ti->stack->start, te->filename) == 0) { - size_t namelen = strlen(te->filename); - if (ti->stack->start[namelen] == '/') - relpath = ti->stack->start + namelen + 1; + if (ti->stack->start[te->filename_len] == '/') + relpath = ti->stack->start + te->filename_len + 1; } - if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL) + if ((tf = tree_iterator__alloc_frame(ti, subtree, relpath)) == NULL) return -1; tf->next = ti->stack; @@ -311,8 +381,9 @@ static int tree_iterator__advance( } while (1) { - te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index); - if (te != NULL) + ++ti->stack->index; + + if ((te = tree_iterator__tree_entry(ti)) != NULL) break; if (!tree_iterator__pop_frame(ti)) @@ -362,8 +433,8 @@ static int tree_iterator__reset( if (iterator__reset_range(self, start, end) < 0) return -1; - ti->stack->index = - git_tree__prefix_position(ti->stack->tree, ti->base.start); + /* reset start position */ + tree_iterator__frame_seek_start(ti->stack); git_buf_clear(&ti->path); ti->path_has_filename = false; @@ -394,10 +465,7 @@ int git_iterator_for_tree_range( if ((error = iterator_update_ignore_case((git_iterator *)ti, flags)) < 0) goto fail; - /* TODO: implement icase support natively in tree iterators */ - ti->base.flags = (ti->base.flags & ~GIT_ITERATOR_IGNORE_CASE); - - ti->stack = ti->tail = tree_iterator__alloc_frame(tree, ti->base.start); + ti->stack = ti->tail = tree_iterator__alloc_frame(ti, tree, ti->base.start); if ((error = tree_iterator__expand_tree(ti)) < 0) goto fail; @@ -447,7 +515,7 @@ static void index_iterator__skip_conflicts( if (ie == NULL || (ii->base.end != NULL && - ITERATOR_PREFIXCMP(ii->base, ie->path, ii->base.end) > 0)) { + ii->base.prefixcomp(ie->path, ii->base.end) > 0)) { ii->current = entrycount; break; } @@ -513,8 +581,10 @@ int git_iterator_for_index_range( ITERATOR_BASE_INIT(ii, index, INDEX); ii->base.repo = git_index_owner(index); - if (index->ignore_case) + if (index->ignore_case) { ii->base.flags |= GIT_ITERATOR_IGNORE_CASE; + ii->base.prefixcomp = git__prefixcmp_icase; + } ii->index = index; GIT_REFCOUNT_INC(index); @@ -772,8 +842,8 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0) return -1; - if (wi->base.end && ITERATOR_PREFIXCMP( - wi->base, wi->path.ptr + wi->root_len, wi->base.end) > 0) + if (wi->base.end && + wi->base.prefixcomp(wi->path.ptr + wi->root_len, wi->base.end) > 0) return 0; wi->entry.path = ps->path; @@ -1064,10 +1134,14 @@ 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); if (iter->type != GIT_ITERATOR_TYPE_TREE || ti->stack == NULL) goto notfound; + strncomp = ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) ? + git__strncasecmp : git__strncmp; + for (tf = ti->tail; tf != NULL; tf = tf->prev) { const git_tree_entry *te; @@ -1076,9 +1150,10 @@ int git_iterator_current_parent_tree( return 0; } - te = git_tree_entry_byindex(tf->tree, tf->index); + te = git_tree_entry_byindex(tf->tree, + tf->icase_map ? (size_t)tf->icase_map[tf->index] : tf->index); - if (strncmp(scan, te->filename, te->filename_len) != 0) + if (strncomp(scan, te->filename, te->filename_len) != 0) goto notfound; scan += te->filename_len; @@ -1129,8 +1204,7 @@ int git_iterator_advance_into_directory( return entry ? git_iterator_current(iter, entry) : 0; } -int git_iterator_cmp( - git_iterator *iter, const char *path_prefix) +int git_iterator_cmp(git_iterator *iter, const char *path_prefix) { const git_index_entry *entry; @@ -1143,7 +1217,7 @@ int git_iterator_cmp( if (!path_prefix) return -1; - return ITERATOR_PREFIXCMP(*iter, entry->path, path_prefix); + return iter->prefixcomp(entry->path, path_prefix); } int git_iterator_current_workdir_path(git_iterator *iter, git_buf **path) diff --git a/src/iterator.h b/src/iterator.h index 67e8a42dd..a9bccfca8 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -47,6 +47,7 @@ struct git_iterator { git_repository *repo; char *start; char *end; + int (*prefixcomp)(const char *str, const char *prefix); unsigned int flags; }; diff --git a/src/status.c b/src/status.c index 5b89e8c0d..282cb396b 100644 --- a/src/status.c +++ b/src/status.c @@ -196,19 +196,23 @@ struct status_file_info { char *expected; unsigned int count; unsigned int status; + int fnm_flags; int ambiguous; }; static int get_one_status(const char *path, unsigned int status, void *data) { struct status_file_info *sfi = data; + int (*strcomp)(const char *a, const char *b); sfi->count++; sfi->status = status; + strcomp = (sfi->fnm_flags & FNM_CASEFOLD) ? git__strcasecmp : git__strcmp; + if (sfi->count > 1 || - (strcmp(sfi->expected, path) != 0 && - p_fnmatch(sfi->expected, path, 0) != 0)) + (strcomp(sfi->expected, path) != 0 && + p_fnmatch(sfi->expected, path, sfi->fnm_flags) != 0)) { sfi->ambiguous = true; return GIT_EAMBIGUOUS; @@ -225,11 +229,17 @@ int git_status_file( int error; git_status_options opts = GIT_STATUS_OPTIONS_INIT; struct status_file_info sfi = {0}; + git_index *index; assert(status_flags && repo && path); + if ((error = git_repository_index__weakptr(&index, repo)) < 0) + return error; + if ((sfi.expected = git__strdup(path)) == NULL) return -1; + if (index->ignore_case) + sfi.fnm_flags = FNM_CASEFOLD; opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED | diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c index 846145abb..566503b6e 100644 --- a/tests-clar/diff/iterator.c +++ b/tests-clar/diff/iterator.c @@ -756,18 +756,16 @@ void test_diff_iterator__workdir_builtin_ignores(void) git_iterator_free(i); } -static void check_first_through_third_range( +static void check_wd_first_through_third_range( git_repository *repo, const char *start, const char *end) { git_iterator *i; const git_index_entry *entry; int idx; - static const char *expected[] = { - "FIRST", "second", "THIRD", NULL - }; + static const char *expected[] = { "FIRST", "second", "THIRD", NULL }; cl_git_pass(git_iterator_for_workdir_range( - &i, repo, GIT_IGNORE_CASE, start, end)); + &i, repo, GIT_ITERATOR_IGNORE_CASE, start, end)); cl_git_pass(git_iterator_current(i, &entry)); for (idx = 0; entry != NULL; ++idx) { @@ -798,9 +796,114 @@ void test_diff_iterator__workdir_handles_icase_range(void) cl_git_mkfile("empty_standard_repo/zafter", "whatever\n"); cl_git_mkfile("empty_standard_repo/Zlast", "whatever\n"); - check_first_through_third_range(repo, "first", "third"); - check_first_through_third_range(repo, "FIRST", "THIRD"); - check_first_through_third_range(repo, "first", "THIRD"); - check_first_through_third_range(repo, "FIRST", "third"); - check_first_through_third_range(repo, "FirSt", "tHiRd"); + check_wd_first_through_third_range(repo, "first", "third"); + check_wd_first_through_third_range(repo, "FIRST", "THIRD"); + check_wd_first_through_third_range(repo, "first", "THIRD"); + check_wd_first_through_third_range(repo, "FIRST", "third"); + check_wd_first_through_third_range(repo, "FirSt", "tHiRd"); +} + +static void check_tree_range( + git_repository *repo, + const char *start, + const char *end, + bool ignore_case, + int expected_count) +{ + git_tree *head; + git_iterator *i; + const git_index_entry *entry; + int count; + + cl_git_pass(git_repository_head_tree(&head, repo)); + + cl_git_pass(git_iterator_for_tree_range( + &i, head, + ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE, + start, end)); + + cl_git_pass(git_iterator_current(i, &entry)); + + for (count = 0; entry != NULL; ) { + ++count; + cl_git_pass(git_iterator_advance(i, &entry)); + } + + cl_assert_equal_i(expected_count, count); + + git_iterator_free(i); + git_tree_free(head); +} + +void test_diff_iterator__tree_handles_icase_range(void) +{ + git_repository *repo; + + repo = cl_git_sandbox_init("testrepo"); + + check_tree_range(repo, "B", "C", false, 0); + check_tree_range(repo, "B", "C", true, 1); + check_tree_range(repo, "a", "z", false, 3); + check_tree_range(repo, "a", "z", true, 4); +} + +static void check_index_range( + git_repository *repo, + const char *start, + const char *end, + bool ignore_case, + int expected_count) +{ + git_index *index; + git_iterator *i; + const git_index_entry *entry; + int count, caps; + bool is_ignoring_case; + + cl_git_pass(git_repository_index(&index, repo)); + + caps = git_index_caps(index); + is_ignoring_case = ((caps & GIT_INDEXCAP_IGNORE_CASE) != 0); + + 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_assert(git_iterator_ignore_case(i) == ignore_case); + + cl_git_pass(git_iterator_current(i, &entry)); + + for (count = 0; entry != NULL; ) { + ++count; + cl_git_pass(git_iterator_advance(i, &entry)); + } + + cl_assert_equal_i(expected_count, count); + + git_iterator_free(i); + git_index_free(index); +} + +void test_diff_iterator__index_handles_icase_range(void) +{ + git_repository *repo; + git_index *index; + git_tree *head; + + repo = cl_git_sandbox_init("testrepo"); + + /* reset index to match HEAD */ + cl_git_pass(git_repository_head_tree(&head, repo)); + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_read_tree(index, head)); + cl_git_pass(git_index_write(index)); + git_tree_free(head); + git_index_free(index); + + /* do some ranged iterator checks toggling case sensitivity */ + check_index_range(repo, "B", "C", false, 0); + check_index_range(repo, "B", "C", true, 1); + check_index_range(repo, "a", "z", false, 3); + check_index_range(repo, "a", "z", true, 4); } From fffe429a20b68677cae940ba8a9a0ff60056d6c0 Mon Sep 17 00:00:00 2001 From: Russell Belfer Date: Thu, 10 Jan 2013 11:11:42 -0800 Subject: [PATCH 11/11] Shortcut spool and sort for empty iterator --- src/iterator.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/iterator.c b/src/iterator.c index 0b6a4fc15..56b262975 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1032,6 +1032,11 @@ int git_iterator_spoolandsort_push(git_iterator *iter, bool ignore_case) 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);