Coverage Report

Created: 2026-10-17 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/line-log.c
Line
Count
Source
1
#define DISABLE_SIGN_COMPARE_WARNINGS
2
3
#include "git-compat-util.h"
4
#include "diffcore.h"
5
#include "line-range.h"
6
#include "hex.h"
7
#include "tag.h"
8
#include "tree.h"
9
#include "diff.h"
10
#include "commit.h"
11
#include "decorate.h"
12
#include "repository.h"
13
#include "revision.h"
14
#include "xdiff-interface.h"
15
#include "strbuf.h"
16
#include "line-log.h"
17
#include "setup.h"
18
#include "strvec.h"
19
#include "bloom.h"
20
#include "tree-walk.h"
21
22
static void range_set_grow(struct range_set *rs, size_t extra)
23
0
{
24
0
  ALLOC_GROW(rs->ranges, rs->nr + extra, rs->alloc);
25
0
}
26
27
/* Either initialization would be fine */
28
0
#define RANGE_SET_INIT {0}
29
30
void range_set_init(struct range_set *rs, size_t prealloc)
31
0
{
32
0
  rs->alloc = rs->nr = 0;
33
0
  rs->ranges = NULL;
34
0
  if (prealloc)
35
0
    range_set_grow(rs, prealloc);
36
0
}
37
38
void range_set_release(struct range_set *rs)
39
0
{
40
0
  FREE_AND_NULL(rs->ranges);
41
0
  rs->alloc = rs->nr = 0;
42
0
}
43
44
/* dst must be uninitialized! */
45
static void range_set_copy(struct range_set *dst, struct range_set *src)
46
0
{
47
0
  range_set_init(dst, src->nr);
48
0
  COPY_ARRAY(dst->ranges, src->ranges, src->nr);
49
0
  dst->nr = src->nr;
50
0
}
51
52
static void range_set_move(struct range_set *dst, struct range_set *src)
53
0
{
54
0
  range_set_release(dst);
55
0
  dst->ranges = src->ranges;
56
0
  dst->nr = src->nr;
57
0
  dst->alloc = src->alloc;
58
0
  src->ranges = NULL;
59
0
  src->alloc = src->nr = 0;
60
0
}
61
62
/* tack on a _new_ range _at the end_ */
63
void range_set_append_unsafe(struct range_set *rs, long a, long b)
64
0
{
65
0
  assert(a <= b);
66
0
  range_set_grow(rs, 1);
67
0
  rs->ranges[rs->nr].start = a;
68
0
  rs->ranges[rs->nr].end = b;
69
0
  rs->nr++;
70
0
}
71
72
void range_set_append(struct range_set *rs, long a, long b)
73
0
{
74
0
  assert(rs->nr == 0 || rs->ranges[rs->nr-1].end <= a);
75
0
  range_set_append_unsafe(rs, a, b);
76
0
}
77
78
static int range_cmp(const void *_r, const void *_s)
79
0
{
80
0
  const struct range *r = _r;
81
0
  const struct range *s = _s;
82
83
  /* this could be simply 'return r.start-s.start', but for the types */
84
0
  if (r->start == s->start)
85
0
    return 0;
86
0
  if (r->start < s->start)
87
0
    return -1;
88
0
  return 1;
89
0
}
90
91
/*
92
 * Check that the ranges are non-empty, sorted and non-overlapping
93
 */
94
static void range_set_check_invariants(struct range_set *rs)
95
0
{
96
0
  unsigned int i;
97
98
0
  if (!rs)
99
0
    return;
100
101
0
  if (rs->nr)
102
0
    assert(rs->ranges[0].start < rs->ranges[0].end);
103
104
0
  for (i = 1; i < rs->nr; i++) {
105
0
    assert(rs->ranges[i-1].end < rs->ranges[i].start);
106
0
    assert(rs->ranges[i].start < rs->ranges[i].end);
107
0
  }
108
0
}
109
110
/*
111
 * In-place pass of sorting and merging the ranges in the range set,
112
 * to establish the invariants when we get the ranges from the user
113
 */
114
void sort_and_merge_range_set(struct range_set *rs)
115
0
{
116
0
  unsigned int i;
117
0
  unsigned int o = 0; /* output cursor */
118
119
0
  QSORT(rs->ranges, rs->nr, range_cmp);
120
121
0
  for (i = 0; i < rs->nr; i++) {
122
0
    if (rs->ranges[i].start == rs->ranges[i].end)
123
0
      continue;
124
0
    if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].end) {
125
0
      if (rs->ranges[o-1].end < rs->ranges[i].end)
126
0
        rs->ranges[o-1].end = rs->ranges[i].end;
127
0
    } else {
128
0
      rs->ranges[o].start = rs->ranges[i].start;
129
0
      rs->ranges[o].end = rs->ranges[i].end;
130
0
      o++;
131
0
    }
132
0
  }
133
0
  assert(o <= rs->nr);
134
0
  rs->nr = o;
135
136
0
  range_set_check_invariants(rs);
137
0
}
138
139
/*
140
 * Union of range sets (i.e., sets of line numbers).  Used to merge
141
 * them when searches meet at a common ancestor.
142
 *
143
 * This is also where the ranges are consolidated into canonical form:
144
 * overlapping and adjacent ranges are merged, and empty ranges are
145
 * removed.
146
 */
147
static void range_set_union(struct range_set *out,
148
           struct range_set *a, struct range_set *b)
149
0
{
150
0
  unsigned int i = 0, j = 0;
151
0
  struct range *ra = a->ranges;
152
0
  struct range *rb = b->ranges;
153
  /* cannot make an alias of out->ranges: it may change during grow */
154
155
0
  assert(out->nr == 0);
156
0
  while (i < a->nr || j < b->nr) {
157
0
    struct range *new_range;
158
0
    if (i < a->nr && j < b->nr) {
159
0
      if (ra[i].start < rb[j].start)
160
0
        new_range = &ra[i++];
161
0
      else if (ra[i].start > rb[j].start)
162
0
        new_range = &rb[j++];
163
0
      else if (ra[i].end < rb[j].end)
164
0
        new_range = &ra[i++];
165
0
      else
166
0
        new_range = &rb[j++];
167
0
    } else if (i < a->nr)      /* b exhausted */
168
0
      new_range = &ra[i++];
169
0
    else                       /* a exhausted */
170
0
      new_range = &rb[j++];
171
0
    if (new_range->start == new_range->end)
172
0
      ; /* empty range */
173
0
    else if (!out->nr || out->ranges[out->nr-1].end < new_range->start) {
174
0
      range_set_grow(out, 1);
175
0
      out->ranges[out->nr].start = new_range->start;
176
0
      out->ranges[out->nr].end = new_range->end;
177
0
      out->nr++;
178
0
    } else if (out->ranges[out->nr-1].end < new_range->end) {
179
0
      out->ranges[out->nr-1].end = new_range->end;
180
0
    }
181
0
  }
182
0
}
183
184
/*
185
 * Difference of range sets (out = a \ b).  Pass the "interesting"
186
 * ranges as 'a' and the target side of the diff as 'b': it removes
187
 * the ranges for which the commit is responsible.
188
 */
189
static void range_set_difference(struct range_set *out,
190
          struct range_set *a, struct range_set *b)
191
0
{
192
0
  unsigned int i, j =  0;
193
0
  for (i = 0; i < a->nr; i++) {
194
0
    long start = a->ranges[i].start;
195
0
    long end = a->ranges[i].end;
196
0
    while (start < end) {
197
0
      while (j < b->nr && start >= b->ranges[j].end)
198
        /*
199
         * a:         |-------
200
         * b: ------|
201
         */
202
0
        j++;
203
0
      if (j >= b->nr || end <= b->ranges[j].start) {
204
        /*
205
         * b exhausted, or
206
         * a:  ----|
207
         * b:         |----
208
         */
209
0
        range_set_append(out, start, end);
210
0
        break;
211
0
      }
212
0
      if (start >= b->ranges[j].start) {
213
        /*
214
         * a:     |--????
215
         * b: |------|
216
         */
217
0
        start = b->ranges[j].end;
218
0
      } else if (end > b->ranges[j].start) {
219
        /*
220
         * a: |-----|
221
         * b:    |--?????
222
         */
223
0
        if (start < b->ranges[j].start)
224
0
          range_set_append(out, start, b->ranges[j].start);
225
0
        start = b->ranges[j].end;
226
0
      }
227
0
    }
228
0
  }
229
0
}
230
231
static void diff_ranges_init(struct diff_ranges *diff)
232
0
{
233
0
  range_set_init(&diff->parent, 0);
234
0
  range_set_init(&diff->target, 0);
235
0
}
236
237
static void diff_ranges_release(struct diff_ranges *diff)
238
0
{
239
0
  range_set_release(&diff->parent);
240
0
  range_set_release(&diff->target);
241
0
}
242
243
static void line_log_data_init(struct line_log_data *r)
244
0
{
245
0
  memset(r, 0, sizeof(struct line_log_data));
246
0
  range_set_init(&r->ranges, 0);
247
0
}
248
249
static void line_log_data_clear(struct line_log_data *r)
250
0
{
251
0
  range_set_release(&r->ranges);
252
0
  free(r->path);
253
0
  if (r->pair)
254
0
    diff_free_filepair(r->pair);
255
0
  diff_ranges_release(&r->diff);
256
0
}
257
258
static void free_line_log_data(struct line_log_data *r)
259
0
{
260
0
  while (r) {
261
0
    struct line_log_data *next = r->next;
262
0
    line_log_data_clear(r);
263
0
    free(r);
264
0
    r = next;
265
0
  }
266
0
}
267
268
static struct line_log_data *
269
search_line_log_data(struct line_log_data *list, const char *path,
270
         struct line_log_data **insertion_point)
271
0
{
272
0
  struct line_log_data *p = list;
273
0
  if (insertion_point)
274
0
    *insertion_point = NULL;
275
0
  while (p) {
276
0
    int cmp = strcmp(p->path, path);
277
0
    if (!cmp)
278
0
      return p;
279
0
    if (insertion_point && cmp < 0)
280
0
      *insertion_point = p;
281
0
    p = p->next;
282
0
  }
283
0
  return NULL;
284
0
}
285
286
/*
287
 * Note: takes ownership of 'path', which happens to be what the only
288
 * caller needs.
289
 */
290
static void line_log_data_insert(struct line_log_data **list,
291
         char *path,
292
         long begin, long end)
293
0
{
294
0
  struct line_log_data *ip;
295
0
  struct line_log_data *p = search_line_log_data(*list, path, &ip);
296
297
0
  if (p) {
298
0
    range_set_append_unsafe(&p->ranges, begin, end);
299
0
    free(path);
300
0
    return;
301
0
  }
302
303
0
  CALLOC_ARRAY(p, 1);
304
0
  p->path = path;
305
0
  range_set_append(&p->ranges, begin, end);
306
0
  if (ip) {
307
0
    p->next = ip->next;
308
0
    ip->next = p;
309
0
  } else {
310
0
    p->next = *list;
311
0
    *list = p;
312
0
  }
313
0
}
314
315
struct collect_diff_cbdata {
316
  struct diff_ranges *diff;
317
};
318
319
static int collect_diff_cb(long start_a, long count_a,
320
         long start_b, long count_b,
321
         void *data)
322
0
{
323
0
  struct collect_diff_cbdata *d = data;
324
325
0
  if (count_a >= 0)
326
0
    range_set_append(&d->diff->parent, start_a, start_a + count_a);
327
0
  if (count_b >= 0)
328
0
    range_set_append(&d->diff->target, start_b, start_b + count_b);
329
330
0
  return 0;
331
0
}
332
333
static int collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out)
334
0
{
335
0
  struct collect_diff_cbdata cbdata = {NULL};
336
0
  xpparam_t xpp;
337
0
  xdemitconf_t xecfg;
338
0
  xdemitcb_t ecb;
339
340
0
  memset(&xpp, 0, sizeof(xpp));
341
0
  memset(&xecfg, 0, sizeof(xecfg));
342
0
  xecfg.ctxlen = xecfg.interhunkctxlen = 0;
343
344
0
  cbdata.diff = out;
345
0
  xecfg.hunk_func = collect_diff_cb;
346
0
  memset(&ecb, 0, sizeof(ecb));
347
0
  ecb.priv = &cbdata;
348
0
  return xdi_diff(parent, target, &xpp, &xecfg, &ecb);
349
0
}
350
351
/*
352
 * These are handy for debugging.  Removing them with #if 0 silences
353
 * the "unused function" warning.
354
 */
355
#if 0
356
static void dump_range_set(struct range_set *rs, const char *desc)
357
{
358
  int i;
359
  printf("range set %s (%d items):\n", desc, rs->nr);
360
  for (i = 0; i < rs->nr; i++)
361
    printf("\t[%ld,%ld]\n", rs->ranges[i].start, rs->ranges[i].end);
362
}
363
364
static void dump_line_log_data(struct line_log_data *r)
365
{
366
  char buf[4096];
367
  while (r) {
368
    snprintf(buf, 4096, "file %s\n", r->path);
369
    dump_range_set(&r->ranges, buf);
370
    r = r->next;
371
  }
372
}
373
374
static void dump_diff_ranges(struct diff_ranges *diff, const char *desc)
375
{
376
  int i;
377
  assert(diff->parent.nr == diff->target.nr);
378
  printf("diff ranges %s (%d items):\n", desc, diff->parent.nr);
379
  printf("\tparent\ttarget\n");
380
  for (i = 0; i < diff->parent.nr; i++) {
381
    printf("\t[%ld,%ld]\t[%ld,%ld]\n",
382
           diff->parent.ranges[i].start,
383
           diff->parent.ranges[i].end,
384
           diff->target.ranges[i].start,
385
           diff->target.ranges[i].end);
386
  }
387
}
388
#endif
389
390
391
static int ranges_overlap(struct range *a, struct range *b)
392
0
{
393
0
  return !(a->end <= b->start || b->end <= a->start);
394
0
}
395
396
/*
397
 * Given a diff and the set of interesting ranges, determine all hunks
398
 * of the diff which touch (overlap) at least one of the interesting
399
 * ranges in the target.
400
 */
401
static void diff_ranges_filter_touched(struct diff_ranges *out,
402
               struct diff_ranges *diff,
403
               struct range_set *rs)
404
0
{
405
0
  unsigned int i, j = 0;
406
407
0
  assert(out->target.nr == 0);
408
409
0
  for (i = 0; i < diff->target.nr; i++) {
410
0
    while (diff->target.ranges[i].start >= rs->ranges[j].end) {
411
0
      j++;
412
0
      if (j == rs->nr)
413
0
        return;
414
0
    }
415
0
    if (ranges_overlap(&diff->target.ranges[i], &rs->ranges[j])) {
416
0
      range_set_append(&out->parent,
417
0
           diff->parent.ranges[i].start,
418
0
           diff->parent.ranges[i].end);
419
0
      range_set_append(&out->target,
420
0
           diff->target.ranges[i].start,
421
0
           diff->target.ranges[i].end);
422
0
    }
423
0
  }
424
0
}
425
426
/*
427
 * Adjust the line counts in 'rs' to account for the lines
428
 * added/removed in the diff.
429
 */
430
static void range_set_shift_diff(struct range_set *out,
431
         struct range_set *rs,
432
         struct diff_ranges *diff)
433
0
{
434
0
  unsigned int i, j = 0;
435
0
  long offset = 0;
436
0
  struct range *src = rs->ranges;
437
0
  struct range *target = diff->target.ranges;
438
0
  struct range *parent = diff->parent.ranges;
439
440
0
  for (i = 0; i < rs->nr; i++) {
441
0
    while (j < diff->target.nr && src[i].start >= target[j].start) {
442
0
      offset += (parent[j].end-parent[j].start)
443
0
        - (target[j].end-target[j].start);
444
0
      j++;
445
0
    }
446
0
    range_set_append(out, src[i].start+offset, src[i].end+offset);
447
0
  }
448
0
}
449
450
/*
451
 * Given a diff and the set of interesting ranges, map the ranges
452
 * across the diff.  That is: observe that the target commit takes
453
 * blame for all the + (target-side) ranges.  So for every pair of
454
 * ranges in the diff that was touched, we remove the latter and add
455
 * its parent side.
456
 */
457
static void range_set_map_across_diff(struct range_set *out,
458
              struct range_set *rs,
459
              struct diff_ranges *diff,
460
              struct diff_ranges **touched_out)
461
0
{
462
0
  struct diff_ranges *touched = xmalloc(sizeof(*touched));
463
0
  struct range_set tmp1 = RANGE_SET_INIT;
464
0
  struct range_set tmp2 = RANGE_SET_INIT;
465
466
0
  diff_ranges_init(touched);
467
0
  diff_ranges_filter_touched(touched, diff, rs);
468
0
  range_set_difference(&tmp1, rs, &touched->target);
469
0
  range_set_shift_diff(&tmp2, &tmp1, diff);
470
0
  range_set_union(out, &tmp2, &touched->parent);
471
0
  range_set_release(&tmp1);
472
0
  range_set_release(&tmp2);
473
474
0
  *touched_out = touched;
475
0
}
476
477
static struct commit *check_single_commit(struct rev_info *revs)
478
0
{
479
0
  struct object *commit = NULL;
480
0
  int found = -1;
481
0
  int i;
482
483
0
  for (i = 0; i < revs->pending.nr; i++) {
484
0
    struct object *obj = revs->pending.objects[i].item;
485
0
    if (obj->flags & UNINTERESTING)
486
0
      continue;
487
0
    obj = deref_tag(revs->repo, obj, NULL, 0);
488
0
    if (!obj || obj->type != OBJ_COMMIT)
489
0
      die("Non commit %s?", revs->pending.objects[i].name);
490
0
    if (commit)
491
0
      die("More than one commit to dig from: %s and %s?",
492
0
          revs->pending.objects[i].name,
493
0
          revs->pending.objects[found].name);
494
0
    commit = obj;
495
0
    found = i;
496
0
  }
497
498
0
  if (!commit)
499
0
    die("No commit specified?");
500
501
0
  return (struct commit *) commit;
502
0
}
503
504
static void fill_blob_sha1(struct repository *r, struct commit *commit,
505
         struct diff_filespec *spec)
506
0
{
507
0
  unsigned short mode;
508
0
  struct object_id oid;
509
510
0
  if (get_tree_entry(r, &commit->object.oid, spec->path, &oid, &mode))
511
0
    die("There is no path %s in the commit", spec->path);
512
0
  fill_filespec(spec, &oid, 1, mode);
513
514
0
  return;
515
0
}
516
517
static void fill_line_ends(struct repository *r,
518
         struct diff_filespec *spec,
519
         long *lines,
520
         unsigned long **line_ends)
521
0
{
522
0
  int num = 0, size = 50;
523
0
  long cur = 0;
524
0
  unsigned long *ends = NULL;
525
0
  char *data = NULL;
526
527
0
  if (diff_populate_filespec(r, spec, NULL))
528
0
    die("Cannot read blob %s", oid_to_hex(&spec->oid));
529
530
0
  ALLOC_ARRAY(ends, size);
531
0
  ends[cur++] = 0;
532
0
  data = spec->data;
533
0
  while (num < spec->size) {
534
0
    if (data[num] == '\n' || num == spec->size - 1) {
535
0
      ALLOC_GROW(ends, (cur + 1), size);
536
0
      ends[cur++] = num;
537
0
    }
538
0
    num++;
539
0
  }
540
541
  /* shrink the array to fit the elements */
542
0
  REALLOC_ARRAY(ends, cur);
543
0
  *lines = cur-1;
544
0
  *line_ends = ends;
545
0
}
546
547
struct nth_line_cb {
548
  struct diff_filespec *spec;
549
  long lines;
550
  unsigned long *line_ends;
551
};
552
553
static const char *nth_line(void *data, long line)
554
0
{
555
0
  struct nth_line_cb *d = data;
556
0
  assert(d && line <= d->lines);
557
0
  assert(d->spec && d->spec->data);
558
559
0
  if (line == 0)
560
0
    return (char *)d->spec->data;
561
0
  else
562
0
    return (char *)d->spec->data + d->line_ends[line] + 1;
563
0
}
564
565
static struct line_log_data *
566
parse_lines(struct repository *r, struct commit *commit,
567
      const char *prefix, struct string_list *args)
568
0
{
569
0
  long lines = 0;
570
0
  unsigned long *ends = NULL;
571
0
  struct nth_line_cb cb_data;
572
0
  struct string_list_item *item;
573
0
  struct line_log_data *ranges = NULL;
574
0
  struct line_log_data *p;
575
576
0
  for_each_string_list_item(item, args) {
577
0
    const char *name_part;
578
0
    char *range_part;
579
0
    char *full_name;
580
0
    struct diff_filespec *spec;
581
0
    long begin = 0, end = 0;
582
0
    long anchor;
583
584
0
    name_part = skip_range_arg(item->string, r->index);
585
0
    if (!name_part || *name_part != ':' || !name_part[1])
586
0
      die("-L argument not 'start,end:file' or ':funcname:file': %s",
587
0
          item->string);
588
0
    range_part = xstrndup(item->string, name_part - item->string);
589
0
    name_part++;
590
591
0
    full_name = prefix_path(r, prefix, prefix ? strlen(prefix) : 0,
592
0
          name_part);
593
594
0
    spec = alloc_filespec(full_name);
595
0
    fill_blob_sha1(r, commit, spec);
596
0
    fill_line_ends(r, spec, &lines, &ends);
597
0
    cb_data.spec = spec;
598
0
    cb_data.lines = lines;
599
0
    cb_data.line_ends = ends;
600
601
0
    p = search_line_log_data(ranges, full_name, NULL);
602
0
    if (p && p->ranges.nr)
603
0
      anchor = p->ranges.ranges[p->ranges.nr - 1].end + 1;
604
0
    else
605
0
      anchor = 1;
606
607
0
    if (parse_range_arg(range_part, nth_line, &cb_data,
608
0
            lines, anchor, &begin, &end,
609
0
            full_name, r->index))
610
0
      die("malformed -L argument '%s'", range_part);
611
0
    if ((!lines && (begin || end)) || lines < begin)
612
0
      die("file %s has only %lu lines", name_part, lines);
613
0
    if (begin < 1)
614
0
      begin = 1;
615
0
    if (end < 1 || lines < end)
616
0
      end = lines;
617
0
    begin--;
618
0
    line_log_data_insert(&ranges, full_name, begin, end);
619
620
0
    free_filespec(spec);
621
0
    FREE_AND_NULL(ends);
622
0
    free(range_part);
623
0
  }
624
625
0
  for (p = ranges; p; p = p->next)
626
0
    sort_and_merge_range_set(&p->ranges);
627
628
0
  return ranges;
629
0
}
630
631
static struct line_log_data *line_log_data_copy_one(struct line_log_data *r)
632
0
{
633
0
  struct line_log_data *ret = xmalloc(sizeof(*ret));
634
635
0
  assert(r);
636
0
  line_log_data_init(ret);
637
0
  range_set_copy(&ret->ranges, &r->ranges);
638
639
0
  ret->path = xstrdup(r->path);
640
641
0
  return ret;
642
0
}
643
644
static struct line_log_data *
645
line_log_data_copy(struct line_log_data *r)
646
0
{
647
0
  struct line_log_data *ret = NULL;
648
0
  struct line_log_data *tmp = NULL, *prev = NULL;
649
650
0
  assert(r);
651
0
  ret = tmp = prev = line_log_data_copy_one(r);
652
0
  r = r->next;
653
0
  while (r) {
654
0
    tmp = line_log_data_copy_one(r);
655
0
    prev->next = tmp;
656
0
    prev = tmp;
657
0
    r = r->next;
658
0
  }
659
660
0
  return ret;
661
0
}
662
663
/* merge two range sets across files */
664
static struct line_log_data *line_log_data_merge(struct line_log_data *a,
665
             struct line_log_data *b)
666
0
{
667
0
  struct line_log_data *head = NULL, **pp = &head;
668
669
0
  while (a || b) {
670
0
    struct line_log_data *src;
671
0
    struct line_log_data *src2 = NULL;
672
0
    struct line_log_data *d;
673
0
    int cmp;
674
0
    if (!a)
675
0
      cmp = 1;
676
0
    else if (!b)
677
0
      cmp = -1;
678
0
    else
679
0
      cmp = strcmp(a->path, b->path);
680
0
    if (cmp < 0) {
681
0
      src = a;
682
0
      a = a->next;
683
0
    } else if (cmp == 0) {
684
0
      src = a;
685
0
      a = a->next;
686
0
      src2 = b;
687
0
      b = b->next;
688
0
    } else {
689
0
      src = b;
690
0
      b = b->next;
691
0
    }
692
0
    d = xmalloc(sizeof(struct line_log_data));
693
0
    line_log_data_init(d);
694
0
    d->path = xstrdup(src->path);
695
0
    *pp = d;
696
0
    pp = &d->next;
697
0
    if (src2)
698
0
      range_set_union(&d->ranges, &src->ranges, &src2->ranges);
699
0
    else
700
0
      range_set_copy(&d->ranges, &src->ranges);
701
0
  }
702
703
0
  return head;
704
0
}
705
706
static void add_line_range(struct rev_info *revs, struct commit *commit,
707
         struct line_log_data *range)
708
0
{
709
0
  struct line_log_data *old_line = NULL;
710
0
  struct line_log_data *new_line = NULL;
711
712
0
  old_line = lookup_decoration(&revs->line_log_data, &commit->object);
713
0
  if (old_line && range) {
714
0
    new_line = line_log_data_merge(old_line, range);
715
0
    free_line_log_data(old_line);
716
0
  } else if (range)
717
0
    new_line = line_log_data_copy(range);
718
719
0
  if (new_line)
720
0
    add_decoration(&revs->line_log_data, &commit->object, new_line);
721
0
}
722
723
static void clear_commit_line_range(struct rev_info *revs, struct commit *commit)
724
0
{
725
0
  struct line_log_data *r;
726
0
  r = lookup_decoration(&revs->line_log_data, &commit->object);
727
0
  if (!r)
728
0
    return;
729
0
  free_line_log_data(r);
730
0
  add_decoration(&revs->line_log_data, &commit->object, NULL);
731
0
}
732
733
static struct line_log_data *lookup_line_range(struct rev_info *revs,
734
                 struct commit *commit)
735
0
{
736
0
  struct line_log_data *ret = NULL;
737
0
  struct line_log_data *d;
738
739
0
  ret = lookup_decoration(&revs->line_log_data, &commit->object);
740
741
0
  for (d = ret; d; d = d->next)
742
0
    range_set_check_invariants(&d->ranges);
743
744
0
  return ret;
745
0
}
746
747
static int same_paths_in_pathspec_and_range(struct pathspec *pathspec,
748
              struct line_log_data *range)
749
0
{
750
0
  int i;
751
0
  struct line_log_data *r;
752
753
0
  for (i = 0, r = range; i < pathspec->nr && r; i++, r = r->next)
754
0
    if (strcmp(pathspec->items[i].match, r->path))
755
0
      return 0;
756
0
  if (i < pathspec->nr || r)
757
    /* different number of pathspec items and ranges */
758
0
    return 0;
759
760
0
  return 1;
761
0
}
762
763
static void parse_pathspec_from_ranges(struct pathspec *pathspec,
764
               struct line_log_data *range)
765
0
{
766
0
  struct line_log_data *r;
767
0
  struct strvec array = STRVEC_INIT;
768
769
0
  for (r = range; r; r = r->next)
770
0
    strvec_push(&array, r->path);
771
772
0
  parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", array.v);
773
774
0
  strvec_clear(&array);
775
0
}
776
777
void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args)
778
0
{
779
0
  struct commit *commit = NULL;
780
0
  struct line_log_data *range;
781
782
0
  commit = check_single_commit(rev);
783
0
  range = parse_lines(rev->diffopt.repo, commit, prefix, args);
784
0
  add_line_range(rev, commit, range);
785
786
0
  parse_pathspec_from_ranges(&rev->diffopt.pathspec, range);
787
788
0
  free_line_log_data(range);
789
0
}
790
791
static void move_diff_queue(struct diff_queue_struct *dst,
792
          struct diff_queue_struct *src)
793
0
{
794
0
  assert(src != dst);
795
0
  memcpy(dst, src, sizeof(*dst));
796
0
  diff_queue_init(src);
797
0
}
798
799
static void filter_diffs_for_paths(struct line_log_data *range, int keep_deletions)
800
0
{
801
0
  int i;
802
0
  struct diff_queue_struct outq = DIFF_QUEUE_INIT;
803
804
0
  for (i = 0; i < diff_queued_diff.nr; i++) {
805
0
    struct diff_filepair *p = diff_queued_diff.queue[i];
806
0
    struct line_log_data *rg = NULL;
807
808
0
    if (!DIFF_FILE_VALID(p->two)) {
809
0
      if (keep_deletions)
810
0
        diff_q(&outq, p);
811
0
      else
812
0
        diff_free_filepair(p);
813
0
      continue;
814
0
    }
815
0
    for (rg = range; rg; rg = rg->next) {
816
0
      if (!strcmp(rg->path, p->two->path))
817
0
        break;
818
0
    }
819
0
    if (rg)
820
0
      diff_q(&outq, p);
821
0
    else
822
0
      diff_free_filepair(p);
823
0
  }
824
0
  free(diff_queued_diff.queue);
825
0
  diff_queued_diff = outq;
826
0
}
827
828
static inline int diff_might_be_rename(void)
829
0
{
830
0
  int i;
831
0
  for (i = 0; i < diff_queued_diff.nr; i++)
832
0
    if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one)) {
833
      /* fprintf(stderr, "diff_might_be_rename found creation of: %s\n", */
834
      /*  diff_queued_diff.queue[i]->two->path); */
835
0
      return 1;
836
0
    }
837
0
  return 0;
838
0
}
839
840
static void queue_diffs(struct line_log_data *range,
841
      struct diff_options *opt,
842
      struct diff_queue_struct *queue,
843
      struct commit *commit, struct commit *parent)
844
0
{
845
0
  struct object_id *tree_oid, *parent_tree_oid;
846
847
0
  assert(commit);
848
849
0
  tree_oid = get_commit_tree_oid(commit);
850
0
  parent_tree_oid = parent ? get_commit_tree_oid(parent) : NULL;
851
852
0
  if (opt->detect_rename &&
853
0
      !same_paths_in_pathspec_and_range(&opt->pathspec, range)) {
854
0
    clear_pathspec(&opt->pathspec);
855
0
    parse_pathspec_from_ranges(&opt->pathspec, range);
856
0
  }
857
0
  diff_queue_clear(&diff_queued_diff);
858
0
  diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
859
0
  if (opt->detect_rename && diff_might_be_rename()) {
860
0
    struct diff_options rename_opts;
861
862
    /*
863
     * Build a private diff_options for rename detection so
864
     * that any user-specified options on the original opts
865
     * (e.g. pickaxe) cannot discard diff pairs needed for
866
     * rename tracking.  Similar to blame's find_rename().
867
     */
868
0
    repo_diff_setup(opt->repo, &rename_opts);
869
0
    rename_opts.flags.recursive = 1;
870
0
    rename_opts.detect_rename = opt->detect_rename;
871
0
    rename_opts.rename_score = opt->rename_score;
872
0
    rename_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
873
0
    diff_setup_done(&rename_opts);
874
875
    /* must look at the full tree diff to detect renames */
876
0
    diff_queue_clear(&diff_queued_diff);
877
0
    diff_tree_oid(parent_tree_oid, tree_oid, "", &rename_opts);
878
879
0
    filter_diffs_for_paths(range, 1);
880
0
    diffcore_std(&rename_opts);
881
0
    filter_diffs_for_paths(range, 0);
882
0
    diff_free(&rename_opts);
883
0
  }
884
0
  move_diff_queue(queue, &diff_queued_diff);
885
0
}
886
887
/*
888
 * Unlike most other functions, this destructively operates on
889
 * 'range'.
890
 */
891
static int process_diff_filepair(struct rev_info *rev,
892
         struct diff_filepair *pair,
893
         struct line_log_data *range,
894
         struct diff_ranges **diff_out)
895
0
{
896
0
  struct line_log_data *rg = range;
897
0
  struct range_set tmp;
898
0
  struct diff_ranges diff;
899
0
  mmfile_t file_parent, file_target;
900
0
  char *parent_data_to_free = NULL;
901
902
0
  assert(pair->two->path);
903
0
  while (rg) {
904
0
    assert(rg->path);
905
0
    if (!strcmp(rg->path, pair->two->path))
906
0
      break;
907
0
    rg = rg->next;
908
0
  }
909
910
0
  if (!rg)
911
0
    return 0;
912
0
  if (rg->ranges.nr == 0)
913
0
    return 0;
914
915
0
  assert(pair->two->oid_valid);
916
0
  diff_populate_filespec(rev->diffopt.repo, pair->two, NULL);
917
0
  file_target.ptr = pair->two->data;
918
0
  file_target.size = pair->two->size;
919
920
0
  if (pair->one->oid_valid) {
921
0
    diff_populate_filespec(rev->diffopt.repo, pair->one, NULL);
922
0
    file_parent.ptr = pair->one->data;
923
0
    file_parent.size = pair->one->size;
924
0
  } else {
925
0
    file_parent.ptr = parent_data_to_free = xstrdup("");
926
0
    file_parent.size = 0;
927
0
  }
928
929
0
  diff_ranges_init(&diff);
930
0
  if (collect_diff(&file_parent, &file_target, &diff))
931
0
    die("unable to generate diff for %s", pair->one->path);
932
933
  /* NEEDSWORK should apply some heuristics to prevent mismatches */
934
0
  free(rg->path);
935
0
  rg->path = xstrdup(pair->one->path);
936
937
0
  range_set_init(&tmp, 0);
938
0
  range_set_map_across_diff(&tmp, &rg->ranges, &diff, diff_out);
939
0
  range_set_release(&rg->ranges);
940
0
  range_set_move(&rg->ranges, &tmp);
941
942
0
  diff_ranges_release(&diff);
943
944
0
  free(parent_data_to_free);
945
0
  return ((*diff_out)->parent.nr > 0);
946
0
}
947
948
static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
949
0
{
950
0
  struct diff_filepair *new_filepair = xcalloc(1, sizeof(struct diff_filepair));
951
0
  new_filepair->one = pair->one;
952
0
  new_filepair->two = pair->two;
953
0
  new_filepair->one->count++;
954
0
  new_filepair->two->count++;
955
0
  return new_filepair;
956
0
}
957
958
static int process_all_files(struct line_log_data **range_out,
959
           struct rev_info *rev,
960
           struct diff_queue_struct *queue,
961
           struct line_log_data *range)
962
0
{
963
0
  int i, changed = 0;
964
965
0
  *range_out = line_log_data_copy(range);
966
967
0
  for (i = 0; i < queue->nr; i++) {
968
0
    struct diff_ranges *pairdiff = NULL;
969
0
    struct diff_filepair *pair = queue->queue[i];
970
0
    if (process_diff_filepair(rev, pair, *range_out, &pairdiff)) {
971
      /*
972
       * Store away the diff for later output.  We
973
       * tuck it in the ranges we got as _input_,
974
       * since that's the commit that caused the
975
       * diff.
976
       *
977
       * NEEDSWORK not enough when we get around to
978
       * doing something interesting with merges;
979
       * currently each invocation on a merge parent
980
       * trashes the previous one's diff.
981
       *
982
       * NEEDSWORK tramples over data structures not owned here
983
       */
984
0
      struct line_log_data *rg = range;
985
0
      changed++;
986
0
      while (rg && strcmp(rg->path, pair->two->path))
987
0
        rg = rg->next;
988
0
      assert(rg);
989
0
      if (rg->pair)
990
0
        diff_free_filepair(rg->pair);
991
0
      rg->pair = diff_filepair_dup(queue->queue[i]);
992
0
      diff_ranges_release(&rg->diff);
993
0
      memcpy(&rg->diff, pairdiff, sizeof(struct diff_ranges));
994
0
      FREE_AND_NULL(pairdiff);
995
0
    }
996
997
0
    if (pairdiff) {
998
0
      diff_ranges_release(pairdiff);
999
0
      free(pairdiff);
1000
0
    }
1001
0
  }
1002
1003
0
  return changed;
1004
0
}
1005
1006
void line_log_queue_pairs(struct rev_info *rev, struct commit *commit)
1007
0
{
1008
0
  struct line_log_data *range = lookup_line_range(rev, commit);
1009
0
  struct line_log_data *r;
1010
1011
0
  for (r = range; r; r = r->next) {
1012
0
    if (r->pair) {
1013
0
      struct diff_filepair *p = diff_filepair_dup(r->pair);
1014
0
      p->line_ranges = &r->ranges;
1015
0
      diff_q(&diff_queued_diff, p);
1016
0
    }
1017
0
  }
1018
0
}
1019
1020
static int bloom_filter_check(struct rev_info *rev,
1021
            struct commit *commit,
1022
            struct line_log_data *range)
1023
0
{
1024
0
  struct bloom_filter *filter;
1025
0
  struct bloom_key key;
1026
0
  int result = 0;
1027
1028
0
  if (!commit->parents)
1029
0
    return 1;
1030
1031
0
  if (!rev->bloom_filter_settings ||
1032
0
      !(filter = get_bloom_filter(rev->repo, commit)))
1033
0
    return 1;
1034
1035
0
  if (!range)
1036
0
    return 0;
1037
1038
0
  while (!result && range) {
1039
0
    bloom_key_fill(&key, range->path, strlen(range->path),
1040
0
             rev->bloom_filter_settings);
1041
1042
0
    if (bloom_filter_contains(filter, &key, rev->bloom_filter_settings))
1043
0
      result = 1;
1044
1045
0
    bloom_key_clear(&key);
1046
0
    range = range->next;
1047
0
  }
1048
1049
0
  return result;
1050
0
}
1051
1052
static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *commit,
1053
            struct line_log_data *range)
1054
0
{
1055
0
  struct commit *parent = NULL;
1056
0
  struct diff_queue_struct queue = DIFF_QUEUE_INIT;
1057
0
  struct line_log_data *parent_range;
1058
0
  int changed;
1059
1060
0
  if (commit->parents)
1061
0
    parent = commit->parents->item;
1062
1063
0
  queue_diffs(range, &rev->diffopt, &queue, commit, parent);
1064
0
  changed = process_all_files(&parent_range, rev, &queue, range);
1065
1066
0
  if (parent)
1067
0
    add_line_range(rev, parent, parent_range);
1068
0
  free_line_log_data(parent_range);
1069
0
  diff_queue_clear(&queue);
1070
0
  return changed;
1071
0
}
1072
1073
static int process_ranges_merge_commit(struct rev_info *rev, struct commit *commit,
1074
               struct line_log_data *range)
1075
0
{
1076
0
  struct line_log_data **cand;
1077
0
  struct commit_list *p;
1078
0
  int i;
1079
0
  int nparents = commit_list_count(commit->parents);
1080
0
  int ret;
1081
1082
0
  if (nparents > 1 && rev->first_parent_only)
1083
0
    nparents = 1;
1084
1085
0
  CALLOC_ARRAY(cand, nparents);
1086
1087
0
  for (p = commit->parents, i = 0;
1088
0
       p && i < nparents;
1089
0
       p = p->next, i++) {
1090
0
    struct commit *parent = p->item;
1091
0
    struct diff_queue_struct diffqueue = DIFF_QUEUE_INIT;
1092
0
    int changed;
1093
1094
0
    queue_diffs(range, &rev->diffopt, &diffqueue, commit, parent);
1095
1096
0
    changed = process_all_files(&cand[i], rev, &diffqueue, range);
1097
0
    diff_queue_clear(&diffqueue);
1098
0
    if (!changed) {
1099
      /*
1100
       * This parent can take all the blame, so we
1101
       * don't follow any other path in history
1102
       */
1103
0
      add_line_range(rev, parent, cand[i]);
1104
0
      commit_list_free(commit->parents);
1105
0
      commit_list_append(parent, &commit->parents);
1106
1107
0
      ret = 0;
1108
0
      goto out;
1109
0
    }
1110
0
  }
1111
1112
  /*
1113
   * No single parent took the blame.  We add the candidates
1114
   * from the above loop to the parents.
1115
   */
1116
0
  for (p = commit->parents, i = 0;
1117
0
       p && i < nparents;
1118
0
       p = p->next, i++)
1119
0
    add_line_range(rev, p->item, cand[i]);
1120
1121
0
  ret = 1;
1122
1123
0
out:
1124
0
  clear_commit_line_range(rev, commit);
1125
0
  for (i = 0; i < nparents; i++) {
1126
0
    if (!cand[i])
1127
0
      continue;
1128
0
    line_log_data_clear(cand[i]);
1129
0
    free(cand[i]);
1130
0
  }
1131
0
  free(cand);
1132
0
  return ret;
1133
1134
  /* NEEDSWORK evil merge detection stuff */
1135
0
}
1136
1137
int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit)
1138
0
{
1139
0
  struct line_log_data *range = lookup_line_range(rev, commit);
1140
0
  int changed = 0;
1141
1142
0
  if (range) {
1143
0
    if (commit->parents && !bloom_filter_check(rev, commit, range)) {
1144
0
      add_line_range(rev, commit->parents->item, range);
1145
0
      clear_commit_line_range(rev, commit);
1146
0
    } else if (commit->parents && commit->parents->next)
1147
0
      changed = process_ranges_merge_commit(rev, commit, range);
1148
0
    else
1149
0
      changed = process_ranges_ordinary_commit(rev, commit, range);
1150
0
  }
1151
1152
0
  if (!changed)
1153
0
    commit->object.flags |= TREESAME;
1154
1155
0
  return changed;
1156
0
}
1157
1158
static enum rewrite_result line_log_rewrite_one(struct rev_info *rev UNUSED,
1159
            struct commit **pp)
1160
0
{
1161
0
  for (;;) {
1162
0
    struct commit *p = *pp;
1163
0
    if (p->parents && p->parents->next)
1164
0
      return rewrite_one_ok;
1165
0
    if (p->object.flags & UNINTERESTING)
1166
0
      return rewrite_one_ok;
1167
0
    if (!(p->object.flags & TREESAME))
1168
0
      return rewrite_one_ok;
1169
0
    if (!p->parents)
1170
0
      return rewrite_one_noparents;
1171
0
    *pp = p->parents->item;
1172
0
  }
1173
0
}
1174
1175
int line_log_filter(struct rev_info *rev)
1176
0
{
1177
0
  struct commit *commit;
1178
0
  struct commit_list *list = rev->commits;
1179
0
  struct commit_list *out = NULL, **pp = &out;
1180
1181
0
  while (list) {
1182
0
    struct commit_list *to_free = NULL;
1183
0
    commit = list->item;
1184
0
    if (line_log_process_ranges_arbitrary_commit(rev, commit)) {
1185
0
      *pp = list;
1186
0
      pp = &list->next;
1187
0
    } else
1188
0
      to_free = list;
1189
0
    list = list->next;
1190
0
    free(to_free);
1191
0
  }
1192
0
  *pp = NULL;
1193
1194
0
  for (list = out; list; list = list->next)
1195
0
    rewrite_parents(rev, list->item, line_log_rewrite_one);
1196
1197
0
  rev->commits = out;
1198
1199
0
  return 0;
1200
0
}
1201
1202
static void free_void_line_log_data(void *data)
1203
0
{
1204
0
  free_line_log_data(data);
1205
0
}
1206
1207
void line_log_free(struct rev_info *rev)
1208
0
{
1209
0
  clear_decoration(&rev->line_log_data, free_void_line_log_data);
1210
0
}