Re: [PATCH 1/5] xdiff: support external hunks via xpparam_t
- From
Michael Montalbo <mmontalbo@gmail.com>
- Date
- May 22, 2026, 19:06 UTC
- Message-ID
- <CAC2QwmKkwnr+TvLDnDuLEvGJeoraB=_YWC6idA57dxUqQ_5Fcg@mail.gmail.com>
- In-Reply-To
- <xmqq33zkui4q.fsf@gitster.g>
On Thu, May 21, 2026 at 10:29 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 36 quoted lines
>
> "Michael Montalbo via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > +/*
> > + * Populate the changed[] arrays from externally supplied hunks,
> > + * bypassing the diff algorithm. Validates that hunks are in order,
> > + * non-overlapping, and within bounds.
> > + *
> > + * Returns 0 on success, -1 on validation failure.
> > + */
> > +static int xdl_populate_hunks_from_external(xdfenv_t *xe,
> > + const struct xdl_hunk *hunks,
> > + size_t nr_hunks)
> > +{
> > + size_t i;
> > + long j, prev_old_end = 0, prev_new_end = 0;
> > + long total_old = 0, total_new = 0;
> > +
> > + /*
> > + * Clear changed[] arrays. xdl_prepare_env() may have dirtied
> > + * them via xdl_cleanup_records(). The allocation is nrec + 2
> > + * elements; changed points one past the start (see xprepare.c).
> > + */
> > + memset(xe->xdf1.changed - 1, 0,
> > + (xe->xdf1.nrec + 2) * sizeof(bool));
> > + memset(xe->xdf2.changed - 1, 0,
> > + (xe->xdf2.nrec + 2) * sizeof(bool));
>
> This, especially the starting offset of -1, looks horrible. The
> internal layout of xdfenv_t might happen to match the way the above
> code expects, which is how xdl_prepare_ctx() may have give you, but
> it somehow feels brittle. I guess the assumption that changed[]
> does not point at the beginning of the allocated area (e.g., it is a
> no-no to free(xe->xdf1.changed) or realloc() it) is so pervasive that
> it cannot be helped. Sigh.
>Agreed it is ugly. I wanted to make sure the entire changed[] including sentinels were clear as a defensive measure for downstream callers (xdl_change_compact). I agree this results in something that is ugly and brittle, but in the end I thought it was superior to relying on the fact that upstream zeroes the entire changed[] array. Maybe if the comment was more explicit about why this is happening it would be helpful?
/*
* Clear changed[] arrays including sentinels.
* xdl_prepare_env() may have dirtied them via
* xdl_cleanup_records(), and xdl_change_compact() reads
* the sentinel at changed[-1] during backward scans.
*/Show 34 quoted lines
> > int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
> > xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
> > xdchange_t *xscr;
> > xdfenv_t xe;
> > emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff;
> >
> > - if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
> > -
> > - return -1;
> > + if (xpp->external_hunks) {
> > + if (xdl_prepare_env(mf1, mf2, xpp, &xe) < 0)
> > + return -1;
> > + if (xdl_populate_hunks_from_external(&xe,
> > + xpp->external_hunks,
> > + xpp->external_hunks_nr) < 0) {
> > + /*
> > + * Invalid external hunks; fall back to the
> > + * builtin diff algorithm. Re-runs
> > + * xdl_prepare_env() via xdl_do_diff().
> > + */
> > + xdl_free_env(&xe);
> > + if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0)
> > + return -1;
>
> If the external tool keeps sending bogus hunks, silently falling
> back to what we would have done if there weren't any external stuff
> may be necessary to pleasantly keep using Git, but two and a half
> short comments here.
>
> (1) "What we would have done" is exactly the same as what appears
> in the corresponding "else" block. Can we make sure that we do
> not have to keep updating both copies in the future with some
> code rearrangement?
>How about something like this:
if (xpp->external_hunks) {
if (xdl_prepare_env(mf1, mf2, xpp, &xe) < 0)
return -1;
if (xdl_populate_hunks_from_external(&xe,
xpp->external_hunks,
xpp->external_hunks_nr) == 0)
goto diff_done;
xdl_free_env(&xe);
} if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0)
return -1;diff_done:
> (2) The writer of the external tool may want to see some trace of > warning under certain flags when a failure of the tool forces > the receiving end to fallback. >
In diff.c how about we emit a warning rather than a trace on fallback:
warning(_("diff process failed for '%s',"
" falling back to builtin diff"),
name_a);> (3) If the tool throws too many broken replies, perhaps we want to > disable it automatically? >
For the RFC I wanted to keep it simple, but I definitely agree. A configurable failure policy makes a lot of sense to me (e.g., disable after N failures).
Show 9 quoted lines
> > + }
> > + } else {
> > + if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0)
> > + return -1;
> > }
> > +
> > if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
> > xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
> > xdl_build_script(&xe, &xscr) < 0) {