From 50887e521fc22e2eefa473bd31a0990534bacfbe Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sat, 29 Jan 2022 15:41:53 -0500 Subject: diff: support regexp ignores Emulate git's `-I` (`--ignore-matching-lines`) with a regular expression. --- include/git2/diff.h | 7 ++++++ src/diff_xdiff.c | 42 +++++++++++++++++++++++++++++++- src/diff_xdiff.h | 3 ++- src/patch_generate.c | 15 +++++++++--- src/regexp.h | 2 +- src/xdiff/git-xdiff.h | 11 +++------ tests/diff/blob.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 133 insertions(+), 14 deletions(-) diff --git a/include/git2/diff.h b/include/git2/diff.h index 9424ffd06..334614680 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -444,6 +444,13 @@ typedef struct { * Defaults to "b". */ const char *new_prefix; + + /** + * Ignore lines matching the given regular expression(s); both + * the preimage and postimage lines must match. + */ + const char **ignore_regexp; + size_t ignore_regexp_count; } git_diff_options; /* The current version of the diff options structure */ diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c index 3f6eccac1..3425f33a3 100644 --- a/src/diff_xdiff.c +++ b/src/diff_xdiff.c @@ -230,9 +230,12 @@ static int git_xdiff(git_patch_generated_output *output, git_patch_generated *pa return xo->output.error; } -void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) +int git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) { + git_regexp **regexen; uint32_t flags = opts ? opts->flags : 0; + int regexp_flags = GIT_REGEXP_EXTENDED | GIT_REGEXP_NEWLINE; + size_t i; xo->output.diff_cb = git_xdiff; @@ -256,5 +259,42 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts) if (flags & GIT_DIFF_IGNORE_BLANK_LINES) xo->params.flags |= XDF_IGNORE_BLANK_LINES; + if (opts && opts->ignore_regexp_count) { + regexen = git__calloc(opts->ignore_regexp_count, sizeof(git_regexp *)); + GIT_ERROR_CHECK_ALLOC(regexen); + + xo->params.ignore_regex = regexen; + xo->params.ignore_regex_nr = opts->ignore_regexp_count; + + for (i = 0; i < opts->ignore_regexp_count; i++) { + regexen[i] = git__malloc(sizeof(git_regexp)); + GIT_ERROR_CHECK_ALLOC(regexen[i]); + + if (git_regexp_compile(regexen[i], + opts->ignore_regexp[i], + regexp_flags) < 0) { + git_error_set(GIT_ERROR_INVALID, "could not compile regular expression"); + git_xdiff_dispose(xo); + return -1; + } + } + } + xo->callback.out_line = git_xdiff_cb; + + return 0; +} + +void git_xdiff_dispose(git_xdiff_output *xo) +{ + size_t i; + + for (i = 0; i < xo->params.ignore_regex_nr; i++) { + if (xo->params.ignore_regex[i]) { + git_regexp_dispose(xo->params.ignore_regex[i]); + git__free(xo->params.ignore_regex[i]); + } + } + + git__free(xo->params.ignore_regex); } diff --git a/src/diff_xdiff.h b/src/diff_xdiff.h index 9b303e9dc..3e051d1e1 100644 --- a/src/diff_xdiff.h +++ b/src/diff_xdiff.h @@ -30,6 +30,7 @@ typedef struct { xdemitcb_t callback; } git_xdiff_output; -void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts); +int git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts); +void git_xdiff_dispose(git_xdiff_output *xo); #endif diff --git a/src/patch_generate.c b/src/patch_generate.c index bc598fea8..1d80c036e 100644 --- a/src/patch_generate.c +++ b/src/patch_generate.c @@ -540,13 +540,16 @@ static int diff_from_sources( memset(&xo, 0, sizeof(xo)); diff_output_init( &xo.output, opts, file_cb, binary_cb, hunk_cb, data_cb, payload); - git_xdiff_init(&xo, opts); + + if ((error = git_xdiff_init(&xo, opts)) < 0) + return error; memset(&pd, 0, sizeof(pd)); error = patch_generated_from_sources(&pd, &xo, oldsrc, newsrc, opts); git_patch_free(&pd.patch.base); + git_xdiff_dispose(&xo); return error; } @@ -570,13 +573,16 @@ static int patch_from_sources( memset(&xo, 0, sizeof(xo)); diff_output_to_patch(&xo.output, &pd->patch); - git_xdiff_init(&xo, opts); + + if ((error = git_xdiff_init(&xo, opts)) < 0) + return error; if (!(error = patch_generated_from_sources(pd, &xo, oldsrc, newsrc, opts))) *out = (git_patch *)pd; else git_patch_free((git_patch *)pd); + git_xdiff_dispose(&xo); return error; } @@ -724,7 +730,9 @@ int git_patch_generated_from_diff( memset(&xo, 0, sizeof(xo)); diff_output_to_patch(&xo.output, patch); - git_xdiff_init(&xo, &diff->opts); + + if ((error = git_xdiff_init(&xo, &diff->opts)) < 0) + return error; error = patch_generated_invoke_file_callback(patch, &xo.output); @@ -741,6 +749,7 @@ int git_patch_generated_from_diff( else *patch_ptr = &patch->base; + git_xdiff_dispose(&xo); return error; } diff --git a/src/regexp.h b/src/regexp.h index 61824bf5c..f4810c70b 100644 --- a/src/regexp.h +++ b/src/regexp.h @@ -62,7 +62,7 @@ int git_regexp_compile(git_regexp *r, const char *pattern, int flags); /** * Free memory associated with the regular expression * - * @param r The regular expression structure to dispose. + e @param r The regular expression structure to dispose. */ void git_regexp_dispose(git_regexp *r); diff --git a/src/xdiff/git-xdiff.h b/src/xdiff/git-xdiff.h index 0b4f0bc04..cdfb315bd 100644 --- a/src/xdiff/git-xdiff.h +++ b/src/xdiff/git-xdiff.h @@ -29,14 +29,9 @@ GIT_INLINE(int) xdl_regexec_buf( const xdl_regex_t *preg, const char *buf, size_t size, size_t nmatch, xdl_regmatch_t pmatch[], int eflags) { - GIT_UNUSED(preg); - GIT_UNUSED(buf); - GIT_UNUSED(size); - GIT_UNUSED(nmatch); - GIT_UNUSED(pmatch); - GIT_UNUSED(eflags); - GIT_ASSERT("not implemented"); - return -1; + GIT_ASSERT(nmatch > 0 && pmatch && eflags == 0); + + return git_regexp_search_n(preg, buf, size, nmatch, pmatch); } #endif diff --git a/tests/diff/blob.c b/tests/diff/blob.c index d2f42207d..88aeeebd3 100644 --- a/tests/diff/blob.c +++ b/tests/diff/blob.c @@ -1061,3 +1061,70 @@ void test_diff_blob__can_compare_buffer_to_buffer(void) diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected)); assert_one_modified(4, 9, 0, 5, 4, &expected); } + +#define EMPTY_DIFF "diff --git a/file b/file\n" \ + "index 92dfa21..d5a68b2 100644\n" \ + "--- a/file\n" \ + "+++ b/file\n" + +#define NOT_EMPTY_DIFF \ + "diff --git a/file b/file\n" \ + "index 92dfa21..d5a68b2 100644\n" \ + "--- a/file\n" \ + "+++ b/file\n" \ + "@@ -5 +5 @@ d\n" \ + "-e\n" \ + "+E\n" + +void test_diff_blob__patch_with_ignore_regexp(void) +{ + git_patch *p; + git_buf buf = GIT_BUF_INIT; + const char *a = "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n"; + const char *b = "a\nb\nc\nd\nE\nf\ng\nh\ni\nj\n"; + const char *ignore_lower = "e"; + const char *ignore_upper = "E"; + const char *ignore_both[] = { ignore_lower, ignore_upper }; + const char *ignore_match = "^(e|E)$"; + + opts.interhunk_lines = 0; + opts.context_lines = 0; + opts.ignore_regexp = ignore_both; + opts.ignore_regexp_count = 2; + + /* match both e and E */ + cl_git_pass(git_patch_from_buffers(&p, a, strlen(a), NULL, + b, strlen(b), NULL, &opts)); + + cl_git_pass(git_patch_to_buf(&buf, p)); + cl_assert_equal_s(EMPTY_DIFF, buf.ptr); + + git_patch_free(p); + git_buf_dispose(&buf); + + /* match ^(e|E)$ */ + opts.ignore_regexp = &ignore_match; + opts.ignore_regexp_count = 1; + + cl_git_pass(git_patch_from_buffers(&p, a, strlen(a), NULL, + b, strlen(b), NULL, &opts)); + + cl_git_pass(git_patch_to_buf(&buf, p)); + cl_assert_equal_s(EMPTY_DIFF, buf.ptr); + + git_patch_free(p); + git_buf_dispose(&buf); + + /* matching only E does not match preimage */ + opts.ignore_regexp = &ignore_upper; + opts.ignore_regexp_count = 1; + + cl_git_pass(git_patch_from_buffers(&p, a, strlen(a), NULL, + b, strlen(b), NULL, &opts)); + + cl_git_pass(git_patch_to_buf(&buf, p)); + cl_assert_equal_s(NOT_EMPTY_DIFF, buf.ptr); + + git_patch_free(p); + git_buf_dispose(&buf); +} -- cgit v1.2.1