Coverage Report

Created: 2025-12-31 07:01

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