16
#include "revision.h"
17
#include "quote.h"
18
#include "diff.h"
19
+#include "diff-hunks.h"
20
#include "diffcore.h"
21
#include "delta.h"
22
#include "hex.h"
2930
return x;
2931
}
2932
2933
+struct diffstat_hunk_cb_data {
2934
+ struct precomputed_hunk **h;
2935
+ size_t *nr, *alloc;
2936
+};
2937
+
2938
+/*
2939
+ * Hunk callback that appends each hunk's coordinates to a growable
2940
+ * array, so one xdiff pass can both sum a diffstat and record hunks for
2941
+ * the store.
2942
+ */
2943
+static int diffstat_hunk_cb(long start_a, long count_a,
2944
+ long start_b, long count_b,
2945
+ void *cb_data)
2946
+{
2947
+ struct diffstat_hunk_cb_data *d = cb_data;
2948
+
2949
+ ALLOC_GROW(*d->h, *d->nr + 1, *d->alloc);
2950
+ (*d->h)[*d->nr].old_start = start_a;
2951
+ (*d->h)[*d->nr].old_count = count_a;
2952
+ (*d->h)[*d->nr].new_start = start_b;
2953
+ (*d->h)[*d->nr].new_count = count_b;
2954
+ (*d->nr)++;
2955
+ return 0;
2956
+}
2957
+
2958
+/*
2959
+ * Collect the hunks of the two files at zero context. diff_fn chooses
2960
+ * whether trimming runs: xdi_diff applies trim_common_tail, yielding the
2961
+ * zero-context hunks blame reads; xdl_diff does not, yielding the
2962
+ * untrimmed hunks. Both run at zero context, so the untrimmed hunks are
2963
+ * not grouped the way a nonzero context would group them; diffstat only
2964
+ * sums their counts, which grouping does not change. Sets *ph (caller
2965
+ * frees) and *ph_nr.
2966
+ */
2967
+typedef int (*xdiff_fn)(mmfile_t *, mmfile_t *, xpparam_t const *,
2968
+ xdemitconf_t const *, xdemitcb_t *);
2969
+static int collect_hunks(xdiff_fn diff_fn, mmfile_t *mf1, mmfile_t *mf2,
2970
+ xpparam_t *xpp, struct precomputed_hunk **ph,
2971
+ size_t *ph_nr)
2972
+{
2973
+ size_t ph_alloc = 0;
2974
+ xdemitcb_t ecb = { 0 };
2975
+ xdemitconf_t xecfg = { 0 };
2976
+ struct diffstat_hunk_cb_data cd = { ph, ph_nr, &ph_alloc };
2977
+
2978
+ *ph = NULL;
2979
+ *ph_nr = 0;
2980
+ xecfg.hunk_func = diffstat_hunk_cb;
2981
+ ecb.priv = &cd;
2982
+ return diff_fn(mf1, mf2, xpp, &xecfg, &ecb);
2983
+}
2984
+
2985
+void diff_hunks_attach(struct diff_options *o)
2986
+{
2987
+ if (!(o->output_format &
2988
+ (DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_NUMSTAT)))
2989
+ return;
2990
+ o->hunks_writer = diff_hunks_writer_maybe_new(o->repo);
2991
+}
2992
+
2993
+void diff_hunks_detach(struct diff_options *o)
2994
+{
2995
+ diff_hunks_writer_finish(o->hunks_writer);
2996
+ o->hunks_writer = NULL;
2997
+}
2998
+
2999
static int diffstat_consume(void *priv, char *line, unsigned long len)
3000
{
3001
struct diffstat_t *diffstat = priv;
4320
return NULL;
4321
}
4322
4323
+/*
4324
+ * Fill data->added/deleted for a modified pair by collecting its hunk
4325
+ * coordinates, and record them into the store. Runs only on a warming
4326
+ * run; returns 1 when it produced the counts, 0 when the caller must
4327
+ * compute the diffstat itself.
4328
+ *
4329
+ * --ignore-blank-lines is excluded: that flag is part of the store
4330
+ * key, but it coalesces hunks differently between the emit and
4331
+ * hunk-callback paths, so a recorded entry would not match a
4332
+ * store-less run's --stat output. (--inter-hunk-context is not
4333
+ * excluded: it only groups hunks, and diffstat sums their counts,
4334
+ * which grouping does not change.) Recording requires both sides to
4335
+ * be valid regular files whose blobs the key can name.
4336
+ */
4337
+static int diffstat_from_hunks(struct diff_options *o,
4338
+ struct diff_filespec *one,
4339
+ struct diff_filespec *two,
4340
+ struct diffstat_file *data)
4341
+{
4342
+ struct precomputed_hunk *ph_trim, *ph_full, *counts;
4343
+ size_t n_trim, n_full, n_counts, k;
4344
+ mmfile_t mf1, mf2;
4345
+ xpparam_t xpp = { .flags = o->xdl_opts,
4346
+ .ignore_regex = o->ignore_regex,
4347
+ .ignore_regex_nr = o->ignore_regex_nr,
4348
+ .anchors = o->anchors,
4349
+ .anchors_nr = o->anchors_nr };
4350
+
4351
+ if (o->xdl_opts & XDF_IGNORE_BLANK_LINES)
4352
+ return 0;
4353
+
4354
+ /* Not a warming run: the caller computes the diffstat. */
4355
+ if (!o->hunks_writer)
4356
+ return 0;
4357
+ /*
4358
+ * -I patterns, --anchored anchors, and break detection (-B)
4359
+ * shape the diff outside the store key, so what they compute
4360
+ * must not be recorded under it.
4361
+ */
4362
+ if (o->ignore_regex_nr || o->anchors_nr || o->break_opt != -1)
4363
+ return 0;
4364
+ /* Recording needs blobs the key can name, on both sides. */
4365
+ if (!one->oid_valid || !two->oid_valid ||
4366
+ S_ISGITLINK(one->mode) || S_ISGITLINK(two->mode) ||
4367
+ !DIFF_FILE_VALID(one) || !DIFF_FILE_VALID(two) ||
4368
+ !S_ISREG(one->mode) || !S_ISREG(two->mode))
4369
+ return 0;
4370
+
4371
+ if (fill_mmfile(o->repo, &mf1, one) < 0 ||
4372
+ fill_mmfile(o->repo, &mf2, two) < 0)
4373
+ die("unable to read files to diff");
4374
+
4375
+ /*
4376
+ * Compute the zero-context trimmed diff (what blame reads) and the
4377
+ * untrimmed diff (whose counts a nonzero-context stat matches).
4378
+ * xdi_diff runs first: it enforces the size limit, so the xdl_diff
4379
+ * call is already bounded.
4380
+ */
4381
+ if (collect_hunks(xdi_diff, &mf1, &mf2, &xpp, &ph_trim, &n_trim) ||
4382
+ collect_hunks(xdl_diff, &mf1, &mf2, &xpp, &ph_full, &n_full))
4383
+ die("unable to generate diffstat for %s", one->path);
4384
+
4385
+ /*
4386
+ * Match a store-less run: at zero context xdi_diff trims, so sum the
4387
+ * trimmed diff; otherwise sum the untrimmed one.
4388
+ */
4389
+ counts = o->context ? ph_full : ph_trim;
4390
+ n_counts = o->context ? n_full : n_trim;
4391
+ for (k = 0; k < n_counts; k++) {
4392
+ data->added += counts[k].new_count;
4393
+ data->deleted += counts[k].old_count;
4394
+ }
4395
+
4396
+ diff_hunks_writer_record_stable(o->hunks_writer, &one->oid, &two->oid,
4397
+ o->xdl_opts, ph_trim, n_trim,
4398
+ ph_full, n_full);
4399
+ free(ph_trim);
4400
+ free(ph_full);
4401
+ return 1;
4402
+}
4403
+
4404
static void builtin_diffstat(const char *name_a, const char *name_b,
4405
struct diff_filespec *one,
4406
struct diff_filespec *two,
4452
}
4453
4454
else if (may_differ) {
4307
- /* Crazy xdl interfaces.. */
4308
- xpparam_t xpp;
4309
- xdemitconf_t xecfg;
4310
-
4311
- if (fill_mmfile(o->repo, &mf1, one) < 0 ||
4312
- fill_mmfile(o->repo, &mf2, two) < 0)
4313
- die("unable to read files to diff");
4314
-
4315
- memset(&xpp, 0, sizeof(xpp));
4316
- memset(&xecfg, 0, sizeof(xecfg));
4317
- xpp.flags = o->xdl_opts;
4318
- xpp.ignore_regex = o->ignore_regex;
4319
- xpp.ignore_regex_nr = o->ignore_regex_nr;
4320
- xpp.anchors = o->anchors;
4321
- xpp.anchors_nr = o->anchors_nr;
4322
- xecfg.ctxlen = o->context;
4323
- xecfg.interhunkctxlen = o->interhunkcontext;
4324
- xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
4325
-
4326
- if (p->line_ranges) {
4327
- struct line_range_filter lr_filter;
4328
-
4329
- line_range_filter_init(&lr_filter, p->line_ranges,
4330
- diffstat_consume, diffstat);
4455
+ /*
4456
+ * Record into the diff-hunks store on a warming run. A
4457
+ * "log -L" range-scoped stat is not the whole-pair diff
4458
+ * the store keys, so it does not record. Otherwise diff
4459
+ * normally.
4460
+ */
4461
+ if (p->line_ranges || !diffstat_from_hunks(o, one, two, data)) {
4462
+ /* Crazy xdl interfaces.. */
4463
+ xpparam_t xpp;
4464
+ xdemitconf_t xecfg;
4465
+
4466
+ if (fill_mmfile(o->repo, &mf1, one) < 0 ||
4467
+ fill_mmfile(o->repo, &mf2, two) < 0)
4468
+ die("unable to read files to diff");
4469
+
4470
+ memset(&xpp, 0, sizeof(xpp));
4471
+ memset(&xecfg, 0, sizeof(xecfg));
4472
+ xpp.flags = o->xdl_opts;
4473
+ xpp.ignore_regex = o->ignore_regex;
4474
+ xpp.ignore_regex_nr = o->ignore_regex_nr;
4475
+ xpp.anchors = o->anchors;
4476
+ xpp.anchors_nr = o->anchors_nr;
4477
+ xecfg.ctxlen = o->context;
4478
+ xecfg.interhunkctxlen = o->interhunkcontext;
4479
+ xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
4480
4332
- if (line_range_filter_diff(&lr_filter, &mf1, &mf2,
4333
- &xpp, &xecfg))
4481
+ if (p->line_ranges) {
4482
+ struct line_range_filter lr_filter;
4483
+
4484
+ line_range_filter_init(&lr_filter,
4485
+ p->line_ranges,
4486
+ diffstat_consume,
4487
+ diffstat);
4488
+
4489
+ if (line_range_filter_diff(&lr_filter, &mf1,
4490
+ &mf2, &xpp, &xecfg))
4491
+ die("unable to generate diffstat for %s",
4492
+ one->path);
4493
+ } else if (xdi_diff_outf(&mf1, &mf2, NULL,
4494
+ diffstat_consume, diffstat,
4495
+ &xpp, &xecfg))
4496
die("unable to generate diffstat for %s",
4497
one->path);
4336
- } else if (xdi_diff_outf(&mf1, &mf2, NULL,
4337
- diffstat_consume, diffstat, &xpp, &xecfg))
4338
- die("unable to generate diffstat for %s", one->path);
4498
+ }
4499
4500
if (DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two)) {
4501
struct diffstat_file *file =