diff: support regexp ignores

Emulate git's `-I` (`--ignore-matching-lines`) with a regular
expression.
This commit is contained in:
Edward Thomson
2022-01-29 15:41:53 -05:00
parent f85a69aed5
commit 50887e521f
7 changed files with 133 additions and 14 deletions

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}