Coverage Report

Created: 2025-12-31 07:01

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