Git development
 help / color / mirror / Atom feed
From: "Michael Montalbo via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Michael Montalbo <mmontalbo@gmail.com>,
	Michael Montalbo <mmontalbo@gmail.com>
Subject: [PATCH v3 1/7] diff: rename line-range filter struct and clarify fields
Date: Thu, 03 Sep 2026 05:05:13 +0000	[thread overview]
Message-ID: <331c6c92f3b54f7ee95ef5fe4db375b99b9be146.1788411919.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2152.v3.git.1788411919.gitgitgadget@gmail.com>

From: Michael Montalbo <mmontalbo@gmail.com>

diff's line-range filtering logic uses the line_range_callback
struct to represent filtering state. However, this name does not
clearly reflect the role it plays. This is especially relevant as
we expand diff's line-range filtering to work with more options,
including --stat and -G.

Also, line_range_callback's fields are terse, while the comment
explaining line_range_callback is verbose and out of place compared to
its surroundings.

Rename line_range_callback to line_range_filter, and replace the verbose
comment with a concise one, instead preferring descriptive field and
variable names that are self-explanatory over comments.

No logical behavior change. Some fields are grouped under a new struct
in the newly renamed line_range_filter. Everything else is just a
rename.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
---
 diff.c | 270 ++++++++++++++++++++++++---------------------------------
 1 file changed, 114 insertions(+), 156 deletions(-)

diff --git a/diff.c b/diff.c
index 414532d09f..679a0e27d4 100644
--- a/diff.c
+++ b/diff.c
@@ -610,49 +610,31 @@ struct emit_callback {
 };
 
 /*
- * State for the line-range callback wrappers that sit between
- * xdi_diff_outf() and fn_out_consume().  xdiff produces a normal,
- * unfiltered diff; the wrappers intercept each hunk header and line,
- * track post-image position, and forward only lines that fall within
- * the requested ranges.  Contiguous in-range lines are collected into
- * range hunks and flushed with a synthetic @@ header so that
- * fn_out_consume() sees well-formed unified-diff fragments.
- *
- * Removal lines ('-') cannot be classified by post-image position, so
- * they are buffered in pending_rm until the next '+' or ' ' line
- * reveals whether they precede an in-range line (flush into range hunk) or
- * an out-of-range line (discard).
+ * Filter the line ranges that are emitted by diff.
  */
-struct line_range_callback {
+struct line_range_filter {
 	xdiff_emit_line_fn orig_line_fn;
 	void *orig_cb_data;
-	const struct range_set *ranges;	/* 0-based [start, end) */
-	unsigned int cur_range;		/* index into the range_set */
-
-	/* Post/pre-image line counters (1-based, set from hunk headers) */
-	long lno_post;
-	long lno_pre;
+	const struct range_set *range_sets_to_filter_by;
+	unsigned int range_set_idx;
+
+	struct {
+		char func_name[80];
+		long func_name_len;
+		long old_begin, old_count;
+		long new_begin, new_count;
+		long lno_in_preimage;
+		long lno_in_postimage;
+		struct strbuf lines;
+		int active;
+		int has_changes;
+	} accumulating_hunk;
 
-	/*
-	 * Function name from most recent xdiff hunk header;
-	 * size matches struct func_line.buf in xdiff/xemit.c.
-	 */
-	char func[80];
-	long funclen;
-
-	/* Range hunk being accumulated for the current range */
-	struct strbuf rhunk;
-	long rhunk_old_begin, rhunk_old_count;
-	long rhunk_new_begin, rhunk_new_count;
-	int rhunk_active;
-	int rhunk_has_changes;		/* any '+' or '-' lines? */
-
-	/* Removal lines not yet known to be in-range */
 	struct strbuf pending_rm;
 	int pending_rm_count;
-	long pending_rm_pre_begin;	/* pre-image line of first pending */
+	long pending_rm_pre_begin;
 
-	int ret;			/* latched error from orig_line_fn */
+	int ret;
 };
 
 static int count_lines(const char *data, int size)
@@ -2540,69 +2522,59 @@ static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED
 	return 1;
 }
 
-static void discard_pending_rm(struct line_range_callback *s)
+static void discard_pending_rm(struct line_range_filter *filter)
 {
-	strbuf_reset(&s->pending_rm);
-	s->pending_rm_count = 0;
+	strbuf_reset(&filter->pending_rm);
+	filter->pending_rm_count = 0;
 }
 
-static void flush_rhunk(struct line_range_callback *s)
+static void flush_range_hunk(struct line_range_filter *filter)
 {
 	struct strbuf hdr = STRBUF_INIT;
-	const char *p, *end;
+	const char *line_buf, *line_buf_end;
 
-	if (!s->rhunk_active || s->ret)
+	if (!filter->accumulating_hunk.active || filter->ret)
 		return;
 
-	/* Drain any pending removal lines into the range hunk */
-	if (s->pending_rm_count) {
-		strbuf_addbuf(&s->rhunk, &s->pending_rm);
-		s->rhunk_old_count += s->pending_rm_count;
-		s->rhunk_has_changes = 1;
-		discard_pending_rm(s);
+	if (filter->pending_rm_count) {
+		strbuf_addbuf(&filter->accumulating_hunk.lines, &filter->pending_rm);
+		filter->accumulating_hunk.old_count += filter->pending_rm_count;
+		filter->accumulating_hunk.has_changes = 1;
+		discard_pending_rm(filter);
 	}
 
-	/*
-	 * Suppress context-only hunks: they contain no actual changes
-	 * and would just be noise.  This can happen when the inflated
-	 * ctxlen causes xdiff to emit context covering a range that
-	 * has no changes in this commit.
-	 */
-	if (!s->rhunk_has_changes) {
-		s->rhunk_active = 0;
-		strbuf_reset(&s->rhunk);
+	if (!filter->accumulating_hunk.has_changes) {
+		filter->accumulating_hunk.active = 0;
+		strbuf_reset(&filter->accumulating_hunk.lines);
 		return;
 	}
 
 	strbuf_addf(&hdr, "@@ -%ld,%ld +%ld,%ld @@",
-		    s->rhunk_old_begin, s->rhunk_old_count,
-		    s->rhunk_new_begin, s->rhunk_new_count);
-	if (s->funclen > 0) {
+		    filter->accumulating_hunk.old_begin, filter->accumulating_hunk.old_count,
+		    filter->accumulating_hunk.new_begin, filter->accumulating_hunk.new_count);
+	if (filter->accumulating_hunk.func_name_len > 0) {
 		strbuf_addch(&hdr, ' ');
-		strbuf_add(&hdr, s->func, s->funclen);
+		strbuf_add(&hdr, filter->accumulating_hunk.func_name,
+	     filter->accumulating_hunk.func_name_len);
 	}
 	strbuf_addch(&hdr, '\n');
 
-	s->ret = s->orig_line_fn(s->orig_cb_data, hdr.buf, hdr.len);
+	filter->ret = filter->orig_line_fn(filter->orig_cb_data, hdr.buf, hdr.len);
 	strbuf_release(&hdr);
 
-	/*
-	 * Replay buffered lines one at a time through fn_out_consume.
-	 * The cast discards const because xdiff_emit_line_fn takes
-	 * char *, though fn_out_consume does not modify the buffer.
-	 */
-	p = s->rhunk.buf;
-	end = p + s->rhunk.len;
-	while (!s->ret && p < end) {
-		const char *eol = memchr(p, '\n', end - p);
-		unsigned long line_len = eol ? (unsigned long)(eol - p + 1)
-					     : (unsigned long)(end - p);
-		s->ret = s->orig_line_fn(s->orig_cb_data, (char *)p, line_len);
-		p += line_len;
+	line_buf = filter->accumulating_hunk.lines.buf;
+	line_buf_end = line_buf + filter->accumulating_hunk.lines.len;
+	while (!filter->ret && line_buf < line_buf_end) {
+		const char *eol = memchr(line_buf, '\n', line_buf_end - line_buf);
+		unsigned long line_len = eol ? (unsigned long)(eol - line_buf + 1)
+					     : (unsigned long)(line_buf_end - line_buf);
+		filter->ret = filter->orig_line_fn(filter->orig_cb_data,
+				     (char *)line_buf, line_len);
+		line_buf += line_len;
 	}
 
-	s->rhunk_active = 0;
-	strbuf_reset(&s->rhunk);
+	filter->accumulating_hunk.active = 0;
+	strbuf_reset(&filter->accumulating_hunk.lines);
 }
 
 static void line_range_hunk_fn(void *data,
@@ -2610,116 +2582,102 @@ static void line_range_hunk_fn(void *data,
 			       long new_begin, long new_nr UNUSED,
 			       const char *func, long funclen)
 {
-	struct line_range_callback *s = data;
+	struct line_range_filter *filter = data;
 
-	/*
-	 * When count > 0, begin is 1-based.  When count == 0, begin is
-	 * adjusted down by 1 by xdl_emit_hunk_hdr(), but no lines of
-	 * that type will arrive, so the value is unused.
-	 *
-	 * Any pending removal lines from the previous xdiff hunk are
-	 * intentionally left in pending_rm: the line callback will
-	 * flush or discard them when the next content line reveals
-	 * whether the removals precede in-range content.
-	 */
-	s->lno_post = new_begin;
-	s->lno_pre = old_begin;
+	filter->accumulating_hunk.lno_in_postimage = new_begin;
+	filter->accumulating_hunk.lno_in_preimage = old_begin;
 
 	if (funclen > 0) {
-		if (funclen > (long)sizeof(s->func))
-			funclen = sizeof(s->func);
-		memcpy(s->func, func, funclen);
+		if (funclen > (long)sizeof(filter->accumulating_hunk.func_name))
+			funclen = sizeof(filter->accumulating_hunk.func_name);
+		memcpy(filter->accumulating_hunk.func_name, func, funclen);
 	}
-	s->funclen = funclen;
+	filter->accumulating_hunk.func_name_len = funclen;
 }
 
 static int line_range_line_fn(void *priv, char *line, unsigned long len)
 {
-	struct line_range_callback *s = priv;
+	struct line_range_filter *filter = priv;
 	const struct range *cur;
-	long lno_0, cur_pre;
+	long idx_in_postimage, cur_pre;
 
-	if (s->ret)
-		return s->ret;
+	if (filter->ret)
+		return filter->ret;
 
 	if (line[0] == '-') {
-		if (!s->pending_rm_count)
-			s->pending_rm_pre_begin = s->lno_pre;
-		s->lno_pre++;
-		strbuf_add(&s->pending_rm, line, len);
-		s->pending_rm_count++;
-		return s->ret;
+		if (!filter->pending_rm_count)
+			filter->pending_rm_pre_begin =
+				filter->accumulating_hunk.lno_in_preimage;
+		filter->accumulating_hunk.lno_in_preimage++;
+		strbuf_add(&filter->pending_rm, line, len);
+		filter->pending_rm_count++;
+		return filter->ret;
 	}
 
 	if (line[0] == '\\') {
-		if (s->pending_rm_count)
-			strbuf_add(&s->pending_rm, line, len);
-		else if (s->rhunk_active)
-			strbuf_add(&s->rhunk, line, len);
-		/* otherwise outside tracked range; drop silently */
-		return s->ret;
+		if (filter->pending_rm_count)
+			strbuf_add(&filter->pending_rm, line, len);
+		else if (filter->accumulating_hunk.active)
+			strbuf_add(&filter->accumulating_hunk.lines, line, len);
+		return filter->ret;
 	}
 
 	if (line[0] != '+' && line[0] != ' ')
 		BUG("unexpected diff line type '%c'", line[0]);
 
-	lno_0 = s->lno_post - 1;
-	cur_pre = s->lno_pre;	/* save before advancing for context lines */
-	s->lno_post++;
+	idx_in_postimage = filter->accumulating_hunk.lno_in_postimage - 1;
+	cur_pre = filter->accumulating_hunk.lno_in_preimage;
+	filter->accumulating_hunk.lno_in_postimage++;
 	if (line[0] == ' ')
-		s->lno_pre++;
+		filter->accumulating_hunk.lno_in_preimage++;
 
-	/* Advance past ranges we've passed */
-	while (s->cur_range < s->ranges->nr &&
-	       lno_0 >= s->ranges->ranges[s->cur_range].end) {
-		if (s->rhunk_active)
-			flush_rhunk(s);
-		discard_pending_rm(s);
-		s->cur_range++;
+	while (filter->range_set_idx < filter->range_sets_to_filter_by->nr &&
+	       idx_in_postimage >=
+		filter->range_sets_to_filter_by->ranges[filter->range_set_idx].end) {
+		if (filter->accumulating_hunk.active)
+			flush_range_hunk(filter);
+		discard_pending_rm(filter);
+		filter->range_set_idx++;
 	}
 
-	/* Past all ranges */
-	if (s->cur_range >= s->ranges->nr) {
-		discard_pending_rm(s);
-		return s->ret;
+	if (filter->range_set_idx >= filter->range_sets_to_filter_by->nr) {
+		discard_pending_rm(filter);
+		return filter->ret;
 	}
 
-	cur = &s->ranges->ranges[s->cur_range];
+	cur = &filter->range_sets_to_filter_by->ranges[filter->range_set_idx];
 
-	/* Before current range */
-	if (lno_0 < cur->start) {
-		discard_pending_rm(s);
-		return s->ret;
+	if (idx_in_postimage < cur->start) {
+		discard_pending_rm(filter);
+		return filter->ret;
 	}
 
-	/* In range so start a new range hunk if needed */
-	if (!s->rhunk_active) {
-		s->rhunk_active = 1;
-		s->rhunk_has_changes = 0;
-		s->rhunk_new_begin = lno_0 + 1;
-		s->rhunk_old_begin = s->pending_rm_count
-			? s->pending_rm_pre_begin : cur_pre;
-		s->rhunk_old_count = 0;
-		s->rhunk_new_count = 0;
-		strbuf_reset(&s->rhunk);
+	if (!filter->accumulating_hunk.active) {
+		filter->accumulating_hunk.active = 1;
+		filter->accumulating_hunk.has_changes = 0;
+		filter->accumulating_hunk.new_begin = idx_in_postimage + 1;
+		filter->accumulating_hunk.old_begin = filter->pending_rm_count
+			? filter->pending_rm_pre_begin : cur_pre;
+		filter->accumulating_hunk.old_count = 0;
+		filter->accumulating_hunk.new_count = 0;
+		strbuf_reset(&filter->accumulating_hunk.lines);
 	}
 
-	/* Flush pending removals into range hunk */
-	if (s->pending_rm_count) {
-		strbuf_addbuf(&s->rhunk, &s->pending_rm);
-		s->rhunk_old_count += s->pending_rm_count;
-		s->rhunk_has_changes = 1;
-		discard_pending_rm(s);
+	if (filter->pending_rm_count) {
+		strbuf_addbuf(&filter->accumulating_hunk.lines, &filter->pending_rm);
+		filter->accumulating_hunk.old_count += filter->pending_rm_count;
+		filter->accumulating_hunk.has_changes = 1;
+		discard_pending_rm(filter);
 	}
 
-	strbuf_add(&s->rhunk, line, len);
-	s->rhunk_new_count++;
+	strbuf_add(&filter->accumulating_hunk.lines, line, len);
+	filter->accumulating_hunk.new_count++;
 	if (line[0] == '+')
-		s->rhunk_has_changes = 1;
+		filter->accumulating_hunk.has_changes = 1;
 	else
-		s->rhunk_old_count++;
+		filter->accumulating_hunk.old_count++;
 
-	return s->ret;
+	return filter->ret;
 }
 
 static void pprint_rename(struct strbuf *name, const char *a, const char *b)
@@ -4066,15 +4024,15 @@ static void builtin_diff(const char *name_a,
 			xdi_diff_outf(&mf1, &mf2, NULL, quick_consume,
 				      &ecbdata, &xpp, &xecfg);
 		} else if (line_ranges) {
-			struct line_range_callback lr_state;
+			struct line_range_filter lr_state;
 			unsigned int i;
 			long max_span = 0;
 
 			memset(&lr_state, 0, sizeof(lr_state));
 			lr_state.orig_line_fn = fn_out_consume;
 			lr_state.orig_cb_data = &ecbdata;
-			lr_state.ranges = line_ranges;
-			strbuf_init(&lr_state.rhunk, 0);
+			lr_state.range_sets_to_filter_by = line_ranges;
+			strbuf_init(&lr_state.accumulating_hunk.lines, 0);
 			strbuf_init(&lr_state.pending_rm, 0);
 
 			/*
@@ -4105,11 +4063,11 @@ static void builtin_diff(const char *name_a,
 				die("unable to generate diff for %s",
 				    one->path);
 
-			flush_rhunk(&lr_state);
+			flush_range_hunk(&lr_state);
 			if (lr_state.ret)
 				die("unable to generate diff for %s",
 				    one->path);
-			strbuf_release(&lr_state.rhunk);
+			strbuf_release(&lr_state.accumulating_hunk.lines);
 			strbuf_release(&lr_state.pending_rm);
 		} else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
 					 &ecbdata, &xpp, &xecfg))
-- 
gitgitgadget


  reply	other threads:[~2026-09-03  5:05 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-18 18:16 [PATCH 0/7] line-log: range-scope stat, check, and -G under -L Michael Montalbo via GitGitGadget
2026-06-18 18:16 ` [PATCH 1/7] diff: rename and group the line-range filter for clarity Michael Montalbo via GitGitGadget
2026-06-18 18:16 ` [PATCH 2/7] diff: simplify the line-range filter by classifying removals immediately Michael Montalbo via GitGitGadget
2026-06-18 18:16 ` [PATCH 3/7] diff: emit -L hunk headers via xdiff's formatter Michael Montalbo via GitGitGadget
2026-06-18 18:16 ` [PATCH 4/7] diff: extract a line-range diff helper for reuse Michael Montalbo via GitGitGadget
2026-06-18 18:16 ` [PATCH 5/7] line-log: support diff stat formats with -L Michael Montalbo via GitGitGadget
2026-06-18 22:00   ` Junio C Hamano
2026-06-23  2:25     ` Michael Montalbo
2026-06-18 18:16 ` [PATCH 6/7] diff: support --check with -L line ranges Michael Montalbo via GitGitGadget
2026-06-18 18:16 ` [PATCH 7/7] diffcore-pickaxe: scope -G to the -L tracked range Michael Montalbo via GitGitGadget
2026-06-27 17:28 ` [PATCH v2 0/7] line-log: scope stat, check, and -G to -L line ranges Michael Montalbo via GitGitGadget
2026-06-27 17:28   ` [PATCH v2 1/7] diff: rename and group the line-range filter for clarity Michael Montalbo via GitGitGadget
2026-06-27 17:28   ` [PATCH v2 2/7] diff: simplify the line-range filter by classifying removals immediately Michael Montalbo via GitGitGadget
2026-06-27 17:28   ` [PATCH v2 3/7] diff: emit -L hunk headers via xdiff's formatter Michael Montalbo via GitGitGadget
2026-06-27 17:28   ` [PATCH v2 4/7] diff: extract a line-range diff helper for reuse Michael Montalbo via GitGitGadget
2026-06-27 17:28   ` [PATCH v2 5/7] line-log: support diff stat formats with -L Michael Montalbo via GitGitGadget
2026-06-27 17:29   ` [PATCH v2 6/7] diff: support --check with -L line ranges Michael Montalbo via GitGitGadget
2026-06-27 17:29   ` [PATCH v2 7/7] diffcore-pickaxe: scope -G to the -L tracked range Michael Montalbo via GitGitGadget
2026-07-15 21:05 ` [PATCH 0/7] line-log: range-scope stat, check, and -G under -L Michael Montalbo
2026-09-03  5:05 ` [PATCH v3 0/7] line-log: scope stat, check, and -G to -L line ranges Michael Montalbo via GitGitGadget
2026-09-03  5:05   ` Michael Montalbo via GitGitGadget [this message]
2026-09-03  5:05   ` [PATCH v3 2/7] diff: simplify the line-range filter by classifying removals immediately Michael Montalbo via GitGitGadget
2026-09-03  5:05   ` [PATCH v3 3/7] diff: emit -L hunk headers via xdiff's formatter Michael Montalbo via GitGitGadget
2026-09-03  5:05   ` [PATCH v3 4/7] diff: extract a line-range diff helper for reuse Michael Montalbo via GitGitGadget
2026-09-03  5:05   ` [PATCH v3 5/7] diff: support stat formats with -L Michael Montalbo via GitGitGadget
2026-09-03  5:05   ` [PATCH v3 6/7] diff: support --check with -L line ranges Michael Montalbo via GitGitGadget
2026-09-03  5:05   ` [PATCH v3 7/7] diffcore-pickaxe: limit -G to the -L tracked range Michael Montalbo via GitGitGadget

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=331c6c92f3b54f7ee95ef5fe4db375b99b9be146.1788411919.git.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=mmontalbo@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox