Coverage Report

Created: 2026-09-28 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/combine-diff.c
Line
Count
Source
1
#define USE_THE_REPOSITORY_VARIABLE
2
#define DISABLE_SIGN_COMPARE_WARNINGS
3
4
#include "git-compat-util.h"
5
#include "odb.h"
6
#include "commit.h"
7
#include "convert.h"
8
#include "diff.h"
9
#include "diffcore.h"
10
#include "environment.h"
11
#include "hex.h"
12
#include "object-name.h"
13
#include "quote.h"
14
#include "xdiff-interface.h"
15
#include "xdiff/xmacros.h"
16
#include "log-tree.h"
17
#include "refs.h"
18
#include "tree.h"
19
#include "userdiff.h"
20
#include "oid-array.h"
21
#include "revision.h"
22
23
static int compare_paths(const struct combine_diff_path *one,
24
        const struct diff_filespec *two)
25
0
{
26
0
  if (!S_ISDIR(one->mode) && !S_ISDIR(two->mode))
27
0
    return strcmp(one->path, two->path);
28
29
0
  return base_name_compare(one->path, strlen(one->path), one->mode,
30
0
         two->path, strlen(two->path), two->mode);
31
0
}
32
33
static int filename_changed(char status)
34
0
{
35
0
  return status == 'R' || status == 'C';
36
0
}
37
38
static struct combine_diff_path *intersect_paths(
39
  struct combine_diff_path *curr,
40
  int n,
41
  int num_parent,
42
  int combined_all_paths)
43
0
{
44
0
  struct diff_queue_struct *q = &diff_queued_diff;
45
0
  struct combine_diff_path *p, **tail = &curr;
46
0
  int i, j, cmp;
47
48
0
  if (!n) {
49
0
    for (i = 0; i < q->nr; i++) {
50
0
      if (diff_unmodified_pair(q->queue[i]))
51
0
        continue;
52
0
      p = combine_diff_path_new(q->queue[i]->two->path,
53
0
              strlen(q->queue[i]->two->path),
54
0
              q->queue[i]->two->mode,
55
0
              &q->queue[i]->two->oid,
56
0
              num_parent);
57
0
      oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
58
0
      p->parent[n].mode = q->queue[i]->one->mode;
59
0
      p->parent[n].status = q->queue[i]->status;
60
61
0
      if (combined_all_paths &&
62
0
          filename_changed(p->parent[n].status)) {
63
0
        p->parent[n].path = xstrdup(q->queue[i]->one->path);
64
0
      }
65
0
      *tail = p;
66
0
      tail = &p->next;
67
0
    }
68
0
    return curr;
69
0
  }
70
71
  /*
72
   * paths in curr (linked list) and q->queue[] (array) are
73
   * both sorted in the tree order.
74
   */
75
0
  i = 0;
76
0
  while ((p = *tail) != NULL) {
77
0
    cmp = ((i >= q->nr)
78
0
           ? -1 : compare_paths(p, q->queue[i]->two));
79
80
0
    if (cmp < 0) {
81
      /* p->path not in q->queue[]; drop it */
82
0
      *tail = p->next;
83
0
      for (j = 0; j < num_parent; j++)
84
0
        free(p->parent[j].path);
85
0
      free(p);
86
0
      continue;
87
0
    }
88
89
0
    if (cmp > 0) {
90
      /* q->queue[i] not in p->path; skip it */
91
0
      i++;
92
0
      continue;
93
0
    }
94
95
0
    oidcpy(&p->parent[n].oid, &q->queue[i]->one->oid);
96
0
    p->parent[n].mode = q->queue[i]->one->mode;
97
0
    p->parent[n].status = q->queue[i]->status;
98
0
    if (combined_all_paths &&
99
0
        filename_changed(p->parent[n].status))
100
0
      p->parent[n].path = xstrdup(q->queue[i]->one->path);
101
102
0
    tail = &p->next;
103
0
    i++;
104
0
  }
105
0
  return curr;
106
0
}
107
108
/* Lines lost from parent */
109
struct lline {
110
  struct lline *next, *prev;
111
  int len;
112
  unsigned long parent_map;
113
  char line[FLEX_ARRAY];
114
};
115
116
/* Lines lost from current parent (before coalescing) */
117
struct plost {
118
  struct lline *lost_head, *lost_tail;
119
  int len;
120
};
121
122
/* Lines surviving in the merge result */
123
struct sline {
124
  /* Accumulated and coalesced lost lines */
125
  struct lline *lost;
126
  int lenlost;
127
  struct plost plost;
128
  char *bol;
129
  int len;
130
  /* bit 0 up to (N-1) are on if the parent has this line (i.e.
131
   * we did not change it).
132
   * bit N is used for "interesting" lines, including context.
133
   * bit (N+1) is used for "do not show deletion before this".
134
   */
135
  unsigned long flag;
136
  unsigned long *p_lno;
137
};
138
139
static int match_string_spaces(const char *line1, int len1,
140
             const char *line2, int len2,
141
             long flags)
142
0
{
143
0
  if (flags & XDF_WHITESPACE_FLAGS) {
144
0
    for (; len1 > 0 && XDL_ISSPACE(line1[len1 - 1]); len1--);
145
0
    for (; len2 > 0 && XDL_ISSPACE(line2[len2 - 1]); len2--);
146
0
  }
147
148
0
  if (!(flags & (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE)))
149
0
    return (len1 == len2 && !memcmp(line1, line2, len1));
150
151
0
  while (len1 > 0 && len2 > 0) {
152
0
    len1--;
153
0
    len2--;
154
0
    if (XDL_ISSPACE(line1[len1]) || XDL_ISSPACE(line2[len2])) {
155
0
      if ((flags & XDF_IGNORE_WHITESPACE_CHANGE) &&
156
0
          (!XDL_ISSPACE(line1[len1]) || !XDL_ISSPACE(line2[len2])))
157
0
        return 0;
158
159
0
      for (; len1 > 0 && XDL_ISSPACE(line1[len1]); len1--);
160
0
      for (; len2 > 0 && XDL_ISSPACE(line2[len2]); len2--);
161
0
    }
162
0
    if (line1[len1] != line2[len2])
163
0
      return 0;
164
0
  }
165
166
0
  if (flags & XDF_IGNORE_WHITESPACE) {
167
    /* Consume remaining spaces */
168
0
    for (; len1 > 0 && XDL_ISSPACE(line1[len1 - 1]); len1--);
169
0
    for (; len2 > 0 && XDL_ISSPACE(line2[len2 - 1]); len2--);
170
0
  }
171
172
  /* We matched full line1 and line2 */
173
0
  if (!len1 && !len2)
174
0
    return 1;
175
176
0
  return 0;
177
0
}
178
179
enum coalesce_direction { MATCH, BASE, NEW };
180
181
/* Coalesce new lines into base by finding LCS */
182
static struct lline *coalesce_lines(struct lline *base, int *lenbase,
183
            struct lline *newline, int lennew,
184
            unsigned long parent, long flags)
185
0
{
186
0
  int **lcs;
187
0
  enum coalesce_direction **directions;
188
0
  struct lline *baseend, *newend = NULL;
189
0
  int i, j, origbaselen = *lenbase;
190
191
0
  if (!newline)
192
0
    return base;
193
194
0
  if (!base) {
195
0
    *lenbase = lennew;
196
0
    return newline;
197
0
  }
198
199
  /*
200
   * Coalesce new lines into base by finding the LCS
201
   * - Create the table to run dynamic programming
202
   * - Compute the LCS
203
   * - Then reverse read the direction structure:
204
   *   - If we have MATCH, assign parent to base flag, and consume
205
   *   both baseend and newend
206
   *   - Else if we have BASE, consume baseend
207
   *   - Else if we have NEW, insert newend lline into base and
208
   *   consume newend
209
   */
210
0
  CALLOC_ARRAY(lcs, st_add(origbaselen, 1));
211
0
  CALLOC_ARRAY(directions, st_add(origbaselen, 1));
212
0
  for (i = 0; i < origbaselen + 1; i++) {
213
0
    CALLOC_ARRAY(lcs[i], st_add(lennew, 1));
214
0
    CALLOC_ARRAY(directions[i], st_add(lennew, 1));
215
0
    directions[i][0] = BASE;
216
0
  }
217
0
  for (j = 1; j < lennew + 1; j++)
218
0
    directions[0][j] = NEW;
219
220
0
  for (i = 1, baseend = base; i < origbaselen + 1; i++) {
221
0
    for (j = 1, newend = newline; j < lennew + 1; j++) {
222
0
      if (match_string_spaces(baseend->line, baseend->len,
223
0
            newend->line, newend->len, flags)) {
224
0
        lcs[i][j] = lcs[i - 1][j - 1] + 1;
225
0
        directions[i][j] = MATCH;
226
0
      } else if (lcs[i][j - 1] >= lcs[i - 1][j]) {
227
0
        lcs[i][j] = lcs[i][j - 1];
228
0
        directions[i][j] = NEW;
229
0
      } else {
230
0
        lcs[i][j] = lcs[i - 1][j];
231
0
        directions[i][j] = BASE;
232
0
      }
233
0
      if (newend->next)
234
0
        newend = newend->next;
235
0
    }
236
0
    if (baseend->next)
237
0
      baseend = baseend->next;
238
0
  }
239
240
0
  for (i = 0; i < origbaselen + 1; i++)
241
0
    free(lcs[i]);
242
0
  free(lcs);
243
244
  /* At this point, baseend and newend point to the end of each lists */
245
0
  i--;
246
0
  j--;
247
0
  while (i != 0 || j != 0) {
248
0
    if (directions[i][j] == MATCH) {
249
0
      baseend->parent_map |= 1<<parent;
250
0
      baseend = baseend->prev;
251
0
      newend = newend->prev;
252
0
      i--;
253
0
      j--;
254
0
    } else if (directions[i][j] == NEW) {
255
0
      struct lline *lline;
256
257
0
      lline = newend;
258
      /* Remove lline from new list and update newend */
259
0
      if (lline->prev)
260
0
        lline->prev->next = lline->next;
261
0
      else
262
0
        newline = lline->next;
263
0
      if (lline->next)
264
0
        lline->next->prev = lline->prev;
265
266
0
      newend = lline->prev;
267
0
      j--;
268
269
      /* Add lline to base list */
270
0
      if (baseend) {
271
0
        lline->next = baseend->next;
272
0
        lline->prev = baseend;
273
0
        if (lline->prev)
274
0
          lline->prev->next = lline;
275
0
      }
276
0
      else {
277
0
        lline->next = base;
278
0
        base = lline;
279
0
      }
280
0
      (*lenbase)++;
281
282
0
      if (lline->next)
283
0
        lline->next->prev = lline;
284
285
0
    } else {
286
0
      baseend = baseend->prev;
287
0
      i--;
288
0
    }
289
0
  }
290
291
0
  newend = newline;
292
0
  while (newend) {
293
0
    struct lline *lline = newend;
294
0
    newend = newend->next;
295
0
    free(lline);
296
0
  }
297
298
0
  for (i = 0; i < origbaselen + 1; i++)
299
0
    free(directions[i]);
300
0
  free(directions);
301
302
0
  return base;
303
0
}
304
305
static char *grab_blob(struct repository *r,
306
           const struct object_id *oid, unsigned int mode,
307
           unsigned long *size, struct userdiff_driver *textconv,
308
           const char *path)
309
0
{
310
0
  char *blob;
311
0
  enum object_type type;
312
313
0
  if (S_ISGITLINK(mode)) {
314
0
    struct strbuf buf = STRBUF_INIT;
315
0
    strbuf_addf(&buf, "Subproject commit %s\n", oid_to_hex(oid));
316
0
    *size = buf.len;
317
0
    blob = strbuf_detach(&buf, NULL);
318
0
  } else if (is_null_oid(oid)) {
319
    /* deleted blob */
320
0
    *size = 0;
321
0
    return xcalloc(1, 1);
322
0
  } else if (textconv) {
323
0
    struct diff_filespec *df = alloc_filespec(path);
324
0
    fill_filespec(df, oid, 1, mode);
325
0
    *size = fill_textconv(r, textconv, df, &blob);
326
0
    free_filespec(df);
327
0
  } else {
328
0
    size_t size_st = 0;
329
0
    blob = odb_read_object(r->objects, oid, &type, &size_st);
330
0
    *size = cast_size_t_to_ulong(size_st);
331
0
    if (!blob)
332
0
      die(_("unable to read %s"), oid_to_hex(oid));
333
0
    if (type != OBJ_BLOB)
334
0
      die("object '%s' is not a blob!", oid_to_hex(oid));
335
0
  }
336
0
  return blob;
337
0
}
338
339
static void append_lost(struct sline *sline, int n, const char *line, int len)
340
0
{
341
0
  struct lline *lline;
342
0
  unsigned long this_mask = (1UL<<n);
343
0
  if (line[len-1] == '\n')
344
0
    len--;
345
346
0
  FLEX_ALLOC_MEM(lline, line, line, len);
347
0
  lline->len = len;
348
0
  lline->next = NULL;
349
0
  lline->prev = sline->plost.lost_tail;
350
0
  if (lline->prev)
351
0
    lline->prev->next = lline;
352
0
  else
353
0
    sline->plost.lost_head = lline;
354
0
  sline->plost.lost_tail = lline;
355
0
  sline->plost.len++;
356
0
  lline->parent_map = this_mask;
357
0
}
358
359
struct combine_diff_state {
360
  unsigned int lno;
361
  int ob, on, nb, nn;
362
  unsigned long nmask;
363
  int num_parent;
364
  int n;
365
  struct sline *sline;
366
  struct sline *lost_bucket;
367
};
368
369
static void consume_hunk(void *state_,
370
       long ob, long on,
371
       long nb, long nn,
372
       const char *func UNUSED, long funclen UNUSED)
373
0
{
374
0
  struct combine_diff_state *state = state_;
375
376
0
  state->ob = ob;
377
0
  state->on = on;
378
0
  state->nb = nb;
379
0
  state->nn = nn;
380
0
  state->lno = state->nb;
381
0
  if (state->nn == 0) {
382
    /* @@ -X,Y +N,0 @@ removed Y lines
383
     * that would have come *after* line N
384
     * in the result.  Our lost buckets hang
385
     * to the line after the removed lines,
386
     *
387
     * Note that this is correct even when N == 0,
388
     * in which case the hunk removes the first
389
     * line in the file.
390
     */
391
0
    state->lost_bucket = &state->sline[state->nb];
392
0
    if (!state->nb)
393
0
      state->nb = 1;
394
0
  } else {
395
0
    state->lost_bucket = &state->sline[state->nb-1];
396
0
  }
397
0
  if (!state->sline[state->nb-1].p_lno)
398
0
    CALLOC_ARRAY(state->sline[state->nb - 1].p_lno,
399
0
           state->num_parent);
400
0
  state->sline[state->nb-1].p_lno[state->n] = state->ob;
401
0
}
402
403
static int consume_line(void *state_, char *line, unsigned long len)
404
0
{
405
0
  struct combine_diff_state *state = state_;
406
0
  if (!state->lost_bucket)
407
0
    return 0; /* not in any hunk yet */
408
0
  switch (line[0]) {
409
0
  case '-':
410
0
    append_lost(state->lost_bucket, state->n, line+1, len-1);
411
0
    break;
412
0
  case '+':
413
0
    state->sline[state->lno-1].flag |= state->nmask;
414
0
    state->lno++;
415
0
    break;
416
0
  }
417
0
  return 0;
418
0
}
419
420
static void combine_diff(struct repository *r,
421
       const struct object_id *parent, unsigned int mode,
422
       mmfile_t *result_file,
423
       struct sline *sline, unsigned int cnt, int n,
424
       int num_parent, int result_deleted,
425
       struct userdiff_driver *textconv,
426
       const char *path, long flags)
427
0
{
428
0
  unsigned int p_lno, lno;
429
0
  unsigned long nmask = (1UL << n);
430
0
  xpparam_t xpp;
431
0
  xdemitconf_t xecfg;
432
0
  mmfile_t parent_file;
433
0
  struct combine_diff_state state;
434
0
  unsigned long sz;
435
436
0
  if (result_deleted)
437
0
    return; /* result deleted */
438
439
0
  parent_file.ptr = grab_blob(r, parent, mode, &sz, textconv, path);
440
0
  parent_file.size = sz;
441
0
  memset(&xpp, 0, sizeof(xpp));
442
0
  xpp.flags = flags;
443
0
  memset(&xecfg, 0, sizeof(xecfg));
444
0
  memset(&state, 0, sizeof(state));
445
0
  state.nmask = nmask;
446
0
  state.sline = sline;
447
0
  state.lno = 1;
448
0
  state.num_parent = num_parent;
449
0
  state.n = n;
450
451
0
  if (xdi_diff_outf(&parent_file, result_file, consume_hunk,
452
0
        consume_line, &state, &xpp, &xecfg))
453
0
    die("unable to generate combined diff for %s",
454
0
        oid_to_hex(parent));
455
0
  free(parent_file.ptr);
456
457
  /* Assign line numbers for this parent.
458
   *
459
   * sline[lno].p_lno[n] records the first line number
460
   * (counting from 1) for parent N if the final hunk display
461
   * started by showing sline[lno] (possibly showing the lost
462
   * lines attached to it first).
463
   */
464
0
  for (lno = 0,  p_lno = 1; lno <= cnt; lno++) {
465
0
    struct lline *ll;
466
0
    sline[lno].p_lno[n] = p_lno;
467
468
    /* Coalesce new lines */
469
0
    if (sline[lno].plost.lost_head) {
470
0
      struct sline *sl = &sline[lno];
471
0
      sl->lost = coalesce_lines(sl->lost, &sl->lenlost,
472
0
              sl->plost.lost_head,
473
0
              sl->plost.len, n, flags);
474
0
      sl->plost.lost_head = sl->plost.lost_tail = NULL;
475
0
      sl->plost.len = 0;
476
0
    }
477
478
    /* How many lines would this sline advance the p_lno? */
479
0
    ll = sline[lno].lost;
480
0
    while (ll) {
481
0
      if (ll->parent_map & nmask)
482
0
        p_lno++; /* '-' means parent had it */
483
0
      ll = ll->next;
484
0
    }
485
0
    if (lno < cnt && !(sline[lno].flag & nmask))
486
0
      p_lno++; /* no '+' means parent had it */
487
0
  }
488
0
  sline[lno].p_lno[n] = p_lno; /* trailer */
489
0
}
490
491
static unsigned long context = 3;
492
static char combine_marker = '@';
493
494
static int interesting(struct sline *sline, unsigned long all_mask)
495
0
{
496
  /* If some parents lost lines here, or if we have added to
497
   * some parent, it is interesting.
498
   */
499
0
  return ((sline->flag & all_mask) || sline->lost);
500
0
}
501
502
static unsigned long adjust_hunk_tail(struct sline *sline,
503
              unsigned long all_mask,
504
              unsigned long hunk_begin,
505
              unsigned long i)
506
0
{
507
  /* i points at the first uninteresting line.  If the last line
508
   * of the hunk was interesting only because it has some
509
   * deletion, then it is not all that interesting for the
510
   * purpose of giving trailing context lines.  This is because
511
   * we output '-' line and then unmodified sline[i-1] itself in
512
   * that case which gives us one extra context line.
513
   */
514
0
  if ((hunk_begin + 1 <= i) && !(sline[i-1].flag & all_mask))
515
0
    i--;
516
0
  return i;
517
0
}
518
519
static unsigned long find_next(struct sline *sline,
520
             unsigned long mark,
521
             unsigned long i,
522
             unsigned long cnt,
523
             int look_for_uninteresting)
524
0
{
525
  /* We have examined up to i-1 and are about to look at i.
526
   * Find next interesting or uninteresting line.  Here,
527
   * "interesting" does not mean interesting(), but marked by
528
   * the give_context() function below (i.e. it includes context
529
   * lines that are not interesting to interesting() function
530
   * that are surrounded by interesting() ones.
531
   */
532
0
  while (i <= cnt)
533
0
    if (look_for_uninteresting
534
0
        ? !(sline[i].flag & mark)
535
0
        : (sline[i].flag & mark))
536
0
      return i;
537
0
    else
538
0
      i++;
539
0
  return i;
540
0
}
541
542
static int give_context(struct sline *sline, unsigned long cnt, int num_parent)
543
0
{
544
0
  unsigned long all_mask = (1UL<<num_parent) - 1;
545
0
  unsigned long mark = (1UL<<num_parent);
546
0
  unsigned long no_pre_delete = (2UL<<num_parent);
547
0
  unsigned long i;
548
549
  /* Two groups of interesting lines may have a short gap of
550
   * uninteresting lines.  Connect such groups to give them a
551
   * bit of context.
552
   *
553
   * We first start from what the interesting() function says,
554
   * and mark them with "mark", and paint context lines with the
555
   * mark.  So interesting() would still say false for such context
556
   * lines but they are treated as "interesting" in the end.
557
   */
558
0
  i = find_next(sline, mark, 0, cnt, 0);
559
0
  if (cnt < i)
560
0
    return 0;
561
562
0
  while (i <= cnt) {
563
0
    unsigned long j = (context < i) ? (i - context) : 0;
564
0
    unsigned long k;
565
566
    /* Paint a few lines before the first interesting line. */
567
0
    while (j < i) {
568
0
      if (!(sline[j].flag & mark))
569
0
        sline[j].flag |= no_pre_delete;
570
0
      sline[j++].flag |= mark;
571
0
    }
572
573
0
  again:
574
    /* we know up to i is to be included.  where does the
575
     * next uninteresting one start?
576
     */
577
0
    j = find_next(sline, mark, i, cnt, 1);
578
0
    if (cnt < j)
579
0
      break; /* the rest are all interesting */
580
581
    /* lookahead context lines */
582
0
    k = find_next(sline, mark, j, cnt, 0);
583
0
    j = adjust_hunk_tail(sline, all_mask, i, j);
584
585
0
    if (k < j + context) {
586
      /* k is interesting and [j,k) are not, but
587
       * paint them interesting because the gap is small.
588
       */
589
0
      while (j < k)
590
0
        sline[j++].flag |= mark;
591
0
      i = k;
592
0
      goto again;
593
0
    }
594
595
    /* j is the first uninteresting line and there is
596
     * no overlap beyond it within context lines.  Paint
597
     * the trailing edge a bit.
598
     */
599
0
    i = k;
600
0
    k = (j + context < cnt+1) ? j + context : cnt+1;
601
0
    while (j < k)
602
0
      sline[j++].flag |= mark;
603
0
  }
604
0
  return 1;
605
0
}
606
607
static int make_hunks(struct sline *sline, unsigned long cnt,
608
           int num_parent, int dense)
609
0
{
610
0
  unsigned long all_mask = (1UL<<num_parent) - 1;
611
0
  unsigned long mark = (1UL<<num_parent);
612
0
  unsigned long i;
613
0
  int has_interesting = 0;
614
615
0
  for (i = 0; i <= cnt; i++) {
616
0
    if (interesting(&sline[i], all_mask))
617
0
      sline[i].flag |= mark;
618
0
    else
619
0
      sline[i].flag &= ~mark;
620
0
  }
621
0
  if (!dense)
622
0
    return give_context(sline, cnt, num_parent);
623
624
  /* Look at each hunk, and if we have changes from only one
625
   * parent, or the changes are the same from all but one
626
   * parent, mark that uninteresting.
627
   */
628
0
  i = 0;
629
0
  while (i <= cnt) {
630
0
    unsigned long j, hunk_begin, hunk_end;
631
0
    unsigned long same_diff;
632
0
    while (i <= cnt && !(sline[i].flag & mark))
633
0
      i++;
634
0
    if (cnt < i)
635
0
      break; /* No more interesting hunks */
636
0
    hunk_begin = i;
637
0
    for (j = i + 1; j <= cnt; j++) {
638
0
      if (!(sline[j].flag & mark)) {
639
        /* Look beyond the end to see if there
640
         * is an interesting line after this
641
         * hunk within context span.
642
         */
643
0
        unsigned long la; /* lookahead */
644
0
        int contin = 0;
645
0
        la = adjust_hunk_tail(sline, all_mask,
646
0
                 hunk_begin, j);
647
0
        la = (la + context < cnt + 1) ?
648
0
          (la + context) : cnt + 1;
649
0
        while (la && j <= --la) {
650
0
          if (sline[la].flag & mark) {
651
0
            contin = 1;
652
0
            break;
653
0
          }
654
0
        }
655
0
        if (!contin)
656
0
          break;
657
0
        j = la;
658
0
      }
659
0
    }
660
0
    hunk_end = j;
661
662
    /* [i..hunk_end) are interesting.  Now is it really
663
     * interesting?  We check if there are only two versions
664
     * and the result matches one of them.  That is, we look
665
     * at:
666
     *   (+) line, which records lines added to which parents;
667
     *       this line appears in the result.
668
     *   (-) line, which records from what parents the line
669
     *       was removed; this line does not appear in the result.
670
     * then check the set of parents the result has difference
671
     * from, from all lines.  If there are lines that have
672
     * different set of parents that the result has differences
673
     * from, that means we have more than two versions.
674
     *
675
     * Even when we have only two versions, if the result does
676
     * not match any of the parents, the it should be considered
677
     * interesting.  In such a case, we would have all '+' line.
678
     * After passing the above "two versions" test, that would
679
     * appear as "the same set of parents" to be "all parents".
680
     */
681
0
    same_diff = 0;
682
0
    has_interesting = 0;
683
0
    for (j = i; j < hunk_end && !has_interesting; j++) {
684
0
      unsigned long this_diff = sline[j].flag & all_mask;
685
0
      struct lline *ll = sline[j].lost;
686
0
      if (this_diff) {
687
        /* This has some changes.  Is it the
688
         * same as others?
689
         */
690
0
        if (!same_diff)
691
0
          same_diff = this_diff;
692
0
        else if (same_diff != this_diff) {
693
0
          has_interesting = 1;
694
0
          break;
695
0
        }
696
0
      }
697
0
      while (ll && !has_interesting) {
698
        /* Lost this line from these parents;
699
         * who are they?  Are they the same?
700
         */
701
0
        this_diff = ll->parent_map;
702
0
        if (!same_diff)
703
0
          same_diff = this_diff;
704
0
        else if (same_diff != this_diff) {
705
0
          has_interesting = 1;
706
0
        }
707
0
        ll = ll->next;
708
0
      }
709
0
    }
710
711
0
    if (!has_interesting && same_diff != all_mask) {
712
      /* This hunk is not that interesting after all */
713
0
      for (j = hunk_begin; j < hunk_end; j++)
714
0
        sline[j].flag &= ~mark;
715
0
    }
716
0
    i = hunk_end;
717
0
  }
718
719
0
  has_interesting = give_context(sline, cnt, num_parent);
720
0
  return has_interesting;
721
0
}
722
723
static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long l1, int n, unsigned long null_context)
724
0
{
725
0
  l0 = sline[l0].p_lno[n];
726
0
  l1 = sline[l1].p_lno[n];
727
0
  printf(" -%lu,%lu", l0, l1-l0-null_context);
728
0
}
729
730
static int hunk_comment_line(const char *bol)
731
0
{
732
0
  int ch;
733
734
0
  if (!bol)
735
0
    return 0;
736
0
  ch = *bol & 0xff;
737
0
  return (isalpha(ch) || ch == '_' || ch == '$');
738
0
}
739
740
static void show_line_to_eol(const char *line, int len, const char *reset)
741
0
{
742
0
  int saw_cr_at_eol = 0;
743
0
  if (len < 0)
744
0
    len = strlen(line);
745
0
  saw_cr_at_eol = (len && line[len-1] == '\r');
746
747
0
  printf("%.*s%s%s\n", len - saw_cr_at_eol, line,
748
0
         reset,
749
0
         saw_cr_at_eol ? "\r" : "");
750
0
}
751
752
static void dump_sline(struct sline *sline, const char *line_prefix,
753
           unsigned long cnt, int num_parent,
754
           enum git_colorbool use_color, int result_deleted)
755
0
{
756
0
  unsigned long mark = (1UL<<num_parent);
757
0
  unsigned long no_pre_delete = (2UL<<num_parent);
758
0
  int i;
759
0
  unsigned long lno = 0;
760
0
  const char *c_frag = diff_get_color(use_color, DIFF_FRAGINFO);
761
0
  const char *c_func = diff_get_color(use_color, DIFF_FUNCINFO);
762
0
  const char *c_new = diff_get_color(use_color, DIFF_FILE_NEW);
763
0
  const char *c_old = diff_get_color(use_color, DIFF_FILE_OLD);
764
0
  const char *c_context = diff_get_color(use_color, DIFF_CONTEXT);
765
0
  const char *c_reset = diff_get_color(use_color, DIFF_RESET);
766
767
0
  if (result_deleted)
768
0
    return; /* result deleted */
769
770
0
  while (1) {
771
0
    unsigned long hunk_end;
772
0
    unsigned long rlines;
773
0
    const char *hunk_comment = NULL;
774
0
    unsigned long null_context = 0;
775
776
0
    while (lno <= cnt && !(sline[lno].flag & mark)) {
777
0
      if (hunk_comment_line(sline[lno].bol))
778
0
        hunk_comment = sline[lno].bol;
779
0
      lno++;
780
0
    }
781
0
    if (cnt < lno)
782
0
      break;
783
0
    else {
784
0
      for (hunk_end = lno + 1; hunk_end <= cnt; hunk_end++)
785
0
        if (!(sline[hunk_end].flag & mark))
786
0
          break;
787
0
    }
788
0
    rlines = hunk_end - lno;
789
0
    if (cnt < hunk_end)
790
0
      rlines--; /* pointing at the last delete hunk */
791
792
0
    if (!context) {
793
      /*
794
       * Even when running with --unified=0, all
795
       * lines in the hunk needs to be processed in
796
       * the loop below in order to show the
797
       * deletion recorded in lost_head.  However,
798
       * we do not want to show the resulting line
799
       * with all blank context markers in such a
800
       * case.  Compensate.
801
       */
802
0
      unsigned long j;
803
0
      for (j = lno; j < hunk_end; j++)
804
0
        if (!(sline[j].flag & (mark-1)))
805
0
          null_context++;
806
0
      rlines -= null_context;
807
0
    }
808
809
0
    printf("%s%s", line_prefix, c_frag);
810
0
    for (i = 0; i <= num_parent; i++) putchar(combine_marker);
811
0
    for (i = 0; i < num_parent; i++)
812
0
      show_parent_lno(sline, lno, hunk_end, i, null_context);
813
0
    printf(" +%lu,%lu ", lno+1, rlines);
814
0
    for (i = 0; i <= num_parent; i++) putchar(combine_marker);
815
816
0
    if (hunk_comment) {
817
0
      int comment_end = 0;
818
0
      for (i = 0; i < 40; i++) {
819
0
        int ch = hunk_comment[i] & 0xff;
820
0
        if (!ch || ch == '\n')
821
0
          break;
822
0
        if (!isspace(ch))
823
0
            comment_end = i;
824
0
      }
825
0
      if (comment_end)
826
0
        printf("%s%s %s%s", c_reset,
827
0
                c_context, c_reset,
828
0
                c_func);
829
0
      for (i = 0; i < comment_end; i++)
830
0
        putchar(hunk_comment[i]);
831
0
    }
832
833
0
    printf("%s\n", c_reset);
834
0
    while (lno < hunk_end) {
835
0
      struct lline *ll;
836
0
      int j;
837
0
      unsigned long p_mask;
838
0
      struct sline *sl = &sline[lno++];
839
0
      ll = (sl->flag & no_pre_delete) ? NULL : sl->lost;
840
0
      while (ll) {
841
0
        printf("%s%s", line_prefix, c_old);
842
0
        for (j = 0; j < num_parent; j++) {
843
0
          if (ll->parent_map & (1UL<<j))
844
0
            putchar('-');
845
0
          else
846
0
            putchar(' ');
847
0
        }
848
0
        show_line_to_eol(ll->line, -1, c_reset);
849
0
        ll = ll->next;
850
0
      }
851
0
      if (cnt < lno)
852
0
        break;
853
0
      p_mask = 1;
854
0
      fputs(line_prefix, stdout);
855
0
      if (!(sl->flag & (mark-1))) {
856
        /*
857
         * This sline was here to hang the
858
         * lost lines in front of it.
859
         */
860
0
        if (!context)
861
0
          continue;
862
0
        fputs(c_context, stdout);
863
0
      }
864
0
      else
865
0
        fputs(c_new, stdout);
866
0
      for (j = 0; j < num_parent; j++) {
867
0
        if (p_mask & sl->flag)
868
0
          putchar('+');
869
0
        else
870
0
          putchar(' ');
871
0
        p_mask <<= 1;
872
0
      }
873
0
      show_line_to_eol(sl->bol, sl->len, c_reset);
874
0
    }
875
0
  }
876
0
}
877
878
static void reuse_combine_diff(struct sline *sline, unsigned long cnt,
879
             int i, int j)
880
0
{
881
  /* We have already examined parent j and we know parent i
882
   * and parent j are the same, so reuse the combined result
883
   * of parent j for parent i.
884
   */
885
0
  unsigned long lno, imask, jmask;
886
0
  imask = (1UL<<i);
887
0
  jmask = (1UL<<j);
888
889
0
  for (lno = 0; lno <= cnt; lno++) {
890
0
    struct lline *ll = sline->lost;
891
0
    sline->p_lno[i] = sline->p_lno[j];
892
0
    while (ll) {
893
0
      if (ll->parent_map & jmask)
894
0
        ll->parent_map |= imask;
895
0
      ll = ll->next;
896
0
    }
897
0
    if (sline->flag & jmask)
898
0
      sline->flag |= imask;
899
0
    sline++;
900
0
  }
901
  /* the overall size of the file (sline[cnt]) */
902
0
  sline->p_lno[i] = sline->p_lno[j];
903
0
}
904
905
static void dump_quoted_path(const char *head,
906
           const char *prefix,
907
           const char *path,
908
           const char *line_prefix,
909
           const char *c_meta, const char *c_reset)
910
0
{
911
0
  static struct strbuf buf = STRBUF_INIT;
912
913
0
  strbuf_reset(&buf);
914
0
  strbuf_addstr(&buf, line_prefix);
915
0
  strbuf_addstr(&buf, c_meta);
916
0
  strbuf_addstr(&buf, head);
917
0
  quote_two_c_style(&buf, prefix, path, 0);
918
0
  strbuf_addstr(&buf, c_reset);
919
0
  puts(buf.buf);
920
0
}
921
922
static void show_combined_header(struct combine_diff_path *elem,
923
         int num_parent,
924
         struct rev_info *rev,
925
         const char *line_prefix,
926
         int mode_differs,
927
         int show_file_header)
928
0
{
929
0
  struct diff_options *opt = &rev->diffopt;
930
0
  int abbrev = opt->flags.full_index ? the_hash_algo->hexsz : DEFAULT_ABBREV;
931
0
  const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
932
0
  const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
933
0
  const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
934
0
  const char *c_reset = diff_get_color_opt(opt, DIFF_RESET);
935
0
  const char *abb;
936
0
  int added = 0;
937
0
  int deleted = 0;
938
0
  int i;
939
0
  int dense = rev->dense_combined_merges;
940
941
0
  if (rev->loginfo && !rev->no_commit_id)
942
0
    show_log(rev);
943
944
0
  dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
945
0
       "", elem->path, line_prefix, c_meta, c_reset);
946
0
  printf("%s%sindex ", line_prefix, c_meta);
947
0
  for (i = 0; i < num_parent; i++) {
948
0
    abb = repo_find_unique_abbrev(the_repository,
949
0
                &elem->parent[i].oid, abbrev);
950
0
    printf("%s%s", i ? "," : "", abb);
951
0
  }
952
0
  abb = repo_find_unique_abbrev(the_repository, &elem->oid, abbrev);
953
0
  printf("..%s%s\n", abb, c_reset);
954
955
0
  if (mode_differs) {
956
0
    deleted = !elem->mode;
957
958
    /* We say it was added if nobody had it */
959
0
    added = !deleted;
960
0
    for (i = 0; added && i < num_parent; i++)
961
0
      if (elem->parent[i].status !=
962
0
          DIFF_STATUS_ADDED)
963
0
        added = 0;
964
0
    if (added)
965
0
      printf("%s%snew file mode %06o",
966
0
             line_prefix, c_meta, elem->mode);
967
0
    else {
968
0
      if (deleted)
969
0
        printf("%s%sdeleted file ",
970
0
               line_prefix, c_meta);
971
0
      printf("mode ");
972
0
      for (i = 0; i < num_parent; i++) {
973
0
        printf("%s%06o", i ? "," : "",
974
0
               elem->parent[i].mode);
975
0
      }
976
0
      if (elem->mode)
977
0
        printf("..%06o", elem->mode);
978
0
    }
979
0
    printf("%s\n", c_reset);
980
0
  }
981
982
0
  if (!show_file_header)
983
0
    return;
984
985
0
  if (rev->combined_all_paths) {
986
0
    for (i = 0; i < num_parent; i++) {
987
0
      const char *path = elem->parent[i].path ?
988
0
             elem->parent[i].path :
989
0
             elem->path;
990
0
      if (elem->parent[i].status == DIFF_STATUS_ADDED)
991
0
        dump_quoted_path("--- ", "", "/dev/null",
992
0
             line_prefix, c_meta, c_reset);
993
0
      else
994
0
        dump_quoted_path("--- ", a_prefix, path,
995
0
             line_prefix, c_meta, c_reset);
996
0
    }
997
0
  } else {
998
0
    if (added)
999
0
      dump_quoted_path("--- ", "", "/dev/null",
1000
0
           line_prefix, c_meta, c_reset);
1001
0
    else
1002
0
      dump_quoted_path("--- ", a_prefix, elem->path,
1003
0
           line_prefix, c_meta, c_reset);
1004
0
  }
1005
0
  if (deleted)
1006
0
    dump_quoted_path("+++ ", "", "/dev/null",
1007
0
         line_prefix, c_meta, c_reset);
1008
0
  else
1009
0
    dump_quoted_path("+++ ", b_prefix, elem->path,
1010
0
         line_prefix, c_meta, c_reset);
1011
0
}
1012
1013
static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
1014
          int working_tree_file,
1015
          struct rev_info *rev)
1016
0
{
1017
0
  struct diff_options *opt = &rev->diffopt;
1018
0
  unsigned long result_size, cnt, lno;
1019
0
  int result_deleted = 0;
1020
0
  char *result, *cp;
1021
0
  struct sline *sline; /* survived lines */
1022
0
  int mode_differs = 0;
1023
0
  int i, show_hunks;
1024
0
  mmfile_t result_file;
1025
0
  struct userdiff_driver *userdiff;
1026
0
  struct userdiff_driver *textconv = NULL;
1027
0
  int is_binary;
1028
0
  const char *line_prefix = diff_line_prefix(opt);
1029
1030
0
  context = opt->context;
1031
0
  userdiff = userdiff_find_by_path(opt->repo->index, elem->path);
1032
0
  if (!userdiff)
1033
0
    userdiff = userdiff_find_by_name("default");
1034
0
  if (opt->flags.allow_textconv)
1035
0
    textconv = userdiff_get_textconv(opt->repo, userdiff);
1036
1037
  /* Read the result of merge first */
1038
0
  if (!working_tree_file)
1039
0
    result = grab_blob(opt->repo, &elem->oid, elem->mode, &result_size,
1040
0
           textconv, elem->path);
1041
0
  else {
1042
    /* Used by diff-tree to read from the working tree */
1043
0
    struct stat st;
1044
0
    int fd = -1;
1045
1046
0
    if (lstat(elem->path, &st) < 0)
1047
0
      goto deleted_file;
1048
1049
0
    if (S_ISLNK(st.st_mode)) {
1050
0
      struct strbuf buf = STRBUF_INIT;
1051
1052
0
      if (strbuf_readlink(&buf, elem->path, st.st_size) < 0) {
1053
0
        error_errno("readlink(%s)", elem->path);
1054
0
        return;
1055
0
      }
1056
0
      result_size = buf.len;
1057
0
      result = strbuf_detach(&buf, NULL);
1058
0
      elem->mode = canon_mode(st.st_mode);
1059
0
    } else if (S_ISDIR(st.st_mode)) {
1060
0
      struct object_id oid;
1061
0
      if (repo_resolve_gitlink_ref(the_repository, elem->path,
1062
0
                 "HEAD", &oid) < 0)
1063
0
        result = grab_blob(opt->repo, &elem->oid,
1064
0
               elem->mode, &result_size,
1065
0
               NULL, NULL);
1066
0
      else
1067
0
        result = grab_blob(opt->repo, &oid, elem->mode,
1068
0
               &result_size, NULL, NULL);
1069
0
    } else if (textconv) {
1070
0
      struct diff_filespec *df = alloc_filespec(elem->path);
1071
0
      fill_filespec(df, null_oid(the_hash_algo), 0, st.st_mode);
1072
0
      result_size = fill_textconv(opt->repo, textconv, df, &result);
1073
0
      free_filespec(df);
1074
0
    } else if (0 <= (fd = open(elem->path, O_RDONLY))) {
1075
0
      size_t len = xsize_t(st.st_size);
1076
0
      ssize_t done;
1077
0
      int is_file, i;
1078
1079
0
      elem->mode = canon_mode(st.st_mode);
1080
      /* if symlinks don't work, assume symlink if all parents
1081
       * are symlinks
1082
       */
1083
0
      is_file = repo_has_symlinks(rev->repo);
1084
0
      for (i = 0; !is_file && i < num_parent; i++)
1085
0
        is_file = !S_ISLNK(elem->parent[i].mode);
1086
0
      if (!is_file)
1087
0
        elem->mode = canon_mode(S_IFLNK);
1088
1089
0
      result_size = len;
1090
0
      result = xmallocz(len);
1091
1092
0
      done = read_in_full(fd, result, len);
1093
0
      if (done < 0)
1094
0
        die_errno("read error '%s'", elem->path);
1095
0
      else if (done < len)
1096
0
        die("early EOF '%s'", elem->path);
1097
1098
      /* If not a fake symlink, apply filters, e.g. autocrlf */
1099
0
      if (is_file) {
1100
0
        struct strbuf buf = STRBUF_INIT;
1101
1102
0
        if (convert_to_git(rev->diffopt.repo->index,
1103
0
               elem->path, result, len, &buf, global_conv_flags_eol)) {
1104
0
          free(result);
1105
0
          result = strbuf_detach(&buf, &len);
1106
0
          result_size = len;
1107
0
        }
1108
0
      }
1109
0
    }
1110
0
    else {
1111
0
    deleted_file:
1112
0
      result_deleted = 1;
1113
0
      result_size = 0;
1114
0
      elem->mode = 0;
1115
0
      result = xcalloc(1, 1);
1116
0
    }
1117
1118
0
    if (0 <= fd)
1119
0
      close(fd);
1120
0
  }
1121
1122
0
  for (i = 0; i < num_parent; i++) {
1123
0
    if (elem->parent[i].mode != elem->mode) {
1124
0
      mode_differs = 1;
1125
0
      break;
1126
0
    }
1127
0
  }
1128
1129
0
  if (textconv)
1130
0
    is_binary = 0;
1131
0
  else if (userdiff->binary != -1)
1132
0
    is_binary = userdiff->binary;
1133
0
  else {
1134
0
    is_binary = buffer_is_binary(result, result_size);
1135
0
    for (i = 0; !is_binary && i < num_parent; i++) {
1136
0
      char *buf;
1137
0
      unsigned long size;
1138
0
      buf = grab_blob(opt->repo,
1139
0
          &elem->parent[i].oid,
1140
0
          elem->parent[i].mode,
1141
0
          &size, NULL, NULL);
1142
0
      if (buffer_is_binary(buf, size))
1143
0
        is_binary = 1;
1144
0
      free(buf);
1145
0
    }
1146
0
  }
1147
0
  if (is_binary) {
1148
0
    show_combined_header(elem, num_parent, rev,
1149
0
             line_prefix, mode_differs, 0);
1150
0
    printf("Binary files differ\n");
1151
0
    free(result);
1152
0
    return;
1153
0
  }
1154
1155
0
  for (cnt = 0, cp = result; cp < result + result_size; cp++) {
1156
0
    if (*cp == '\n')
1157
0
      cnt++;
1158
0
  }
1159
0
  if (result_size && result[result_size-1] != '\n')
1160
0
    cnt++; /* incomplete line */
1161
1162
0
  CALLOC_ARRAY(sline, st_add(cnt, 2));
1163
0
  sline[0].bol = result;
1164
0
  for (lno = 0, cp = result; cp < result + result_size; cp++) {
1165
0
    if (*cp == '\n') {
1166
0
      sline[lno].len = cp - sline[lno].bol;
1167
0
      lno++;
1168
0
      if (lno < cnt)
1169
0
        sline[lno].bol = cp + 1;
1170
0
    }
1171
0
  }
1172
0
  if (result_size && result[result_size-1] != '\n')
1173
0
    sline[cnt-1].len = result_size - (sline[cnt-1].bol - result);
1174
1175
0
  result_file.ptr = result;
1176
0
  result_file.size = result_size;
1177
1178
  /*
1179
   * Even p_lno[cnt+1] is valid -- that is for the end line number
1180
   * for deletion hunk at the end.
1181
   */
1182
0
  CALLOC_ARRAY(sline[0].p_lno, st_mult(st_add(cnt, 2), num_parent));
1183
0
  for (lno = 0; lno <= cnt; lno++)
1184
0
    sline[lno+1].p_lno = sline[lno].p_lno + num_parent;
1185
1186
0
  for (i = 0; i < num_parent; i++) {
1187
0
    int j;
1188
0
    for (j = 0; j < i; j++) {
1189
0
      if (oideq(&elem->parent[i].oid,
1190
0
          &elem->parent[j].oid)) {
1191
0
        reuse_combine_diff(sline, cnt, i, j);
1192
0
        break;
1193
0
      }
1194
0
    }
1195
0
    if (i <= j)
1196
0
      combine_diff(opt->repo,
1197
0
             &elem->parent[i].oid,
1198
0
             elem->parent[i].mode,
1199
0
             &result_file, sline,
1200
0
             cnt, i, num_parent, result_deleted,
1201
0
             textconv, elem->path, opt->xdl_opts);
1202
0
  }
1203
1204
0
  show_hunks = make_hunks(sline, cnt, num_parent, rev->dense_combined_merges);
1205
1206
0
  if (show_hunks || mode_differs || working_tree_file) {
1207
0
    show_combined_header(elem, num_parent, rev,
1208
0
             line_prefix, mode_differs, 1);
1209
0
    dump_sline(sline, line_prefix, cnt, num_parent,
1210
0
         opt->use_color, result_deleted);
1211
0
  }
1212
0
  free(result);
1213
1214
0
  for (lno = 0; lno < cnt + 2; lno++) {
1215
0
    if (sline[lno].lost) {
1216
0
      struct lline *ll = sline[lno].lost;
1217
0
      while (ll) {
1218
0
        struct lline *tmp = ll;
1219
0
        ll = ll->next;
1220
0
        free(tmp);
1221
0
      }
1222
0
    }
1223
0
  }
1224
0
  free(sline[0].p_lno);
1225
0
  free(sline);
1226
0
}
1227
1228
static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev)
1229
0
{
1230
0
  struct diff_options *opt = &rev->diffopt;
1231
0
  int line_termination, inter_name_termination, i;
1232
0
  const char *line_prefix = diff_line_prefix(opt);
1233
1234
0
  line_termination = opt->line_termination;
1235
0
  inter_name_termination = '\t';
1236
0
  if (!line_termination)
1237
0
    inter_name_termination = 0;
1238
1239
0
  if (rev->loginfo && !rev->no_commit_id)
1240
0
    show_log(rev);
1241
1242
1243
0
  if (opt->output_format & DIFF_FORMAT_RAW) {
1244
0
    printf("%s", line_prefix);
1245
1246
    /* As many colons as there are parents */
1247
0
    for (i = 0; i < num_parent; i++)
1248
0
      putchar(':');
1249
1250
    /* Show the modes */
1251
0
    for (i = 0; i < num_parent; i++)
1252
0
      printf("%06o ", p->parent[i].mode);
1253
0
    printf("%06o", p->mode);
1254
1255
    /* Show sha1's */
1256
0
    for (i = 0; i < num_parent; i++)
1257
0
      printf(" %s", diff_aligned_abbrev(&p->parent[i].oid,
1258
0
                opt->abbrev));
1259
0
    printf(" %s ", diff_aligned_abbrev(&p->oid, opt->abbrev));
1260
0
  }
1261
1262
0
  if (opt->output_format & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) {
1263
0
    for (i = 0; i < num_parent; i++)
1264
0
      putchar(p->parent[i].status);
1265
0
    putchar(inter_name_termination);
1266
0
  }
1267
1268
0
  for (i = 0; i < num_parent; i++)
1269
0
    if (rev->combined_all_paths) {
1270
0
      const char *path = p->parent[i].path ?
1271
0
             p->parent[i].path :
1272
0
             p->path;
1273
0
      write_name_quoted(path, stdout, inter_name_termination);
1274
0
    }
1275
0
  write_name_quoted(p->path, stdout, line_termination);
1276
0
}
1277
1278
/*
1279
 * The result (p->elem) is from the working tree and their
1280
 * parents are typically from multiple stages during a merge
1281
 * (i.e. diff-files) or the state in HEAD and in the index
1282
 * (i.e. diff-index).
1283
 */
1284
void show_combined_diff(struct combine_diff_path *p,
1285
           int num_parent,
1286
           struct rev_info *rev)
1287
0
{
1288
0
  struct diff_options *opt = &rev->diffopt;
1289
1290
0
  if (opt->output_format & (DIFF_FORMAT_RAW |
1291
0
          DIFF_FORMAT_NAME |
1292
0
          DIFF_FORMAT_NAME_STATUS))
1293
0
    show_raw_diff(p, num_parent, rev);
1294
0
  else if (opt->output_format & DIFF_FORMAT_PATCH)
1295
0
    show_patch_diff(p, num_parent, 1, rev);
1296
0
}
1297
1298
static void free_combined_pair(struct diff_filepair *pair)
1299
0
{
1300
0
  free(pair->two);
1301
0
  free(pair);
1302
0
}
1303
1304
/*
1305
 * A combine_diff_path expresses N parents on the LHS against 1 merge
1306
 * result. Synthesize a diff_filepair that has N entries on the "one"
1307
 * side and 1 entry on the "two" side.
1308
 *
1309
 * In the future, we might want to add more data to combine_diff_path
1310
 * so that we can fill fields we are ignoring (most notably, size) here,
1311
 * but currently nobody uses it, so this should suffice for now.
1312
 */
1313
static struct diff_filepair *combined_pair(struct combine_diff_path *p,
1314
             int num_parent)
1315
0
{
1316
0
  int i;
1317
0
  struct diff_filepair *pair;
1318
0
  struct diff_filespec *pool;
1319
1320
0
  CALLOC_ARRAY(pair, 1);
1321
0
  CALLOC_ARRAY(pool, st_add(num_parent, 1));
1322
0
  pair->one = pool + 1;
1323
0
  pair->two = pool;
1324
1325
0
  for (i = 0; i < num_parent; i++) {
1326
0
    pair->one[i].path = p->path;
1327
0
    pair->one[i].mode = p->parent[i].mode;
1328
0
    oidcpy(&pair->one[i].oid, &p->parent[i].oid);
1329
0
    pair->one[i].oid_valid = !is_null_oid(&p->parent[i].oid);
1330
0
    pair->one[i].has_more_entries = 1;
1331
0
  }
1332
0
  pair->one[num_parent - 1].has_more_entries = 0;
1333
1334
0
  pair->two->path = p->path;
1335
0
  pair->two->mode = p->mode;
1336
0
  oidcpy(&pair->two->oid, &p->oid);
1337
0
  pair->two->oid_valid = !is_null_oid(&p->oid);
1338
0
  return pair;
1339
0
}
1340
1341
static void handle_combined_callback(struct diff_options *opt,
1342
             struct combine_diff_path *paths,
1343
             int num_parent,
1344
             int num_paths)
1345
0
{
1346
0
  struct combine_diff_path *p;
1347
0
  struct diff_queue_struct q;
1348
0
  int i;
1349
1350
0
  CALLOC_ARRAY(q.queue, num_paths);
1351
0
  q.alloc = num_paths;
1352
0
  q.nr = num_paths;
1353
0
  for (i = 0, p = paths; p; p = p->next)
1354
0
    q.queue[i++] = combined_pair(p, num_parent);
1355
0
  opt->format_callback(&q, opt, opt->format_callback_data);
1356
0
  for (i = 0; i < num_paths; i++)
1357
0
    free_combined_pair(q.queue[i]);
1358
0
  free(q.queue);
1359
0
}
1360
1361
static const char *path_path(void *obj)
1362
0
{
1363
0
  struct combine_diff_path *path = (struct combine_diff_path *)obj;
1364
1365
0
  return path->path;
1366
0
}
1367
1368
/*
1369
 * Diff stat formats which we always compute solely against the first parent.
1370
 */
1371
0
#define STAT_FORMAT_MASK (DIFF_FORMAT_NUMSTAT \
1372
0
        | DIFF_FORMAT_SHORTSTAT \
1373
0
        | DIFF_FORMAT_SUMMARY \
1374
0
        | DIFF_FORMAT_DIRSTAT \
1375
0
        | DIFF_FORMAT_DIFFSTAT)
1376
1377
/* find set of paths that every parent touches */
1378
static struct combine_diff_path *find_paths_generic(const struct object_id *oid,
1379
  const struct oid_array *parents,
1380
  struct diff_options *opt,
1381
  int combined_all_paths)
1382
0
{
1383
0
  struct combine_diff_path *paths = NULL;
1384
0
  int i, num_parent = parents->nr;
1385
0
  int output_format = opt->output_format;
1386
0
  char *orderfile = opt->orderfile;
1387
1388
0
  opt->output_format = DIFF_FORMAT_NO_OUTPUT;
1389
  /* tell diff_tree to emit paths in sorted (=tree) order */
1390
0
  opt->orderfile = NULL;
1391
1392
  /* D(A,P1...Pn) = D(A,P1) ^ ... ^ D(A,Pn)  (wrt paths) */
1393
0
  for (i = 0; i < num_parent; i++) {
1394
    /*
1395
     * show stat against the first parent even when doing
1396
     * combined diff.
1397
     */
1398
0
    int stat_opt = output_format & STAT_FORMAT_MASK;
1399
0
    if (i == 0 && stat_opt)
1400
0
      opt->output_format = stat_opt;
1401
0
    else
1402
0
      opt->output_format = DIFF_FORMAT_NO_OUTPUT;
1403
0
    diff_tree_oid(&parents->oid[i], oid, "", opt);
1404
0
    diffcore_std(opt);
1405
0
    paths = intersect_paths(paths, i, num_parent,
1406
0
          combined_all_paths);
1407
1408
    /* if showing diff, show it in requested order */
1409
0
    if (opt->output_format != DIFF_FORMAT_NO_OUTPUT &&
1410
0
        orderfile) {
1411
0
      diffcore_order(orderfile);
1412
0
    }
1413
1414
0
    diff_flush(opt);
1415
0
  }
1416
1417
0
  opt->output_format = output_format;
1418
0
  opt->orderfile = orderfile;
1419
0
  return paths;
1420
0
}
1421
1422
1423
/*
1424
 * find set of paths that everybody touches, assuming diff is run without
1425
 * rename/copy detection, etc, comparing all trees simultaneously (= faster).
1426
 */
1427
static struct combine_diff_path *find_paths_multitree(
1428
  const struct object_id *oid, const struct oid_array *parents,
1429
  struct diff_options *opt)
1430
0
{
1431
0
  int i, nparent = parents->nr;
1432
0
  const struct object_id **parents_oid;
1433
0
  struct combine_diff_path *paths;
1434
0
  struct strbuf base;
1435
1436
0
  ALLOC_ARRAY(parents_oid, nparent);
1437
0
  for (i = 0; i < nparent; i++)
1438
0
    parents_oid[i] = &parents->oid[i];
1439
1440
0
  strbuf_init(&base, PATH_MAX);
1441
0
  paths = diff_tree_paths(oid, parents_oid, nparent, &base, opt);
1442
1443
0
  strbuf_release(&base);
1444
0
  free(parents_oid);
1445
0
  return paths;
1446
0
}
1447
1448
static int match_objfind(struct combine_diff_path *path,
1449
       int num_parent,
1450
       const struct oidset *set)
1451
0
{
1452
0
  int i;
1453
0
  if (oidset_contains(set, &path->oid))
1454
0
    return 1;
1455
0
  for (i = 0; i < num_parent; i++) {
1456
0
    if (oidset_contains(set, &path->parent[i].oid))
1457
0
      return 1;
1458
0
  }
1459
0
  return 0;
1460
0
}
1461
1462
static struct combine_diff_path *combined_objfind(struct diff_options *opt,
1463
              struct combine_diff_path *paths,
1464
              int num_parent)
1465
0
{
1466
0
  struct combine_diff_path *ret = NULL, **tail = &ret;
1467
0
  struct combine_diff_path *p = paths;
1468
1469
0
  while (p) {
1470
0
    struct combine_diff_path *next = p->next;
1471
1472
0
    if (match_objfind(p, num_parent, opt->objfind)) {
1473
0
      p->next = NULL;
1474
0
      *tail = p;
1475
0
      tail = &p->next;
1476
0
    } else {
1477
0
      free(p);
1478
0
    }
1479
0
    p = next;
1480
0
  }
1481
1482
0
  return ret;
1483
0
}
1484
1485
void diff_tree_combined(const struct object_id *oid,
1486
      const struct oid_array *parents,
1487
      struct rev_info *rev)
1488
0
{
1489
0
  struct diff_options *opt = &rev->diffopt;
1490
0
  struct diff_options diffopts;
1491
0
  struct combine_diff_path *p, *paths;
1492
0
  int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
1493
0
  int need_generic_pathscan;
1494
1495
0
  if (opt->ignore_regex_nr)
1496
0
    die("combined diff and '%s' cannot be used together",
1497
0
        "--ignore-matching-lines");
1498
0
  if (opt->close_file)
1499
0
    die("combined diff and '%s' cannot be used together",
1500
0
        "--output");
1501
1502
  /* nothing to do, if no parents */
1503
0
  if (!num_parent)
1504
0
    return;
1505
1506
0
  show_log_first = !!rev->loginfo && !rev->no_commit_id;
1507
0
  needsep = 0;
1508
0
  if (show_log_first) {
1509
0
    show_log(rev);
1510
1511
0
    if (rev->verbose_header && opt->output_format &&
1512
0
        opt->output_format != DIFF_FORMAT_NO_OUTPUT &&
1513
0
        !commit_format_is_empty(rev->commit_format))
1514
0
      printf("%s%c", diff_line_prefix(opt),
1515
0
             opt->line_termination);
1516
0
  }
1517
1518
0
  diffopts = *opt;
1519
0
  copy_pathspec(&diffopts.pathspec, &opt->pathspec);
1520
0
  diffopts.flags.allow_external = 0;
1521
0
  if (!opt->flags.no_recursive_diff_tree_combined)
1522
0
    diffopts.flags.recursive = 1;
1523
1524
  /* find set of paths that everybody touches
1525
   *
1526
   * NOTE
1527
   *
1528
   * Diffcore transformations are bound to diff_filespec and logic
1529
   * comparing two entries - i.e. they do not apply directly to combine
1530
   * diff.
1531
   *
1532
   * If some of such transformations is requested - we launch generic
1533
   * path scanning, which works significantly slower compared to
1534
   * simultaneous all-trees-in-one-go scan in find_paths_multitree().
1535
   *
1536
   * TODO some of the filters could be ported to work on
1537
   * combine_diff_paths - i.e. all functionality that skips paths, so in
1538
   * theory, we could end up having only multitree path scanning.
1539
   *
1540
   * NOTE please keep this semantically in sync with diffcore_std()
1541
   */
1542
0
  need_generic_pathscan = opt->skip_stat_unmatch  ||
1543
0
      opt->flags.follow_renames  ||
1544
0
      opt->break_opt != -1  ||
1545
0
      opt->detect_rename  ||
1546
0
      (opt->pickaxe_opts &
1547
0
       (DIFF_PICKAXE_KINDS_MASK & ~DIFF_PICKAXE_KIND_OBJFIND)) ||
1548
0
      opt->filter;
1549
1550
0
  if (need_generic_pathscan) {
1551
    /*
1552
     * NOTE generic case also handles --stat, as it computes
1553
     * diff(sha1,parent_i) for all i to do the job, specifically
1554
     * for parent0.
1555
     */
1556
0
    paths = find_paths_generic(oid, parents, &diffopts,
1557
0
             rev->combined_all_paths);
1558
0
  }
1559
0
  else {
1560
0
    int stat_opt;
1561
0
    paths = find_paths_multitree(oid, parents, &diffopts);
1562
1563
0
    if (opt->pickaxe_opts & DIFF_PICKAXE_KIND_OBJFIND)
1564
0
      paths = combined_objfind(opt, paths, num_parent);
1565
1566
    /*
1567
     * show stat against the first parent even
1568
     * when doing combined diff.
1569
     */
1570
0
    stat_opt = opt->output_format & STAT_FORMAT_MASK;
1571
0
    if (stat_opt) {
1572
0
      diffopts.output_format = stat_opt;
1573
1574
0
      diff_tree_oid(&parents->oid[0], oid, "", &diffopts);
1575
0
      diffcore_std(&diffopts);
1576
0
      if (opt->orderfile)
1577
0
        diffcore_order(opt->orderfile);
1578
0
      diff_flush(&diffopts);
1579
0
    }
1580
0
  }
1581
1582
  /* find out number of surviving paths */
1583
0
  for (num_paths = 0, p = paths; p; p = p->next)
1584
0
    num_paths++;
1585
1586
  /* order paths according to diffcore_order */
1587
0
  if (opt->orderfile && num_paths) {
1588
0
    struct obj_order *o;
1589
1590
0
    ALLOC_ARRAY(o, num_paths);
1591
0
    for (i = 0, p = paths; p; p = p->next, i++)
1592
0
      o[i].obj = p;
1593
0
    order_objects(opt->orderfile, path_path, o, num_paths);
1594
0
    for (i = 0; i < num_paths - 1; i++) {
1595
0
      p = o[i].obj;
1596
0
      p->next = o[i+1].obj;
1597
0
    }
1598
1599
0
    p = o[num_paths-1].obj;
1600
0
    p->next = NULL;
1601
0
    paths = o[0].obj;
1602
0
    free(o);
1603
0
  }
1604
1605
1606
0
  if (num_paths) {
1607
0
    if (opt->output_format & (DIFF_FORMAT_RAW |
1608
0
            DIFF_FORMAT_NAME |
1609
0
            DIFF_FORMAT_NAME_STATUS)) {
1610
0
      for (p = paths; p; p = p->next)
1611
0
        show_raw_diff(p, num_parent, rev);
1612
0
      needsep = 1;
1613
0
    }
1614
0
    else if (opt->output_format & STAT_FORMAT_MASK)
1615
0
      needsep = 1;
1616
0
    else if (opt->output_format & DIFF_FORMAT_CALLBACK)
1617
0
      handle_combined_callback(opt, paths, num_parent, num_paths);
1618
1619
0
    if (opt->output_format & DIFF_FORMAT_PATCH) {
1620
0
      if (needsep)
1621
0
        printf("%s%c", diff_line_prefix(opt),
1622
0
               opt->line_termination);
1623
0
      for (p = paths; p; p = p->next)
1624
0
        show_patch_diff(p, num_parent, 0, rev);
1625
0
    }
1626
0
  }
1627
1628
  /* Clean things up */
1629
0
  while (paths) {
1630
0
    struct combine_diff_path *tmp = paths;
1631
0
    paths = paths->next;
1632
0
    for (i = 0; i < num_parent; i++)
1633
0
      free(tmp->parent[i].path);
1634
0
    free(tmp);
1635
0
  }
1636
1637
0
  clear_pathspec(&diffopts.pathspec);
1638
0
}
1639
1640
void diff_tree_combined_merge(const struct commit *commit,
1641
            struct rev_info *rev)
1642
0
{
1643
0
  struct commit_list *parent = get_saved_parents(rev, commit);
1644
0
  struct oid_array parents = OID_ARRAY_INIT;
1645
1646
0
  while (parent) {
1647
0
    oid_array_append(&parents, &parent->item->object.oid);
1648
0
    parent = parent->next;
1649
0
  }
1650
0
  diff_tree_combined(&commit->object.oid, &parents, rev);
1651
0
  oid_array_clear(&parents);
1652
0
}
1653
1654
struct combine_diff_path *combine_diff_path_new(const char *path,
1655
            size_t path_len,
1656
            unsigned int mode,
1657
            const struct object_id *oid,
1658
            size_t num_parents)
1659
0
{
1660
0
  struct combine_diff_path *p;
1661
0
  size_t parent_len = st_mult(sizeof(p->parent[0]), num_parents);
1662
1663
0
  p = xmalloc(st_add4(sizeof(*p), path_len, 1, parent_len));
1664
0
  p->path = (char *)&(p->parent[num_parents]);
1665
0
  memcpy(p->path, path, path_len);
1666
0
  p->path[path_len] = 0;
1667
0
  p->next = NULL;
1668
0
  p->mode = mode;
1669
0
  oidcpy(&p->oid, oid);
1670
1671
0
  memset(p->parent, 0, parent_len);
1672
1673
0
  return p;
1674
0
}