Commit 4a0dbeb0 by Edward Thomson

diff: use new iterator pathlist handling

When using literal pathspecs in diff with `GIT_DIFF_DISABLE_PATHSPEC_MATCH`
turn on the faster iterator pathlist handling.

Updates iterator pathspecs to include directory prefixes (eg, `foo/`)
for compatibility with `GIT_DIFF_DISABLE_PATHSPEC_MATCH`.
parent 3273ab3f
...@@ -80,14 +80,13 @@ static bool diff_pathspec_match( ...@@ -80,14 +80,13 @@ static bool diff_pathspec_match(
/* The iterator has filtered out paths for us, so the fact that we're /* The iterator has filtered out paths for us, so the fact that we're
* seeing this patch means that it must match the given path list. * seeing this patch means that it must match the given path list.
*/ */
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_ENABLE_FILELIST_MATCH)) { if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH)) {
*matched_pathspec = path; *matched_pathspec = path;
return true; return true;
} }
return git_pathspec__match( return git_pathspec__match(
&diff->pathspec, path, &diff->pathspec, path, false,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE), DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
matched_pathspec, NULL); matched_pathspec, NULL);
} }
...@@ -1063,6 +1062,12 @@ static int handle_unmatched_new_item( ...@@ -1063,6 +1062,12 @@ static int handle_unmatched_new_item(
&info->nitem, &untracked_state, info->new_iter)) < 0) &info->nitem, &untracked_state, info->new_iter)) < 0)
return error; return error;
/* if we found nothing that matched our pathlist filter, exclude */
if (untracked_state == GIT_ITERATOR_STATUS_FILTERED) {
git_vector_pop(&diff->deltas);
git__free(last);
}
/* if we found nothing or just ignored items, update the record */ /* if we found nothing or just ignored items, update the record */
if (untracked_state == GIT_ITERATOR_STATUS_IGNORED || if (untracked_state == GIT_ITERATOR_STATUS_IGNORED ||
untracked_state == GIT_ITERATOR_STATUS_EMPTY) { untracked_state == GIT_ITERATOR_STATUS_EMPTY) {
...@@ -1276,8 +1281,7 @@ cleanup: ...@@ -1276,8 +1281,7 @@ cleanup:
#define DIFF_FROM_ITERATORS(MAKE_FIRST, FLAGS_FIRST, MAKE_SECOND, FLAGS_SECOND) do { \ #define DIFF_FROM_ITERATORS(MAKE_FIRST, FLAGS_FIRST, MAKE_SECOND, FLAGS_SECOND) do { \
git_iterator *a = NULL, *b = NULL; \ git_iterator *a = NULL, *b = NULL; \
git_vector pathlist = GIT_VECTOR_INIT; \ char *pfx = (opts && !(opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) ? \
char *pfx = (opts && !(opts->flags & GIT_DIFF_ENABLE_FILELIST_MATCH)) ? \
git_pathspec_prefix(&opts->pathspec) : NULL; \ git_pathspec_prefix(&opts->pathspec) : NULL; \
git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, \ git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, \
b_opts = GIT_ITERATOR_OPTIONS_INIT; \ b_opts = GIT_ITERATOR_OPTIONS_INIT; \
...@@ -1288,19 +1292,15 @@ cleanup: ...@@ -1288,19 +1292,15 @@ cleanup:
b_opts.start = pfx; \ b_opts.start = pfx; \
b_opts.end = pfx; \ b_opts.end = pfx; \
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \ GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \
if (opts && (opts->flags & GIT_DIFF_ENABLE_FILELIST_MATCH) && opts->pathspec.count) { \ if (opts && (opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) { \
size_t __i; \ a_opts.pathlist.strings = opts->pathspec.strings; \
error = git_vector_init(&pathlist, opts->pathspec.count, NULL); \ a_opts.pathlist.count = opts->pathspec.count; \
for (__i = 0; !error && __i < opts->pathspec.count; __i++) { \ b_opts.pathlist.strings = opts->pathspec.strings; \
error = git_vector_insert(&pathlist, opts->pathspec.strings[__i]); \ b_opts.pathlist.count = opts->pathspec.count; \
} \
a_opts.pathlist = &pathlist; \
b_opts.pathlist = &pathlist; \
} \ } \
if (!error && !(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \ if (!error && !(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \
error = git_diff__from_iterators(diff, repo, a, b, opts); \ error = git_diff__from_iterators(diff, repo, a, b, opts); \
git__free(pfx); git_iterator_free(a); git_iterator_free(b); \ git__free(pfx); git_iterator_free(a); git_iterator_free(b); \
git_vector_free(&pathlist); \
} while (0) } while (0)
int git_diff_tree_to_tree( int git_diff_tree_to_tree(
......
...@@ -38,15 +38,14 @@ typedef enum { ...@@ -38,15 +38,14 @@ typedef enum {
GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 5), GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 5),
} git_iterator_flag_t; } git_iterator_flag_t;
typedef struct { typedef struct {
const char *start; const char *start;
const char *end; const char *end;
/* paths to include in the iterator (literal). any paths not listed /* paths to include in the iterator (literal). if set, any paths not
* will be excluded. note that this vector may be resorted! * listed here will be excluded from iteration.
*/ */
git_vector *pathlist; git_strarray pathlist;
/* flags, from above */ /* flags, from above */
unsigned int flags; unsigned int flags;
...@@ -70,8 +69,9 @@ struct git_iterator { ...@@ -70,8 +69,9 @@ struct git_iterator {
git_repository *repo; git_repository *repo;
char *start; char *start;
char *end; char *end;
git_vector *pathlist; git_vector pathlist;
size_t pathlist_idx; int (*strcomp)(const char *a, const char *b);
int (*strncomp)(const char *a, const char *b, size_t n);
int (*prefixcomp)(const char *str, const char *prefix); int (*prefixcomp)(const char *str, const char *prefix);
size_t stat_calls; size_t stat_calls;
unsigned int flags; unsigned int flags;
...@@ -277,7 +277,8 @@ extern git_index *git_iterator_get_index(git_iterator *iter); ...@@ -277,7 +277,8 @@ extern git_index *git_iterator_get_index(git_iterator *iter);
typedef enum { typedef enum {
GIT_ITERATOR_STATUS_NORMAL = 0, GIT_ITERATOR_STATUS_NORMAL = 0,
GIT_ITERATOR_STATUS_IGNORED = 1, GIT_ITERATOR_STATUS_IGNORED = 1,
GIT_ITERATOR_STATUS_EMPTY = 2 GIT_ITERATOR_STATUS_EMPTY = 2,
GIT_ITERATOR_STATUS_FILTERED = 3
} git_iterator_status_t; } git_iterator_status_t;
/* Advance over a directory and check if it contains no files or just /* Advance over a directory and check if it contains no files or just
......
...@@ -2357,7 +2357,8 @@ static int merge_check_index(size_t *conflicts, git_repository *repo, git_index ...@@ -2357,7 +2357,8 @@ static int merge_check_index(size_t *conflicts, git_repository *repo, git_index
} }
iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
iter_opts.pathlist = &staged_paths; iter_opts.pathlist.strings = (char **)staged_paths.contents;
iter_opts.pathlist.count = staged_paths.length;
if ((error = git_iterator_for_index(&iter_repo, index_repo, &iter_opts)) < 0 || if ((error = git_iterator_for_index(&iter_repo, index_repo, &iter_opts)) < 0 ||
(error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 || (error = git_iterator_for_index(&iter_new, index_new, &iter_opts)) < 0 ||
......
...@@ -486,7 +486,7 @@ void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void) ...@@ -486,7 +486,7 @@ void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void)
/* ensure that a single NULL pathspec is filtered out (like when using /* ensure that a single NULL pathspec is filtered out (like when using
* fnmatch filtering) * fnmatch filtering)
*/ */
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1; opts.pathspec.count = 1;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
...@@ -581,6 +581,30 @@ void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void) ...@@ -581,6 +581,30 @@ void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void)
git_diff_free(diff); git_diff_free(diff);
/* ensure that multiple trailing slashes are ignored */
pathspec = "subdir//////";
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
memset(&exp, 0, sizeof(exp));
if (use_iterator)
cl_git_pass(diff_foreach_via_iterator(
diff, diff_file_cb, NULL, NULL, NULL, &exp));
else
cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
cl_assert_equal_i(3, exp.files);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
}
git_diff_free(diff);
/* ensure that fnmatching is completely disabled */ /* ensure that fnmatching is completely disabled */
pathspec = "subdir/*"; pathspec = "subdir/*";
......
...@@ -26,7 +26,7 @@ static void expect_iterator_items( ...@@ -26,7 +26,7 @@ static void expect_iterator_items(
const git_index_entry *entry; const git_index_entry *entry;
int count, error; int count, error;
int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES); int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES);
bool v = true; bool v = false;
if (expected_flat < 0) { v = true; expected_flat = -expected_flat; } if (expected_flat < 0) { v = true; expected_flat = -expected_flat; }
if (expected_total < 0) { v = true; expected_total = -expected_total; } if (expected_total < 0) { v = true; expected_total = -expected_total; }
...@@ -1099,7 +1099,8 @@ void test_repo_iterator__indexfilelist(void) ...@@ -1099,7 +1099,8 @@ void test_repo_iterator__indexfilelist(void)
/* In this test we DO NOT force a case setting on the index. */ /* In this test we DO NOT force a case setting on the index. */
default_icase = ((git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0); default_icase = ((git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0);
i_opts.pathlist = &filelist; i_opts.pathlist.strings = (char **)filelist.contents;
i_opts.pathlist.count = filelist.length;
/* All indexfilelist iterator tests are "autoexpand with no tree entries" */ /* All indexfilelist iterator tests are "autoexpand with no tree entries" */
...@@ -1147,7 +1148,8 @@ void test_repo_iterator__indexfilelist_2(void) ...@@ -1147,7 +1148,8 @@ void test_repo_iterator__indexfilelist_2(void)
cl_git_pass(git_vector_insert(&filelist, "e")); cl_git_pass(git_vector_insert(&filelist, "e"));
cl_git_pass(git_vector_insert(&filelist, "k/a")); cl_git_pass(git_vector_insert(&filelist, "k/a"));
i_opts.pathlist = &filelist; i_opts.pathlist.strings = (char **)filelist.contents;
i_opts.pathlist.count = filelist.length;
i_opts.start = "b"; i_opts.start = "b";
i_opts.end = "k/D"; i_opts.end = "k/D";
...@@ -1188,7 +1190,8 @@ void test_repo_iterator__indexfilelist_icase(void) ...@@ -1188,7 +1190,8 @@ void test_repo_iterator__indexfilelist_icase(void)
/* All indexfilelist iterator tests are "autoexpand with no tree entries" */ /* All indexfilelist iterator tests are "autoexpand with no tree entries" */
i_opts.pathlist = &filelist; i_opts.pathlist.strings = (char **)filelist.contents;
i_opts.pathlist.count = filelist.length;
i_opts.start = "c"; i_opts.start = "c";
i_opts.end = "k/D"; i_opts.end = "k/D";
...@@ -1248,7 +1251,8 @@ void test_repo_iterator__workdirfilelist(void) ...@@ -1248,7 +1251,8 @@ void test_repo_iterator__workdirfilelist(void)
/* All indexfilelist iterator tests are "autoexpand with no tree entries" */ /* All indexfilelist iterator tests are "autoexpand with no tree entries" */
/* In this test we DO NOT force a case on the iteratords and verify default behavior. */ /* In this test we DO NOT force a case on the iteratords and verify default behavior. */
i_opts.pathlist = &filelist; i_opts.pathlist.strings = (char **)filelist.contents;
i_opts.pathlist.count = filelist.length;
cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts)); cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
expect_iterator_items(i, 8, NULL, 8, NULL); expect_iterator_items(i, 8, NULL, 8, NULL);
...@@ -1297,7 +1301,8 @@ void test_repo_iterator__workdirfilelist_icase(void) ...@@ -1297,7 +1301,8 @@ void test_repo_iterator__workdirfilelist_icase(void)
g_repo = cl_git_sandbox_init("icase"); g_repo = cl_git_sandbox_init("icase");
i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE; i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
i_opts.pathlist = &filelist; i_opts.pathlist.strings = (char **)filelist.contents;
i_opts.pathlist.count = filelist.length;
i_opts.start = "c"; i_opts.start = "c";
i_opts.end = "k/D"; i_opts.end = "k/D";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment