git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 1/2] diff: add -I<regex> that ignores matching changes

From
Michał Kępień <michal@isc.org>
Date
Oct 1, 2020, 12:06 UTC
Message-ID
<20201001120606.25773-2-michal@isc.org>
In-Reply-To
<20201001120606.25773-1-michal@isc.org>

Add a new diff option that enables ignoring changes whose all lines (changed, removed, and added) match a given regular expression. This is similar to the -I option in standalone diff utilities and can be used e.g. to look for unrelated changes in commits containing a large number of automatically applied modifications (e.g. a tree-wide string replacement). The difference between -G/-S and the new -I option is that the latter filters output on a per-change basis.

Use the 'ignore' field of xdchange_t for marking a change as ignored or not. Since the same field is used by --ignore-blank-lines, identical hunk emitting rules apply for --ignore-blank-lines and -I. These two options can also be used together at the same time.

Apart from adding a new field to struct diff_options, also define a new flag, XDF_IGNORE_REGEX, for the 'xdl_opts' field of that structure. This is done because the xpparam_t structure (which is used for passing around the regular expression supplied to -I) is not zeroed out in all call stacks involving xdl_diff() and thus only performing a NULL check on xpp->ignore_regex could result in xdl_mark_ignorable_regex() treating garbage on the stack as a regular expression. As the 'flags' field of xpparam_t is initialized in all call stacks involving xdl_diff(), adding a flag check avoids that problem.

Signed-off-by: Michał Kępień <michal@isc.org>
---
 Documentation/diff-options.txt |  3 +++
 diff.c                         | 16 +++++++++++++++
 diff.h                         |  2 ++
 xdiff/xdiff.h                  |  2 ++
 xdiff/xdiffi.c                 | 36 ++++++++++++++++++++++++++++++++++
 5 files changed, 59 insertions(+)
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 573fb9bb71..a7ef3f5645 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -687,6 +687,9 @@ endif::git-format-patch[]
 --ignore-blank-lines::
 	Ignore changes whose lines are all blank.
 
+-I<regex>::
+	Ignore changes whose all lines match <regex>.
+
 --inter-hunk-context=<lines>::
 	Show the context between diff hunks, up to the specified number
 	of lines, thereby fusing hunks that are close to each other.
diff --git a/diff.c b/diff.c
index 2bb2f8f57e..c9603c941e 100644
--- a/diff.c
+++ b/diff.c
@@ -3587,6 +3587,7 @@ static void builtin_diff(const char *name_a,
 		if (header.len && !o->flags.suppress_diff_headers)
 			ecbdata.header = &header;
 		xpp.flags = o->xdl_opts;
+		xpp.ignore_regex = o->ignore_regex;
 		xpp.anchors = o->anchors;
 		xpp.anchors_nr = o->anchors_nr;
 		xecfg.ctxlen = o->context;
@@ -3716,6 +3717,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
 		memset(&xpp, 0, sizeof(xpp));
 		memset(&xecfg, 0, sizeof(xecfg));
 		xpp.flags = o->xdl_opts;
+		xpp.ignore_regex = o->ignore_regex;
 		xpp.anchors = o->anchors;
 		xpp.anchors_nr = o->anchors_nr;
 		xecfg.ctxlen = o->context;
@@ -5203,6 +5205,17 @@ static int diff_opt_patience(const struct option *opt,
 	return 0;
 }
 
+static int diff_opt_ignore_regex(const struct option *opt,
+				 const char *arg, int unset)
+{
+	struct diff_options *options = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	options->xdl_opts |= XDF_IGNORE_REGEX;
+	options->ignore_regex = arg;
+	return 0;
+}
+
 static int diff_opt_pickaxe_regex(const struct option *opt,
 				  const char *arg, int unset)
 {
@@ -5491,6 +5504,9 @@ static void prep_parse_options(struct diff_options *options)
 		OPT_BIT_F(0, "ignore-blank-lines", &options->xdl_opts,
 			  N_("ignore changes whose lines are all blank"),
 			  XDF_IGNORE_BLANK_LINES, PARSE_OPT_NONEG),
+		OPT_CALLBACK_F('I', NULL, options, N_("<regex>"),
+			       N_("ignore changes whose all lines match <regex>"),
+			       0, diff_opt_ignore_regex),
 		OPT_BIT(0, "indent-heuristic", &options->xdl_opts,
 			N_("heuristic to shift diff hunk boundaries for easy reading"),
 			XDF_INDENT_HEURISTIC),
diff --git a/diff.h b/diff.h
index 3de343270f..0b8871c0c1 100644
--- a/diff.h
+++ b/diff.h
@@ -234,6 +234,8 @@ struct diff_options {
 	 */
 	const char *pickaxe;
 
+	const char *ignore_regex;
+
 	const char *single_follow;
 	const char *a_prefix, *b_prefix;
 	const char *line_prefix;
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 032e3a9f41..db28055a5e 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -40,6 +40,7 @@ extern "C" {
 			      XDF_IGNORE_CR_AT_EOL)
 
 #define XDF_IGNORE_BLANK_LINES (1 << 7)
+#define XDF_IGNORE_REGEX (1 << 8)
 
 #define XDF_PATIENCE_DIFF (1 << 14)
 #define XDF_HISTOGRAM_DIFF (1 << 15)
@@ -78,6 +79,7 @@ typedef struct s_mmbuffer {
 
 typedef struct s_xpparam {
 	unsigned long flags;
+	const char *ignore_regex;
 
 	/* See Documentation/diff-options.txt. */
 	char **anchors;
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index bd035139f9..13f7ab95ac 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -1019,6 +1019,39 @@ static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
 	}
 }
 
+static void
+xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,
+			 const char *ignore_regex)
+{
+	xdchange_t *xch;
+	regex_t regex;
+
+	if (regcomp(&regex, ignore_regex, REG_EXTENDED | REG_NEWLINE))
+		die("invalid regex: %s", ignore_regex);
+
+	for (xch = xscr; xch; xch = xch->next) {
+		regmatch_t regmatch;
+		xrecord_t **rec;
+		int ignore = 1;
+		long i;
+
+		rec = &xe->xdf1.recs[xch->i1];
+		for (i = 0; i < xch->chg1 && ignore; i++)
+			ignore = !regexec_buf(&regex, rec[i]->ptr, rec[i]->size,
+					      1, &regmatch, 0);
+
+		rec = &xe->xdf2.recs[xch->i2];
+		for (i = 0; i < xch->chg2 && ignore; i++)
+			ignore = !regexec_buf(&regex, rec[i]->ptr, rec[i]->size,
+					      1, &regmatch, 0);
+
+		/*
+		 * Do not override --ignore-blank-lines.
+		 */
+		xch->ignore |= ignore;
+	}
+}
+
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
 	xdchange_t *xscr;
@@ -1040,6 +1073,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 		if (xpp->flags & XDF_IGNORE_BLANK_LINES)
 			xdl_mark_ignorable(xscr, &xe, xpp->flags);
 
+		if ((xpp->flags & XDF_IGNORE_REGEX) && xpp->ignore_regex)
+			xdl_mark_ignorable_regex(xscr, &xe, xpp->ignore_regex);
+
 		if (ef(&xe, xscr, ecb, xecfg) < 0) {
 
 			xdl_free_script(xscr);
-- 
2.28.0
Previous: Michał KępieńNext: Junio C Hamano
Message 3 of 57 in “diff: add -I<regex> that ignores matching changes”
  1. 0/2 diff: add -I<regex> that ignores matching changesMichał Kępień, Oct 1, 2020
  2. 2/2 t: add -I<regex> testsMichał Kępień, Oct 1, 2020
  3. 1/2 diff: add -I<regex> that ignores matching changesMichał Kępień, Oct 1, 2020
  4. Junio C HamanoOct 1, 2020
  5. Michał KępieńOct 7, 2020
  6. Junio C HamanoOct 7, 2020
  7. Junio C HamanoOct 1, 2020
  8. 0/3 diff: add -I<regex> that ignores matching changesMichał Kępień, Oct 12, 2020
  9. 1/3 merge-base, xdiff: zero out xpparam_t structuresMichał Kępień, Oct 12, 2020
  10. Johannes SchindelinOct 12, 2020
  11. Junio C HamanoOct 12, 2020
  12. Junio C HamanoOct 12, 2020
  13. Michał KępieńOct 13, 2020
  14. 2/3 diff: add -I<regex> that ignores matching changesMichał Kępień, Oct 12, 2020
  15. Johannes SchindelinOct 12, 2020
  16. Junio C HamanoOct 12, 2020
  17. Johannes SchindelinOct 12, 2020
  18. Junio C HamanoOct 12, 2020
  19. Michał KępieńOct 13, 2020
  20. Junio C HamanoOct 13, 2020
  21. Michał KępieńOct 13, 2020
  22. Johannes SchindelinOct 13, 2020
  23. Junio C HamanoOct 13, 2020
  24. Michał KępieńOct 13, 2020
  25. Junio C HamanoOct 12, 2020
  26. Michał KępieńOct 13, 2020
  27. Junio C HamanoOct 12, 2020
  28. Michał KępieńOct 13, 2020
  29. 3/3 t: add -I<regex> testsMichał Kępień, Oct 12, 2020
  30. Johannes SchindelinOct 12, 2020
  31. Michał KępieńOct 13, 2020
  32. Johannes SchindelinOct 13, 2020
  33. Junio C HamanoOct 13, 2020
  34. Michał KępieńOct 13, 2020
  35. Johannes SchindelinOct 15, 2020
  36. 0/2 diff: add -I<regex> that ignores matching changesMichał Kępień, Oct 15, 2020
  37. 1/2 merge-base, xdiff: zero out xpparam_t structuresMichał Kępień, Oct 15, 2020
  38. 2/2 diff: add -I<regex> that ignores matching changesMichał Kępień, Oct 15, 2020
  39. Phillip WoodOct 16, 2020
  40. Junio C HamanoOct 16, 2020
  41. Michał KępieńOct 19, 2020
  42. Junio C HamanoOct 16, 2020
  43. Michał KępieńOct 19, 2020
  44. Junio C HamanoOct 19, 2020
  45. Johannes SchindelinOct 16, 2020
  46. 0/2 diff: add -I<regex> that ignores matching changesMichał Kępień, Oct 20, 2020
  47. 1/2 merge-base, xdiff: zero out xpparam_t structuresMichał Kępień, Oct 20, 2020
  48. 2/2 diff: add -I<regex> that ignores matching changesMichał Kępień, Oct 20, 2020
  49. 1/2 diff: add an API for deferred freeingÆvar Arnfjörð Bjarmason, Feb 5, 2021
  50. Johannes SchindelinFeb 10, 2021
  51. Ævar Arnfjörð BjarmasonFeb 11, 2021
  52. Johannes SchindelinFeb 11, 2021
  53. Jeff KingFeb 11, 2021
  54. 2/2 diff: plug memory leak from regcomp() on {log,diff} -IÆvar Arnfjörð Bjarmason, Feb 11, 2021
  55. 0/2 diff: add an API for deferred freeingÆvar Arnfjörð Bjarmason, Feb 11, 2021
  56. 1/2 diff: add an API for deferred freeingÆvar Arnfjörð Bjarmason, Feb 11, 2021
  57. 2/2 diff: plug memory leak from regcomp() on {log,diff} -IÆvar Arnfjörð Bjarmason, Feb 5, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.