Coverage Report

Created: 2024-09-08 06:24

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