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
next prev parent 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.