Coverage Report

Created: 2026-10-17 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/graph.c
Line
Count
Source
1
#define DISABLE_SIGN_COMPARE_WARNINGS
2
3
#include "git-compat-util.h"
4
#include "gettext.h"
5
#include "config.h"
6
#include "commit.h"
7
#include "color.h"
8
#include "graph.h"
9
#include "revision.h"
10
#include "strvec.h"
11
12
/* Internal API */
13
14
/*
15
 * Output a padding line in the graph.
16
 * This is similar to graph_next_line().  However, it is guaranteed to
17
 * never print the current commit line.  Instead, if the commit line is
18
 * next, it will simply output a line of vertical padding, extending the
19
 * branch lines downwards, but leaving them otherwise unchanged.
20
 */
21
static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
22
23
/*
24
 * Print a strbuf.  If the graph is non-NULL, all lines but the first will be
25
 * prefixed with the graph output.
26
 *
27
 * If the strbuf ends with a newline, the output will end after this
28
 * newline.  A new graph line will not be printed after the final newline.
29
 * If the strbuf is empty, no output will be printed.
30
 *
31
 * Since the first line will not include the graph output, the caller is
32
 * responsible for printing this line's graph (perhaps via
33
 * graph_show_commit() or graph_show_oneline()) before calling
34
 * graph_show_strbuf().
35
 *
36
 * Note that unlike some other graph display functions, you must pass the file
37
 * handle directly. It is assumed that this is the same file handle as the
38
 * file specified by the graph diff options. This is necessary so that
39
 * graph_show_strbuf can be called even with a NULL graph.
40
 * If a NULL graph is supplied, the strbuf is printed as-is.
41
 */
42
static void graph_show_strbuf(struct git_graph *graph,
43
            FILE *file,
44
            struct strbuf const *sb);
45
46
/*
47
 * TODO:
48
 * - Limit the number of columns, similar to the way gitk does.
49
 *   If we reach more than a specified number of columns, omit
50
 *   sections of some columns.
51
 */
52
53
struct column {
54
  /*
55
   * The parent commit of this column.
56
   */
57
  struct commit *commit;
58
  /*
59
   * The color to (optionally) print this column in.  This is an
60
   * index into column_colors.
61
   */
62
  unsigned short color;
63
  /*
64
   * Marks if a commit is a non-first parent of a merge. These columns are
65
   * already visually connected to the merge commit and do not need
66
   * indentation.
67
   *
68
   * The first parent is the one that inherits the column and it can need
69
   * indentation if turns out to be a visual root and there's still
70
   * commits to render.
71
   */
72
  unsigned int is_merge_parent:1;
73
};
74
75
enum graph_state {
76
  GRAPH_PADDING,
77
  GRAPH_SKIP,
78
  GRAPH_PRE_COMMIT,
79
  GRAPH_PRE_ROOT,
80
  GRAPH_COMMIT,
81
  GRAPH_POST_MERGE,
82
  GRAPH_COLLAPSING
83
};
84
85
static void graph_show_line_prefix(const struct diff_options *diffopt)
86
0
{
87
0
  if (!diffopt || !diffopt->line_prefix)
88
0
    return;
89
90
0
  fputs(diffopt->line_prefix, diffopt->file);
91
0
}
92
93
static const char **column_colors;
94
static unsigned short column_colors_max;
95
96
static void parse_graph_colors_config(struct strvec *colors, const char *string)
97
0
{
98
0
  const char *end, *start;
99
100
0
  start = string;
101
0
  end = string + strlen(string);
102
0
  while (start < end) {
103
0
    const char *comma = strchrnul(start, ',');
104
0
    char color[COLOR_MAXLEN];
105
106
0
    if (!color_parse_mem(start, comma - start, color))
107
0
      strvec_push(colors, color);
108
0
    else
109
0
      warning(_("ignored invalid color '%.*s' in log.graphColors"),
110
0
        (int)(comma - start), start);
111
0
    start = comma + 1;
112
0
  }
113
0
  strvec_push(colors, GIT_COLOR_RESET);
114
0
}
115
116
void graph_set_column_colors(const char **colors, unsigned short colors_max)
117
0
{
118
0
  column_colors = colors;
119
0
  column_colors_max = colors_max;
120
0
}
121
122
static const char *column_get_color_code(unsigned short color)
123
0
{
124
0
  return column_colors[color];
125
0
}
126
127
struct graph_line {
128
  struct strbuf *buf;
129
  size_t width;
130
};
131
132
static inline void graph_line_addch(struct graph_line *line, int c)
133
0
{
134
0
  strbuf_addch(line->buf, c);
135
0
  line->width++;
136
0
}
137
138
static inline void graph_line_addchars(struct graph_line *line, int c, size_t n)
139
0
{
140
0
  strbuf_addchars(line->buf, c, n);
141
0
  line->width += n;
142
0
}
143
144
static inline void graph_line_addstr(struct graph_line *line, const char *s)
145
0
{
146
0
  strbuf_addstr(line->buf, s);
147
0
  line->width += strlen(s);
148
0
}
149
150
static inline void graph_line_addcolor(struct graph_line *line, unsigned short color)
151
0
{
152
0
  strbuf_addstr(line->buf, column_get_color_code(color));
153
0
}
154
155
static void graph_line_write_column(struct graph_line *line, const struct column *c,
156
            char col_char)
157
0
{
158
0
  if (c->color < column_colors_max)
159
0
    graph_line_addcolor(line, c->color);
160
0
  graph_line_addch(line, col_char);
161
0
  if (c->color < column_colors_max)
162
0
    graph_line_addcolor(line, column_colors_max);
163
0
}
164
165
struct git_graph {
166
  /*
167
   * The commit currently being processed
168
   */
169
  struct commit *commit;
170
  /* The rev-info used for the current traversal */
171
  struct rev_info *revs;
172
  /*
173
   * The number of interesting parents that this commit has.
174
   *
175
   * Note that this is not the same as the actual number of parents.
176
   * This count excludes parents that won't be printed in the graph
177
   * output, as determined by graph_is_interesting().
178
   */
179
  int num_parents;
180
  /*
181
   * The width of the graph output for this commit.
182
   * All rows for this commit are padded to this width, so that
183
   * messages printed after the graph output are aligned.
184
   */
185
  int width;
186
  /*
187
   * The next expansion row to print
188
   * when state is GRAPH_PRE_COMMIT
189
   */
190
  int expansion_row;
191
  /*
192
   * The current output state.
193
   * This tells us what kind of line graph_next_line() should output.
194
   */
195
  enum graph_state state;
196
  /*
197
   * The output state for the previous line of output.
198
   * This is primarily used to determine how the first merge line
199
   * should appear, based on the last line of the previous commit.
200
   */
201
  enum graph_state prev_state;
202
  /*
203
   * The index of the column that refers to this commit.
204
   *
205
   * If none of the incoming columns refer to this commit,
206
   * this will be equal to num_columns.
207
   */
208
  int commit_index;
209
  /*
210
   * The commit_index for the previously displayed commit.
211
   *
212
   * This is used to determine how the first line of a merge
213
   * graph output should appear, based on the last line of the
214
   * previous commit.
215
   */
216
  int prev_commit_index;
217
  /*
218
   * Which layout variant to use to display merge commits. If the
219
   * commit's first parent is known to be in a column to the left of the
220
   * merge, then this value is 0 and we use the layout on the left.
221
   * Otherwise, the value is 1 and the layout on the right is used. This
222
   * field tells us how many columns the first parent occupies.
223
   *
224
   *    0)      1)
225
   *
226
   *    | | | *-.   | | *---.
227
   *    | |_|/|\ \    | | |\ \ \
228
   *    |/| | | | |   | | | | | *
229
   */
230
  int merge_layout;
231
  /*
232
   * The number of columns added to the graph by the current commit. For
233
   * 2-way and octopus merges, this is usually one less than the
234
   * number of parents:
235
   *
236
   *    | | |     | |    \
237
   *    | * |     | *---. \
238
   *    | |\ \      | |\ \ \ \
239
   *    | | | |           | | | | | |
240
   *
241
   *    num_parents: 2    num_parents: 4
242
   *    edges_added: 1    edges_added: 3
243
   *
244
   * For left-skewed merges, the first parent fuses with its neighbor and
245
   * so one less column is added:
246
   *
247
   *    | | |     | |  \
248
   *    | * |     | *-. \
249
   *    |/| |     |/|\ \ \
250
   *    | | |     | | | | |
251
   *
252
   *    num_parents: 2    num_parents: 4
253
   *    edges_added: 0    edges_added: 2
254
   *
255
   * This number determines how edges to the right of the merge are
256
   * displayed in commit and post-merge lines; if no columns have been
257
   * added then a vertical line should be used where a right-tracking
258
   * line would otherwise be used.
259
   *
260
   *    | * \     | * |
261
   *    | |\ \      |/| |
262
   *    | | * \     | * |
263
   */
264
  int edges_added;
265
  /*
266
   * The number of columns added by the previous commit, which is used to
267
   * smooth edges appearing to the right of a commit in a commit line
268
   * following a post-merge line.
269
   */
270
  int prev_edges_added;
271
  /*
272
   * The maximum number of columns that can be stored in the columns
273
   * and new_columns arrays.  This is also half the number of entries
274
   * that can be stored in the mapping and old_mapping arrays.
275
   */
276
  int column_capacity;
277
  /*
278
   * The number of columns (also called "branch lines" in some places)
279
   */
280
  int num_columns;
281
  /*
282
   * The number of columns in the new_columns array
283
   */
284
  int num_new_columns;
285
  /*
286
   * The number of entries in the mapping array
287
   */
288
  int mapping_size;
289
  /*
290
   * The column state before we output the current commit.
291
   */
292
  struct column *columns;
293
  /*
294
   * The new column state after we output the current commit.
295
   * Only valid when state is GRAPH_COLLAPSING.
296
   */
297
  struct column *new_columns;
298
  /*
299
   * An array that tracks the current state of each
300
   * character in the output line during state GRAPH_COLLAPSING.
301
   * Each entry is -1 if this character is empty, or a non-negative
302
   * integer if the character contains a branch line.  The value of
303
   * the integer indicates the target position for this branch line.
304
   * (I.e., this array maps the current column positions to their
305
   * desired positions.)
306
   *
307
   * The maximum capacity of this array is always
308
   * sizeof(int) * 2 * column_capacity.
309
   */
310
  int *mapping;
311
  /*
312
   * A copy of the contents of the mapping array from the last commit,
313
   * which we use to improve the display of columns that are tracking
314
   * from right to left through a commit line.  We also use this to
315
   * avoid allocating a fresh array when we compute the next mapping.
316
   */
317
  int *old_mapping;
318
  /*
319
   * The current default column color being used.  This is
320
   * stored as an index into the array column_colors.
321
   */
322
  unsigned short default_column_color;
323
324
  /*
325
   * Scratch buffer for generating prefixes to be used with
326
   * diff_output_prefix_callback().
327
   */
328
  struct strbuf prefix_buf;
329
330
  /*
331
   * Lookahead buffer: up to 2 pre-fetched commits that will be shown.
332
   * Populated by get_revision() so graph_peek_next_visible() can use
333
   * actual walk results instead of peeking at rev_info internals.
334
   */
335
  struct commit *lookahead[2];
336
  int lookahead_nr;
337
338
  /*
339
   * If a commit is a visual root, we need to indent it to prevent
340
   * unrelated commits from being vertically adjacent to it.
341
   */
342
  unsigned int is_visual_root:1;
343
344
  /*
345
   * Indentation increases for each visual root adjacent to another visual
346
   * root, making visual root commits indentation cascade.
347
   */
348
  unsigned int visual_root_depth;
349
350
  /*
351
   * When a visual root is adjacent to other visual roots, the first one
352
   * can avoid indentation and the rest cascades, increasing the indentation
353
   * for each one.
354
   */
355
  unsigned int visual_root_cascade:1;
356
357
  /*
358
   * Set when the current commit was already present in graph->columns
359
   * before being processed.
360
   */
361
  unsigned int commit_in_columns:1;
362
};
363
364
struct graph_lookahead_flags {
365
366
  /*
367
   * Set when there will be a commit after the current one that will be
368
   * rendered.
369
   */
370
  unsigned int is_next_visible:1;
371
372
  /*
373
   * Set when the next visible commit is candidate to be a visual root.
374
   */
375
  unsigned int is_next_visual_root:1;
376
377
  /*
378
   * Set when the next visible commit will be rendered under the current
379
   * commit.
380
   */
381
  unsigned int next_has_column:1;
382
};
383
384
static inline int graph_needs_truncation(struct git_graph *graph, int lane)
385
0
{
386
0
  int max = graph->revs->graph_max_lanes;
387
  /*
388
   * Ignore values <= 0, meaning no limit.
389
   */
390
0
  return max > 0 && lane >= max;
391
0
}
392
393
static const char *diff_output_prefix_callback(struct diff_options *opt, void *data)
394
0
{
395
0
  struct git_graph *graph = data;
396
397
0
  assert(opt);
398
399
0
  if (!graph)
400
0
    return opt->line_prefix;
401
402
0
  strbuf_reset(&graph->prefix_buf);
403
0
  if (opt->line_prefix)
404
0
    strbuf_addstr(&graph->prefix_buf, opt->line_prefix);
405
0
  graph_padding_line(graph, &graph->prefix_buf);
406
0
  return graph->prefix_buf.buf;
407
0
}
408
409
static const struct diff_options *default_diffopt;
410
411
void graph_setup_line_prefix(struct diff_options *diffopt)
412
0
{
413
0
  default_diffopt = diffopt;
414
415
  /* setup an output prefix callback if necessary */
416
0
  if (diffopt && !diffopt->output_prefix)
417
0
    diffopt->output_prefix = diff_output_prefix_callback;
418
0
}
419
420
static void graph_read_config(struct rev_info *revs)
421
0
{
422
0
  int val;
423
424
0
  if (!column_colors) {
425
0
    char *string;
426
0
    if (repo_config_get_string(revs->repo, "log.graphcolors", &string)) {
427
      /* not configured -- use default */
428
0
      graph_set_column_colors(column_colors_ansi,
429
0
            column_colors_ansi_max);
430
0
    } else {
431
0
      static struct strvec custom_colors = STRVEC_INIT;
432
0
      strvec_clear(&custom_colors);
433
0
      parse_graph_colors_config(&custom_colors, string);
434
0
      free(string);
435
      /* graph_set_column_colors takes a max-index, not a count */
436
0
      graph_set_column_colors(custom_colors.v,
437
0
            custom_colors.nr - 1);
438
0
    }
439
0
  }
440
441
0
  if (!repo_config_get_bool(revs->repo, "log.graphIndent", &val))
442
0
    revs->no_graph_indent = !val;
443
0
}
444
445
struct git_graph *graph_init(struct rev_info *opt)
446
0
{
447
0
  struct git_graph *graph = xmalloc(sizeof(struct git_graph));
448
449
0
  graph_read_config(opt);
450
451
0
  graph->commit = NULL;
452
0
  graph->revs = opt;
453
0
  graph->num_parents = 0;
454
0
  graph->expansion_row = 0;
455
0
  graph->state = GRAPH_PADDING;
456
0
  graph->prev_state = GRAPH_PADDING;
457
0
  graph->commit_index = 0;
458
0
  graph->prev_commit_index = 0;
459
0
  graph->merge_layout = 0;
460
0
  graph->edges_added = 0;
461
0
  graph->prev_edges_added = 0;
462
0
  graph->num_columns = 0;
463
0
  graph->num_new_columns = 0;
464
0
  graph->mapping_size = 0;
465
0
  graph->lookahead[0] = NULL;
466
0
  graph->lookahead[1] = NULL;
467
0
  graph->lookahead_nr = 0;
468
0
  graph->visual_root_depth = 0;
469
0
  graph->visual_root_cascade = 0;
470
  /*
471
   * Start the column color at the maximum value, since we'll
472
   * always increment it for the first commit we output.
473
   * This way we start at 0 for the first commit.
474
   */
475
0
  graph->default_column_color = column_colors_max - 1;
476
477
  /*
478
   * Allocate a reasonably large default number of columns
479
   * We'll automatically grow columns later if we need more room.
480
   */
481
0
  graph->column_capacity = 30;
482
0
  ALLOC_ARRAY(graph->columns, graph->column_capacity);
483
0
  ALLOC_ARRAY(graph->new_columns, graph->column_capacity);
484
0
  ALLOC_ARRAY(graph->mapping, 2 * graph->column_capacity);
485
0
  ALLOC_ARRAY(graph->old_mapping, 2 * graph->column_capacity);
486
487
  /*
488
   * The diff output prefix callback, with this we can make
489
   * all the diff output to align with the graph lines.
490
   */
491
0
  strbuf_init(&graph->prefix_buf, 0);
492
0
  opt->diffopt.output_prefix = diff_output_prefix_callback;
493
0
  opt->diffopt.output_prefix_data = graph;
494
495
0
  return graph;
496
0
}
497
498
void graph_clear(struct git_graph *graph)
499
0
{
500
0
  if (!graph)
501
0
    return;
502
503
0
  free(graph->columns);
504
0
  free(graph->new_columns);
505
0
  free(graph->mapping);
506
0
  free(graph->old_mapping);
507
0
  strbuf_release(&graph->prefix_buf);
508
0
  free(graph);
509
0
}
510
511
static void graph_update_state(struct git_graph *graph, enum graph_state s)
512
0
{
513
0
  graph->prev_state = graph->state;
514
0
  graph->state = s;
515
0
}
516
517
static void graph_ensure_capacity(struct git_graph *graph, int num_columns)
518
0
{
519
0
  if (graph->column_capacity >= num_columns)
520
0
    return;
521
522
0
  do {
523
0
    graph->column_capacity *= 2;
524
0
  } while (graph->column_capacity < num_columns);
525
526
0
  REALLOC_ARRAY(graph->columns, graph->column_capacity);
527
0
  REALLOC_ARRAY(graph->new_columns, graph->column_capacity);
528
0
  REALLOC_ARRAY(graph->mapping, graph->column_capacity * 2);
529
0
  REALLOC_ARRAY(graph->old_mapping, graph->column_capacity * 2);
530
0
}
531
532
/*
533
 * Returns 1 if the commit will be printed in the graph output,
534
 * and 0 otherwise.
535
 */
536
static int graph_is_interesting(struct git_graph *graph, struct commit *commit)
537
0
{
538
  /*
539
   * Commits in the lookahead buffer have been pre-fetched by
540
   * get_revision() and will be shown in the future. They already have
541
   * the SHOWN flag set when they were pre-fetched but the graph still
542
   * needs to treat them as interesting parents.
543
   */
544
0
  for (int i = 0; i < graph->lookahead_nr; i++)
545
0
    if (graph->lookahead[i] == commit)
546
0
      return 1;
547
  /*
548
   * If revs->boundary is set, commits whose children have
549
   * been shown are always interesting, even if they have the
550
   * UNINTERESTING or TREESAME flags set.
551
   */
552
0
  if (graph->revs && graph->revs->boundary) {
553
0
    if (commit->object.flags & CHILD_SHOWN)
554
0
      return 1;
555
0
  }
556
557
  /*
558
   * Otherwise, use get_commit_action() to see if this commit is
559
   * interesting
560
   */
561
0
  return get_commit_action(graph->revs, commit) == commit_show;
562
0
}
563
564
static struct commit_list *next_interesting_parent(struct git_graph *graph,
565
               struct commit_list *orig)
566
0
{
567
0
  struct commit_list *list;
568
569
  /*
570
   * If revs->first_parent_only is set, only the first
571
   * parent is interesting.  None of the others are.
572
   */
573
0
  if (graph->revs->first_parent_only)
574
0
    return NULL;
575
576
  /*
577
   * Return the next interesting commit after orig
578
   */
579
0
  for (list = orig->next; list; list = list->next) {
580
0
    if (graph_is_interesting(graph, list->item))
581
0
      return list;
582
0
  }
583
584
0
  return NULL;
585
0
}
586
587
static struct commit_list *first_interesting_parent(struct git_graph *graph)
588
0
{
589
0
  struct commit_list *parents = graph->commit->parents;
590
591
  /*
592
   * If this commit has no parents, ignore it
593
   */
594
0
  if (!parents)
595
0
    return NULL;
596
597
  /*
598
   * If the first parent is interesting, return it
599
   */
600
0
  if (graph_is_interesting(graph, parents->item))
601
0
    return parents;
602
603
  /*
604
   * Otherwise, call next_interesting_parent() to get
605
   * the next interesting parent
606
   */
607
0
  return next_interesting_parent(graph, parents);
608
0
}
609
610
static unsigned short graph_get_current_column_color(const struct git_graph *graph)
611
0
{
612
0
  if (!want_color(graph->revs->diffopt.use_color))
613
0
    return column_colors_max;
614
0
  return graph->default_column_color;
615
0
}
616
617
/*
618
 * Update the graph's default column color.
619
 */
620
static void graph_increment_column_color(struct git_graph *graph)
621
0
{
622
0
  graph->default_column_color = (graph->default_column_color + 1) %
623
0
    column_colors_max;
624
0
}
625
626
static unsigned short graph_find_commit_color(const struct git_graph *graph,
627
                const struct commit *commit)
628
0
{
629
0
  int i;
630
0
  for (i = 0; i < graph->num_columns; i++) {
631
0
    if (graph->columns[i].commit == commit)
632
0
      return graph->columns[i].color;
633
0
  }
634
0
  return graph_get_current_column_color(graph);
635
0
}
636
637
static int graph_find_new_column_by_commit(struct git_graph *graph,
638
             struct commit *commit)
639
0
{
640
0
  int i;
641
0
  for (i = 0; i < graph->num_new_columns; i++) {
642
0
    if (graph->new_columns[i].commit == commit)
643
0
      return i;
644
0
  }
645
0
  return -1;
646
0
}
647
648
static void graph_insert_into_new_columns(struct git_graph *graph,
649
            struct commit *commit,
650
            int idx)
651
0
{
652
  /*
653
   * Get the initial merge_layout before it's modified to know if this
654
   * is a merge.
655
   */
656
0
  int initial_merge_layout = graph->merge_layout;
657
0
  int i = graph_find_new_column_by_commit(graph, commit);
658
0
  int mapping_idx;
659
660
  /*
661
   * If the commit is not already in the new_columns array, then add it
662
   * and record it as being in the final column.
663
   */
664
0
  if (i < 0) {
665
0
    i = graph->num_new_columns++;
666
0
    graph->new_columns[i].commit = commit;
667
0
    graph->new_columns[i].color = graph_find_commit_color(graph, commit);
668
0
    graph->new_columns[i].is_merge_parent = 0;
669
0
  }
670
671
0
  if (graph->num_parents > 1 && idx > -1 && graph->merge_layout == -1) {
672
    /*
673
     * If this is the first parent of a merge, choose a layout for
674
     * the merge line based on whether the parent appears in a
675
     * column to the left of the merge
676
     */
677
0
    int dist, shift;
678
679
0
    dist = idx - i;
680
0
    shift = (dist > 1) ? 2 * dist - 3 : 1;
681
682
0
    graph->merge_layout = (dist > 0) ? 0 : 1;
683
0
    graph->edges_added = graph->num_parents + graph->merge_layout  - 2;
684
685
0
    mapping_idx = graph->width + (graph->merge_layout - 1) * shift;
686
0
    graph->width += 2 * graph->merge_layout;
687
688
0
  } else if (graph->edges_added > 0 && i == graph->mapping[graph->width - 2]) {
689
    /*
690
     * If some columns have been added by a merge, but this commit
691
     * was found in the last existing column, then adjust the
692
     * numbers so that the two edges immediately join, i.e.:
693
     *
694
     *    * |   * |
695
     *    |\ \  =>  |\|
696
     *    | |/    | *
697
     *    | *
698
     */
699
0
    mapping_idx = graph->width - 2;
700
0
    graph->edges_added = -1;
701
0
  } else {
702
0
    mapping_idx = graph->width;
703
0
    graph->width += 2;
704
0
  }
705
706
0
  graph->mapping[mapping_idx] = i;
707
708
  /*
709
   * Mark non-first parents of a merge.
710
   */
711
0
  if (graph->num_parents > 1 && initial_merge_layout >= 0 && idx > -1)
712
0
    graph->new_columns[i].is_merge_parent = 1;
713
0
}
714
715
static void graph_update_columns(struct git_graph *graph)
716
0
{
717
0
  struct commit_list *parent;
718
0
  int max_new_columns;
719
0
  int i, seen_this, is_commit_in_columns;
720
721
  /*
722
   * Swap graph->columns with graph->new_columns
723
   * graph->columns contains the state for the previous commit,
724
   * and new_columns now contains the state for our commit.
725
   *
726
   * We'll re-use the old columns array as storage to compute the new
727
   * columns list for the commit after this one.
728
   */
729
0
  SWAP(graph->columns, graph->new_columns);
730
0
  graph->num_columns = graph->num_new_columns;
731
0
  graph->num_new_columns = 0;
732
733
  /*
734
   * Now update new_columns and mapping with the information for the
735
   * commit after this one.
736
   *
737
   * First, make sure we have enough room.  At most, there will
738
   * be graph->num_columns + graph->num_parents columns for the next
739
   * commit.
740
   */
741
0
  max_new_columns = graph->num_columns + graph->num_parents;
742
0
  graph_ensure_capacity(graph, max_new_columns);
743
744
  /*
745
   * Clear out graph->mapping
746
   */
747
0
  graph->mapping_size = 2 * max_new_columns;
748
0
  for (i = 0; i < graph->mapping_size; i++)
749
0
    graph->mapping[i] = -1;
750
751
0
  graph->width = 0;
752
0
  graph->prev_edges_added = graph->edges_added;
753
0
  graph->edges_added = 0;
754
755
  /*
756
   * Populate graph->new_columns and graph->mapping
757
   *
758
   * Some of the parents of this commit may already be in
759
   * graph->columns.  If so, graph->new_columns should only contain a
760
   * single entry for each such commit.  graph->mapping should
761
   * contain information about where each current branch line is
762
   * supposed to end up after the collapsing is performed.
763
   */
764
0
  seen_this = 0;
765
0
  is_commit_in_columns = 1;
766
0
  for (i = 0; i <= graph->num_columns; i++) {
767
0
    struct commit *col_commit;
768
0
    if (i == graph->num_columns) {
769
0
      if (seen_this)
770
0
        break;
771
0
      is_commit_in_columns = 0;
772
0
      col_commit = graph->commit;
773
0
    } else {
774
0
      col_commit = graph->columns[i].commit;
775
0
    }
776
777
0
    if (col_commit == graph->commit) {
778
0
      seen_this = 1;
779
0
      graph->commit_index = i;
780
0
      graph->merge_layout = -1;
781
0
      for (parent = first_interesting_parent(graph);
782
0
           parent;
783
0
           parent = next_interesting_parent(graph, parent)) {
784
        /*
785
         * If this is a merge, or the start of a new
786
         * childless column, increment the current
787
         * color.
788
         */
789
0
        if (graph->num_parents > 1 ||
790
0
            !is_commit_in_columns) {
791
0
          graph_increment_column_color(graph);
792
0
        }
793
0
        graph_insert_into_new_columns(graph, parent->item, i);
794
0
      }
795
      /*
796
       * We always need to increment graph->width by at
797
       * least 2, even if it has no interesting parents.
798
       * The current commit always takes up at least 2
799
       * spaces.
800
       */
801
0
      if (graph->num_parents == 0)
802
0
        graph->width += 2;
803
0
    } else {
804
0
      int j;
805
0
      graph_insert_into_new_columns(graph, col_commit, -1);
806
      /*
807
       * This column is not the current commit, but we need to
808
       * propagate the flag until the commit is processed.
809
       */
810
0
      j = graph_find_new_column_by_commit(graph, col_commit);
811
0
      if (j >= 0 && graph->columns[i].is_merge_parent)
812
0
        graph->new_columns[j].is_merge_parent = 1;
813
0
    }
814
0
  }
815
816
0
  graph->commit_in_columns = is_commit_in_columns;
817
818
  /*
819
   * If graph_max_lanes is set, cap the width
820
   */
821
0
  if (graph->revs->graph_max_lanes > 0) {
822
    /*
823
     * width of "| " per lanes plus truncation mark "~ ".
824
     * Allow commits from merges to align to the merged lane.
825
     */
826
0
    int max_width = graph->revs->graph_max_lanes * 2 + 2;
827
0
    if (graph->width > max_width)
828
0
      graph->width = max_width;
829
0
  }
830
831
  /*
832
   * Shrink mapping_size to be the minimum necessary
833
   */
834
0
  while (graph->mapping_size > 1 &&
835
0
         graph->mapping[graph->mapping_size - 1] < 0)
836
0
    graph->mapping_size--;
837
0
}
838
839
static int graph_num_dashed_parents(struct git_graph *graph)
840
0
{
841
0
  return graph->num_parents + graph->merge_layout - 3;
842
0
}
843
844
static int graph_num_expansion_rows(struct git_graph *graph)
845
0
{
846
  /*
847
   * Normally, we need two expansion rows for each dashed parent line from
848
   * an octopus merge:
849
   *
850
   *    | *
851
   *    | |\
852
   *    | | \
853
   *    | |  \
854
   *    | *-. \
855
   *    | |\ \ \
856
   *
857
   * If the merge is skewed to the left, then its parents occupy one less
858
   * column, and we don't need as many expansion rows to route around it;
859
   * in some cases that means we don't need any expansion rows at all:
860
   *
861
   *    | *
862
   *    | |\
863
   *    | * \
864
   *    |/|\ \
865
   */
866
0
  return graph_num_dashed_parents(graph) * 2;
867
0
}
868
869
static int graph_needs_pre_commit_line(struct git_graph *graph)
870
0
{
871
0
  return graph->num_parents >= 3 &&
872
0
         graph->commit_index < (graph->num_columns - 1) &&
873
0
         graph->expansion_row < graph_num_expansion_rows(graph);
874
0
}
875
876
struct commit *graph_pop_lookahead(struct git_graph *graph)
877
0
{
878
0
  struct commit *c;
879
880
0
  if (!graph->lookahead_nr)
881
0
    return NULL;
882
883
0
  c = graph->lookahead[0];
884
0
  if (!c)
885
0
    BUG("lookahead buffer has %d entries but the first one is NULL",
886
0
        graph->lookahead_nr);
887
888
0
  graph->lookahead[0] = graph->lookahead[1];
889
0
  graph->lookahead[1] = NULL;
890
0
  graph->lookahead_nr--;
891
0
  return c;
892
0
}
893
894
int graph_get_lookahead_room(struct git_graph *graph)
895
0
{
896
0
  return (int)ARRAY_SIZE(graph->lookahead) - graph->lookahead_nr;
897
0
}
898
899
void graph_push_lookahead(struct git_graph *graph, struct commit *c)
900
0
{
901
0
  if (!graph_get_lookahead_room(graph))
902
0
    BUG("pushing into lookahead buffer when it is already full");
903
904
0
  graph->lookahead[graph->lookahead_nr++] = c;
905
0
}
906
907
/*
908
 * A commit can be a visual root when:
909
 *
910
 * - It has no parents.
911
 *
912
 * - It has parents but they are all filtered out and
913
 *   commit->parents arrives NULL.
914
 *
915
 * - Its parents are uninteresting.
916
 *
917
 * - It is not a boundary commit. Boundary commits also have no visible
918
 *   parents, but they are not selected as visual roots because they cannot
919
 *   cause the ambiguity of being vertically adjacent because:
920
 *
921
 *   1. A boundary only appears because an included commit is its child.
922
 *      Children are always above, and the renderer draws an edge down to
923
 *      the boundary from that child. Rather than starting a column like a
924
 *      visual root would do, it inherits its child column.
925
 *
926
 *   2. Included commits cannot appear below a boundary. Boundaries are
927
 *      ancestors of the exclusion point; if an included commit were an
928
 *      ancestor of the boundary it would be excluded and not rendered.
929
 *      Boundaries therefore always sink to the bottom.
930
 */
931
static int graph_is_visual_root_candidate(struct commit *c, struct git_graph *graph)
932
0
{
933
0
  struct commit_list *p;
934
935
0
  if (c->object.flags & BOUNDARY)
936
0
    return 0;
937
0
  for (p = c->parents; p; p = p->next)
938
0
    if (graph_is_interesting(graph, p->item))
939
0
      return 0;
940
0
  return 1;
941
0
}
942
943
static int graph_is_visual_root(struct git_graph *graph,
944
        struct graph_lookahead_flags *flags)
945
0
{
946
  /*
947
   * This must be only called for the current commit as graph contains
948
   * the state for the current commit only.
949
   *
950
   * To check if a commit is a visual root, call graph_is_visual_root_candidate()
951
   * but we won't know if it is really a visual root until we get to the
952
   * next commit state.
953
   *
954
   * The current commit is an actual visual root if it is a candidate and
955
   * the commit is not a non-first parent of a merge.
956
   *
957
   *   *
958
   *   |\
959
   *   | *    <- it is a visual root candidate but it shouldn't be indented
960
   *   *         because it is already connected by an edge.
961
   *   ^         if commit_in_columns && is_merge_parent means the commit
962
   *   |         was put by a merge and is connected.
963
   *   |
964
   *   `-------- if !is_next_visible means we're on the last commit, avoid
965
   *             indentation unless the one before is a visual root, then
966
   *             we need to differentiate from the one above.
967
   *
968
   * If next_has_columns means that the next commit has
969
   * already a column, so it will not be rendered below, the
970
   * current commit has to act as the last commit and omit
971
   * indentation.
972
   */
973
0
  return graph_is_visual_root_candidate(graph->commit, graph) &&
974
0
         !(graph->commit_in_columns &&
975
0
     graph->columns[graph->commit_index].is_merge_parent) &&
976
0
         flags->is_next_visible &&
977
0
         (!flags->next_has_column || graph->visual_root_depth > 0);
978
0
}
979
980
/*
981
 * Peeks the next commits via the lookahead buffer and sets the lookahead flags.
982
 */
983
static void graph_peek_next_visible(struct git_graph *graph,
984
            struct graph_lookahead_flags *flags)
985
0
{
986
0
  flags->is_next_visible = 0;
987
0
  flags->is_next_visual_root = 0;
988
0
  flags->next_has_column = 0;
989
990
0
  if (!graph->lookahead_nr)
991
0
    return;
992
993
0
  flags->is_next_visible = 1;
994
0
  flags->next_has_column =
995
0
    graph_find_new_column_by_commit(graph, graph->lookahead[0]) >= 0;
996
997
0
  if (!graph_is_visual_root_candidate(graph->lookahead[0], graph))
998
0
    return;
999
1000
0
  if (graph->lookahead_nr >= 2)
1001
0
    flags->is_next_visual_root = 1;
1002
0
}
1003
1004
static int graph_needs_pre_root_line(struct git_graph *graph)
1005
0
{
1006
0
  return graph->commit_in_columns && graph->is_visual_root &&
1007
0
         graph->num_columns > 0 && !graph->visual_root_cascade &&
1008
0
         !graph->revs->no_graph_indent;
1009
0
}
1010
1011
void graph_update(struct git_graph *graph, struct commit *commit)
1012
0
{
1013
0
  struct commit_list *parent;
1014
0
  struct graph_lookahead_flags flags;
1015
1016
  /*
1017
   * Set the new commit
1018
   */
1019
0
  graph->commit = commit;
1020
1021
  /*
1022
   * Count how many interesting parents this commit has
1023
   */
1024
0
  graph->num_parents = 0;
1025
0
  for (parent = first_interesting_parent(graph);
1026
0
       parent;
1027
0
       parent = next_interesting_parent(graph, parent))
1028
0
  {
1029
0
    graph->num_parents++;
1030
0
  }
1031
1032
  /*
1033
   * Store the old commit_index in prev_commit_index.
1034
   * graph_update_columns() will update graph->commit_index for this
1035
   * commit.
1036
   */
1037
0
  graph->prev_commit_index = graph->commit_index;
1038
1039
  /*
1040
   * Call graph_update_columns() to update
1041
   * columns, new_columns, and mapping.
1042
   */
1043
0
  graph_update_columns(graph);
1044
1045
0
  graph_peek_next_visible(graph, &flags);
1046
1047
0
  graph->is_visual_root = graph_is_visual_root(graph, &flags);
1048
1049
0
  if (graph->is_visual_root) {
1050
    /*
1051
     * If next is a visual root we can omit the indent for the first
1052
     * visual root and start cascading.
1053
     */
1054
0
    if (!graph->visual_root_depth && flags.is_next_visual_root)
1055
0
      graph->visual_root_cascade = 1;
1056
1057
    /*
1058
     * We wrap the cascading at a max of four columns at most, after
1059
     * that we wrap it back to the initial column.
1060
     *
1061
     * This could cause ambiguity in case of the next commit not
1062
     * being a visual root and be at the initial column after the
1063
     * first wrap.
1064
     *
1065
     * In case of being a non-visual-root the next, stop the
1066
     * cascading to get the commit indented.
1067
     */
1068
0
    if (!flags.is_next_visual_root &&
1069
0
        graph->visual_root_depth &&
1070
0
        !(graph->visual_root_depth % 4))
1071
0
      graph->visual_root_cascade = 0;
1072
1073
0
    graph->visual_root_depth++;
1074
0
  } else {
1075
0
    graph->visual_root_depth = 0;
1076
0
    graph->visual_root_cascade = 0;
1077
0
  }
1078
1079
0
  graph->expansion_row = 0;
1080
1081
  /*
1082
   * Update graph->state.
1083
   * Note that we don't call graph_update_state() here, since
1084
   * we don't want to update graph->prev_state.  No line for
1085
   * graph->state was ever printed.
1086
   *
1087
   * If the previous commit didn't get to the GRAPH_PADDING state,
1088
   * it never finished its output.  Goto GRAPH_SKIP, to print out
1089
   * a line to indicate that portion of the graph is missing.
1090
   *
1091
   * If there are 3 or more parents, we may need to print extra rows
1092
   * before the commit, to expand the branch lines around it and make
1093
   * room for it.  We need to do this only if there is a branch row
1094
   * (or more) to the right of this commit.
1095
   *
1096
   * If it is a visual root, we need to print an extra row to
1097
   * connect the indentation.
1098
   *
1099
   * If there are less than 3 parents, we can immediately print the
1100
   * commit line.
1101
   */
1102
0
  if (graph->state != GRAPH_PADDING)
1103
0
    graph->state = GRAPH_SKIP;
1104
0
  else if (graph_needs_pre_root_line(graph))
1105
0
    graph->state = GRAPH_PRE_ROOT;
1106
0
  else if (graph_needs_pre_commit_line(graph))
1107
0
    graph->state = GRAPH_PRE_COMMIT;
1108
0
  else
1109
0
    graph->state = GRAPH_COMMIT;
1110
0
}
1111
1112
static int graph_is_mapping_correct(struct git_graph *graph)
1113
0
{
1114
0
  int i;
1115
1116
  /*
1117
   * The mapping is up to date if each entry is at its target,
1118
   * or is 1 greater than its target.
1119
   * (If it is 1 greater than the target, '/' will be printed, so it
1120
   * will look correct on the next row.)
1121
   */
1122
0
  for (i = 0; i < graph->mapping_size; i++) {
1123
0
    int target = graph->mapping[i];
1124
0
    if (target < 0)
1125
0
      continue;
1126
0
    if (target == (i / 2))
1127
0
      continue;
1128
0
    return 0;
1129
0
  }
1130
1131
0
  return 1;
1132
0
}
1133
1134
static void graph_pad_horizontally(struct git_graph *graph, struct graph_line *line)
1135
0
{
1136
  /*
1137
   * Add additional spaces to the end of the strbuf, so that all
1138
   * lines for a particular commit have the same width.
1139
   *
1140
   * This way, fields printed to the right of the graph will remain
1141
   * aligned for the entire commit.
1142
   */
1143
0
  if (line->width < graph->width)
1144
0
    graph_line_addchars(line, ' ', graph->width - line->width);
1145
0
}
1146
1147
static void graph_output_padding_line(struct git_graph *graph,
1148
              struct graph_line *line)
1149
0
{
1150
0
  int i;
1151
1152
  /*
1153
   * Output a padding row, that leaves all branch lines unchanged
1154
   */
1155
0
  for (i = 0; i < graph->num_new_columns; i++) {
1156
0
    if (graph_needs_truncation(graph, i)) {
1157
0
      graph_line_addstr(line, "~ ");
1158
0
      break;
1159
0
    }
1160
0
    graph_line_write_column(line, &graph->new_columns[i], '|');
1161
0
    graph_line_addch(line, ' ');
1162
0
  }
1163
0
}
1164
1165
1166
int graph_width(struct git_graph *graph)
1167
0
{
1168
0
  return graph->width;
1169
0
}
1170
1171
1172
static void graph_output_skip_line(struct git_graph *graph, struct graph_line *line)
1173
0
{
1174
  /*
1175
   * Output an ellipsis to indicate that a portion
1176
   * of the graph is missing.
1177
   */
1178
0
  graph_line_addstr(line, "...");
1179
1180
0
  if (graph_needs_pre_commit_line(graph))
1181
0
    graph_update_state(graph, GRAPH_PRE_COMMIT);
1182
0
  else
1183
0
    graph_update_state(graph, GRAPH_COMMIT);
1184
0
}
1185
1186
static void graph_output_pre_commit_line(struct git_graph *graph,
1187
           struct graph_line *line)
1188
0
{
1189
0
  int i, seen_this;
1190
1191
  /*
1192
   * This function formats a row that increases the space around a commit
1193
   * with multiple parents, to make room for it.  It should only be
1194
   * called when there are 3 or more parents.
1195
   *
1196
   * We need 2 extra rows for every parent over 2.
1197
   */
1198
0
  assert(graph->num_parents >= 3);
1199
1200
  /*
1201
   * graph->expansion_row tracks the current expansion row we are on.
1202
   * It should be in the range [0, num_expansion_rows - 1]
1203
   */
1204
0
  assert(0 <= graph->expansion_row &&
1205
0
         graph->expansion_row < graph_num_expansion_rows(graph));
1206
1207
  /*
1208
   * Output the row
1209
   */
1210
0
  seen_this = 0;
1211
0
  for (i = 0; i < graph->num_columns; i++) {
1212
0
    struct column *col = &graph->columns[i];
1213
0
    if (col->commit == graph->commit) {
1214
0
      seen_this = 1;
1215
0
      graph_line_write_column(line, col, '|');
1216
0
      graph_line_addchars(line, ' ', graph->expansion_row);
1217
0
    } else if (seen_this && graph_needs_truncation(graph, i)) {
1218
0
      graph_line_addstr(line, "~ ");
1219
0
      break;
1220
0
    } else if (seen_this && (graph->expansion_row == 0)) {
1221
      /*
1222
       * This is the first line of the pre-commit output.
1223
       * If the previous commit was a merge commit and
1224
       * ended in the GRAPH_POST_MERGE state, all branch
1225
       * lines after graph->prev_commit_index were
1226
       * printed as "\" on the previous line.  Continue
1227
       * to print them as "\" on this line.  Otherwise,
1228
       * print the branch lines as "|".
1229
       */
1230
0
      if (graph->prev_state == GRAPH_POST_MERGE &&
1231
0
          graph->prev_commit_index < i)
1232
0
        graph_line_write_column(line, col, '\\');
1233
0
      else
1234
0
        graph_line_write_column(line, col, '|');
1235
0
    } else if (seen_this && (graph->expansion_row > 0)) {
1236
0
      graph_line_write_column(line, col, '\\');
1237
0
    } else {
1238
0
      graph_line_write_column(line, col, '|');
1239
0
    }
1240
0
    graph_line_addch(line, ' ');
1241
0
  }
1242
1243
  /*
1244
   * Increment graph->expansion_row,
1245
   * and move to state GRAPH_COMMIT if necessary
1246
   */
1247
0
  graph->expansion_row++;
1248
0
  if (!graph_needs_pre_commit_line(graph))
1249
0
    graph_update_state(graph, GRAPH_COMMIT);
1250
0
}
1251
1252
static void graph_output_commit_char(struct git_graph *graph, struct graph_line *line)
1253
0
{
1254
  /*
1255
   * For boundary commits, print 'o'
1256
   * (We should only see boundary commits when revs->boundary is set.)
1257
   */
1258
0
  if (graph->commit->object.flags & BOUNDARY) {
1259
0
    assert(graph->revs->boundary);
1260
0
    graph_line_addch(line, 'o');
1261
0
    return;
1262
0
  }
1263
1264
  /*
1265
   * get_revision_mark() handles all other cases without assert()
1266
   */
1267
0
  graph_line_addstr(line, get_revision_mark(graph->revs, graph->commit));
1268
0
}
1269
1270
/*
1271
 * Draw the horizontal dashes of an octopus merge.
1272
 */
1273
static void graph_draw_octopus_merge(struct git_graph *graph, struct graph_line *line)
1274
0
{
1275
  /*
1276
   * The parents of a merge commit can be arbitrarily reordered as they
1277
   * are mapped onto display columns, for example this is a valid merge:
1278
   *
1279
   *  | | *---.
1280
   *  | | |\ \ \
1281
   *  | | |/ / /
1282
   *  | |/| | /
1283
   *  | |_|_|/
1284
   *  |/| | |
1285
   *  3 1 0 2
1286
   *
1287
   * The numbers denote which parent of the merge each visual column
1288
   * corresponds to; we can't assume that the parents will initially
1289
   * display in the order given by new_columns.
1290
   *
1291
   * To find the right color for each dash, we need to consult the
1292
   * mapping array, starting from the column 2 places to the right of the
1293
   * merge commit, and use that to find out which logical column each
1294
   * edge will collapse to.
1295
   *
1296
   * Commits are rendered once all edges have collapsed to their correct
1297
   * logcial column, so commit_index gives us the right visual offset for
1298
   * the merge commit.
1299
   */
1300
1301
0
  int i, j;
1302
0
  struct column *col;
1303
1304
0
  int dashed_parents = graph_num_dashed_parents(graph);
1305
1306
0
  for (i = 0; i < dashed_parents; i++) {
1307
0
    j = graph->mapping[(graph->commit_index + i + 2) * 2];
1308
0
    col = &graph->new_columns[j];
1309
1310
0
    graph_line_write_column(line, col, '-');
1311
1312
    /*
1313
     * Commit is at commit_index, each iteration move one lane to
1314
     * the right from the commit.
1315
     */
1316
0
    if (graph_needs_truncation(graph, graph->commit_index + 1 + i)) {
1317
0
      graph_line_addstr(line, "~ ");
1318
0
      break;
1319
0
    }
1320
1321
0
    graph_line_write_column(line, col, (i == dashed_parents - 1) ? '.' : '-');
1322
0
  }
1323
1324
0
  return;
1325
0
}
1326
1327
static void graph_output_commit_line(struct git_graph *graph, struct graph_line *line)
1328
0
{
1329
0
  int seen_this = 0;
1330
0
  int i;
1331
1332
  /*
1333
   * Output the row containing this commit
1334
   * Iterate up to and including graph->num_columns,
1335
   * since the current commit may not be in any of the existing
1336
   * columns.  (This happens when the current commit doesn't have any
1337
   * children that we have already processed.)
1338
   */
1339
0
  seen_this = 0;
1340
0
  for (i = 0; i <= graph->num_columns; i++) {
1341
0
    struct column *col = &graph->columns[i];
1342
0
    struct commit *col_commit;
1343
0
    if (i == graph->num_columns) {
1344
0
      if (seen_this)
1345
0
        break;
1346
0
      col_commit = graph->commit;
1347
0
    } else {
1348
0
      col_commit = graph->columns[i].commit;
1349
0
    }
1350
1351
0
    if (col_commit == graph->commit) {
1352
0
      seen_this = 1;
1353
0
      if (graph->is_visual_root && !graph->revs->no_graph_indent) {
1354
0
        int depth = graph->visual_root_depth;
1355
        /*
1356
         * Each visual column is 2 characters wide.
1357
         * Omit the indentation for the first visual
1358
         * root in cascade mode.
1359
         *
1360
         * Have a max of 4 columns when cascading, after
1361
         * that wrap it and repeat.
1362
         */
1363
0
        int padding = ((depth - graph->visual_root_cascade) % 4) * 2;
1364
0
        graph_line_addchars(line, ' ', padding);
1365
0
        graph->width += padding;
1366
0
      }
1367
0
      graph_output_commit_char(graph, line);
1368
1369
0
      if (graph_needs_truncation(graph, i)) {
1370
0
        graph_line_addch(line, ' ');
1371
0
        break;
1372
0
      }
1373
1374
0
      if (graph->num_parents > 2)
1375
0
        graph_draw_octopus_merge(graph, line);
1376
0
    } else if (graph_needs_truncation(graph, i)) {
1377
0
      graph_line_addstr(line, "~ ");
1378
0
      seen_this = 1;
1379
0
      break;
1380
0
    } else if (seen_this && (graph->edges_added > 1)) {
1381
0
      graph_line_write_column(line, col, '\\');
1382
0
    } else if (seen_this && (graph->edges_added == 1)) {
1383
      /*
1384
       * This is either a right-skewed 2-way merge
1385
       * commit, or a left-skewed 3-way merge.
1386
       * There is no GRAPH_PRE_COMMIT stage for such
1387
       * merges, so this is the first line of output
1388
       * for this commit.  Check to see what the previous
1389
       * line of output was.
1390
       *
1391
       * If it was GRAPH_POST_MERGE, the branch line
1392
       * coming into this commit may have been '\',
1393
       * and not '|' or '/'.  If so, output the branch
1394
       * line as '\' on this line, instead of '|'.  This
1395
       * makes the output look nicer.
1396
       */
1397
0
      if (graph->prev_state == GRAPH_POST_MERGE &&
1398
0
          graph->prev_edges_added > 0 &&
1399
0
          graph->prev_commit_index < i)
1400
0
        graph_line_write_column(line, col, '\\');
1401
0
      else
1402
0
        graph_line_write_column(line, col, '|');
1403
0
    } else if (graph->prev_state == GRAPH_COLLAPSING &&
1404
0
         graph->old_mapping[2 * i + 1] == i &&
1405
0
         graph->mapping[2 * i] < i) {
1406
0
      graph_line_write_column(line, col, '/');
1407
0
    } else {
1408
0
      graph_line_write_column(line, col, '|');
1409
0
    }
1410
0
    graph_line_addch(line, ' ');
1411
0
  }
1412
1413
  /*
1414
   * Update graph->state
1415
   *
1416
   * If the commit is a merge and the first parent is in a visible lane,
1417
   * then the GRAPH_POST_MERGE is needed to draw the merge lane.
1418
   *
1419
   * If the commit is over the truncation limit, but the first parent is on
1420
   * a visible lane, then we still need the merge lane but truncated.
1421
   *
1422
   * If both commit and first parent are over the truncation limit, then
1423
   * there's no need to draw the merge lane because it would work as a
1424
   * padding lane.
1425
   */
1426
0
  if (graph->num_parents > 1) {
1427
0
    if (!graph_needs_truncation(graph, graph->commit_index)) {
1428
0
      graph_update_state(graph, GRAPH_POST_MERGE);
1429
0
    } else {
1430
0
      struct commit_list *p = first_interesting_parent(graph);
1431
0
      int lane;
1432
1433
      /*
1434
       * graph->num_parents are found using first_interesting_parent
1435
       * and next_interesting_parent so it can't be a scenario
1436
       * where num_parents > 1 and there are no interesting parents
1437
       */
1438
0
      if (!p)
1439
0
        BUG("num_parents > 1 but no interesting parent");
1440
1441
0
      lane = graph_find_new_column_by_commit(graph, p->item);
1442
1443
0
      if (!graph_needs_truncation(graph, lane))
1444
0
        graph_update_state(graph, GRAPH_POST_MERGE);
1445
0
      else if (graph_is_mapping_correct(graph))
1446
0
        graph_update_state(graph, GRAPH_PADDING);
1447
0
      else
1448
0
        graph_update_state(graph, GRAPH_COLLAPSING);
1449
0
    }
1450
0
  } else if (graph_is_mapping_correct(graph)) {
1451
0
    graph_update_state(graph, GRAPH_PADDING);
1452
0
  } else {
1453
0
    graph_update_state(graph, GRAPH_COLLAPSING);
1454
0
  }
1455
0
}
1456
1457
static const char merge_chars[] = {'/', '|', '\\'};
1458
1459
static void graph_output_post_merge_line(struct git_graph *graph, struct graph_line *line)
1460
0
{
1461
0
  int seen_this = 0;
1462
0
  int i, j;
1463
1464
0
  struct commit_list *first_parent = first_interesting_parent(graph);
1465
0
  struct column *parent_col = NULL;
1466
1467
  /*
1468
   * Output the post-merge row
1469
   */
1470
0
  for (i = 0; i <= graph->num_columns; i++) {
1471
0
    struct column *col = &graph->columns[i];
1472
0
    struct commit *col_commit;
1473
0
    if (i == graph->num_columns) {
1474
0
      if (seen_this)
1475
0
        break;
1476
0
      col_commit = graph->commit;
1477
0
    } else {
1478
0
      col_commit = col->commit;
1479
0
    }
1480
1481
0
    if (col_commit == graph->commit) {
1482
      /*
1483
       * Since the current commit is a merge find
1484
       * the columns for the parent commits in
1485
       * new_columns and use those to format the
1486
       * edges.
1487
       */
1488
0
      struct commit_list *parents = first_parent;
1489
0
      int par_column;
1490
0
      int idx = graph->merge_layout;
1491
0
      char c;
1492
0
      int truncated = 0;
1493
0
      seen_this = 1;
1494
1495
0
      for (j = 0; j < graph->num_parents; j++) {
1496
0
        par_column = graph_find_new_column_by_commit(graph, parents->item);
1497
0
        assert(par_column >= 0);
1498
1499
0
        c = merge_chars[idx];
1500
0
        graph_line_write_column(line, &graph->new_columns[par_column], c);
1501
1502
        /*
1503
         * j counts parents, it needs to be halved to be
1504
         * comparable with i. Don't truncate if there are
1505
         * no more lanes to print (end of the lane)
1506
         */
1507
0
        if (graph_needs_truncation(graph, j / 2 + i) &&
1508
0
            j / 2 + i <= graph->num_columns) {
1509
0
          if ((j + i * 2) % 2 != 0)
1510
0
            graph_line_addch(line, ' ');
1511
0
          graph_line_addstr(line, "~ ");
1512
0
          truncated = 1;
1513
0
          break;
1514
0
        }
1515
1516
0
        if (idx == 2) {
1517
          /*
1518
           * Check if the next lane needs truncation
1519
           * to avoid having the padding doubled
1520
           */
1521
0
          if (graph_needs_truncation(graph, (j + 1) / 2 + i) &&
1522
0
              j < graph->num_parents - 1) {
1523
0
            graph_line_addstr(line, "~ ");
1524
0
            truncated = 1;
1525
0
            break;
1526
0
          } else if (graph->edges_added > 0 || j < graph->num_parents - 1)
1527
0
            graph_line_addch(line, ' ');
1528
0
        } else {
1529
0
          idx++;
1530
0
        }
1531
0
        parents = next_interesting_parent(graph, parents);
1532
0
      }
1533
0
      if (truncated)
1534
0
        break;
1535
0
      if (graph->edges_added == 0)
1536
0
        graph_line_addch(line, ' ');
1537
0
    } else if (graph_needs_truncation(graph, i)) {
1538
0
      graph_line_addstr(line, "~ ");
1539
0
      break;
1540
0
    } else if (seen_this) {
1541
0
      if (graph->edges_added > 0)
1542
0
        graph_line_write_column(line, col, '\\');
1543
0
      else
1544
0
        graph_line_write_column(line, col, '|');
1545
      /*
1546
       * If it's between two lanes and next would be truncated,
1547
       * don't add space padding.
1548
       */
1549
0
      if (!graph_needs_truncation(graph, i + 1))
1550
0
        graph_line_addch(line, ' ');
1551
0
    } else {
1552
0
      graph_line_write_column(line, col, '|');
1553
0
      if (graph->merge_layout != 0 || i != graph->commit_index - 1) {
1554
0
        if (parent_col)
1555
0
          graph_line_write_column(
1556
0
            line, parent_col, '_');
1557
0
        else
1558
0
          graph_line_addch(line, ' ');
1559
0
      }
1560
0
    }
1561
1562
0
    if (col_commit == first_parent->item)
1563
0
      parent_col = col;
1564
0
  }
1565
1566
  /*
1567
   * Update graph->state
1568
   */
1569
0
  if (graph_is_mapping_correct(graph))
1570
0
    graph_update_state(graph, GRAPH_PADDING);
1571
0
  else
1572
0
    graph_update_state(graph, GRAPH_COLLAPSING);
1573
0
}
1574
1575
static void graph_output_collapsing_line(struct git_graph *graph, struct graph_line *line)
1576
0
{
1577
0
  int i;
1578
0
  short used_horizontal = 0;
1579
0
  int horizontal_edge = -1;
1580
0
  int horizontal_edge_target = -1;
1581
0
  int truncated = 0;
1582
1583
  /*
1584
   * Swap the mapping and old_mapping arrays
1585
   */
1586
0
  SWAP(graph->mapping, graph->old_mapping);
1587
1588
  /*
1589
   * Clear out the mapping array
1590
   */
1591
0
  for (i = 0; i < graph->mapping_size; i++)
1592
0
    graph->mapping[i] = -1;
1593
1594
0
  for (i = 0; i < graph->mapping_size; i++) {
1595
0
    int target = graph->old_mapping[i];
1596
0
    if (target < 0)
1597
0
      continue;
1598
1599
    /*
1600
     * Since update_columns() always inserts the leftmost
1601
     * column first, each branch's target location should
1602
     * always be either its current location or to the left of
1603
     * its current location.
1604
     *
1605
     * We never have to move branches to the right.  This makes
1606
     * the graph much more legible, since whenever branches
1607
     * cross, only one is moving directions.
1608
     */
1609
0
    assert(target * 2 <= i);
1610
1611
0
    if (target * 2 == i) {
1612
      /*
1613
       * This column is already in the
1614
       * correct place
1615
       */
1616
0
      assert(graph->mapping[i] == -1);
1617
0
      graph->mapping[i] = target;
1618
0
    } else if (graph->mapping[i - 1] < 0) {
1619
      /*
1620
       * Nothing is to the left.
1621
       * Move to the left by one
1622
       */
1623
0
      graph->mapping[i - 1] = target;
1624
      /*
1625
       * If there isn't already an edge moving horizontally
1626
       * select this one.
1627
       */
1628
0
      if (horizontal_edge == -1) {
1629
0
        int j;
1630
0
        horizontal_edge = i;
1631
0
        horizontal_edge_target = target;
1632
        /*
1633
         * The variable target is the index of the graph
1634
         * column, and therefore target*2+3 is the
1635
         * actual screen column of the first horizontal
1636
         * line.
1637
         */
1638
0
        for (j = (target * 2)+3; j < (i - 2); j += 2)
1639
0
          graph->mapping[j] = target;
1640
0
      }
1641
0
    } else if (graph->mapping[i - 1] == target) {
1642
      /*
1643
       * There is a branch line to our left
1644
       * already, and it is our target.  We
1645
       * combine with this line, since we share
1646
       * the same parent commit.
1647
       *
1648
       * We don't have to add anything to the
1649
       * output or mapping, since the
1650
       * existing branch line has already taken
1651
       * care of it.
1652
       */
1653
0
    } else {
1654
      /*
1655
       * There is a branch line to our left,
1656
       * but it isn't our target.  We need to
1657
       * cross over it.
1658
       *
1659
       * The space just to the left of this
1660
       * branch should always be empty.
1661
       */
1662
0
      assert(graph->mapping[i - 1] > target);
1663
0
      assert(graph->mapping[i - 2] < 0);
1664
0
      graph->mapping[i - 2] = target;
1665
      /*
1666
       * Mark this branch as the horizontal edge to
1667
       * prevent any other edges from moving
1668
       * horizontally.
1669
       */
1670
0
      if (horizontal_edge == -1) {
1671
0
        int j;
1672
0
        horizontal_edge_target = target;
1673
0
        horizontal_edge = i - 1;
1674
1675
0
        for (j = (target * 2) + 3; j < (i - 2); j += 2)
1676
0
          graph->mapping[j] = target;
1677
0
      }
1678
0
    }
1679
0
  }
1680
1681
  /*
1682
   * Copy the current mapping array into old_mapping
1683
   */
1684
0
  COPY_ARRAY(graph->old_mapping, graph->mapping, graph->mapping_size);
1685
1686
  /*
1687
   * The new mapping may be 1 smaller than the old mapping
1688
   */
1689
0
  if (graph->mapping[graph->mapping_size - 1] < 0)
1690
0
    graph->mapping_size--;
1691
1692
  /*
1693
   * Output out a line based on the new mapping info
1694
   */
1695
0
  for (i = 0; i < graph->mapping_size; i++) {
1696
0
    int target = graph->mapping[i];
1697
1698
0
    if (!truncated && graph_needs_truncation(graph, i / 2)) {
1699
0
      graph_line_addstr(line, "~ ");
1700
0
      truncated = 1;
1701
0
    }
1702
1703
0
    if (target < 0) {
1704
0
      if (!truncated)
1705
0
        graph_line_addch(line, ' ');
1706
0
    } else if (target * 2 == i) {
1707
0
      if (!truncated)
1708
0
        graph_line_write_column(line, &graph->new_columns[target], '|');
1709
0
    } else if (target == horizontal_edge_target &&
1710
0
         i != horizontal_edge - 1) {
1711
      /*
1712
       * Set the mappings for all but the
1713
       * first segment to -1 so that they
1714
       * won't continue into the next line.
1715
       */
1716
0
      if (i != (target * 2)+3)
1717
0
        graph->mapping[i] = -1;
1718
0
      used_horizontal = 1;
1719
0
      if (!truncated)
1720
0
        graph_line_write_column(line, &graph->new_columns[target], '_');
1721
0
    } else {
1722
0
      if (used_horizontal && i < horizontal_edge)
1723
0
        graph->mapping[i] = -1;
1724
0
      if (!truncated)
1725
0
        graph_line_write_column(line, &graph->new_columns[target], '/');
1726
0
    }
1727
0
  }
1728
1729
  /*
1730
   * If graph->mapping indicates that all of the branch lines
1731
   * are already in the correct positions, we are done.
1732
   * Otherwise, we need to collapse some branch lines together.
1733
   */
1734
0
  if (graph_is_mapping_correct(graph))
1735
0
    graph_update_state(graph, GRAPH_PADDING);
1736
0
}
1737
1738
static void graph_output_pre_root_line(struct git_graph *graph, struct graph_line *line)
1739
0
{
1740
  /*
1741
   * This function adds a row before a visual root, to connect the
1742
   * branch to the indented commit. It must only be called on a
1743
   * visual root.
1744
   */
1745
0
  if (!graph->is_visual_root)
1746
0
    BUG("commit must be a visual root to call pre_root_line");
1747
1748
0
  for (int i = 0; i < graph->num_columns; i++) {
1749
0
    struct column *col = &graph->columns[i];
1750
0
    if (col->commit == graph->commit) {
1751
0
      graph_line_addch(line, ' ');
1752
0
      graph_line_write_column(line, col, '\\');
1753
0
    } else {
1754
0
      graph_line_write_column(line, col, '|');
1755
0
    }
1756
0
    graph_line_addch(line, ' ');
1757
0
  }
1758
1759
0
  graph_update_state(graph, GRAPH_COMMIT);
1760
0
}
1761
1762
int graph_next_line(struct git_graph *graph, struct strbuf *sb)
1763
0
{
1764
0
  int shown_commit_line = 0;
1765
0
  struct graph_line line = { .buf = sb, .width = 0 };
1766
1767
  /*
1768
   * We could conceivable be called with a NULL commit
1769
   * if our caller has a bug, and invokes graph_next_line()
1770
   * immediately after graph_init(), without first calling
1771
   * graph_update().  Return without outputting anything in this
1772
   * case.
1773
   */
1774
0
  if (!graph->commit)
1775
0
    return -1;
1776
1777
0
  switch (graph->state) {
1778
0
  case GRAPH_PADDING:
1779
0
    graph_output_padding_line(graph, &line);
1780
0
    break;
1781
0
  case GRAPH_SKIP:
1782
0
    graph_output_skip_line(graph, &line);
1783
0
    break;
1784
0
  case GRAPH_PRE_COMMIT:
1785
0
    graph_output_pre_commit_line(graph, &line);
1786
0
    break;
1787
0
  case GRAPH_PRE_ROOT:
1788
0
    graph_output_pre_root_line(graph, &line);
1789
0
    break;
1790
0
  case GRAPH_COMMIT:
1791
0
    graph_output_commit_line(graph, &line);
1792
0
    shown_commit_line = 1;
1793
0
    break;
1794
0
  case GRAPH_POST_MERGE:
1795
0
    graph_output_post_merge_line(graph, &line);
1796
0
    break;
1797
0
  case GRAPH_COLLAPSING:
1798
0
    graph_output_collapsing_line(graph, &line);
1799
0
    break;
1800
0
  }
1801
1802
0
  graph_pad_horizontally(graph, &line);
1803
0
  return shown_commit_line;
1804
0
}
1805
1806
static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
1807
0
{
1808
0
  int i;
1809
0
  struct graph_line line = { .buf = sb, .width = 0 };
1810
1811
0
  if (graph->state != GRAPH_COMMIT) {
1812
0
    graph_next_line(graph, sb);
1813
0
    return;
1814
0
  }
1815
1816
  /*
1817
   * Output the row containing this commit
1818
   * Iterate up to and including graph->num_columns,
1819
   * since the current commit may not be in any of the existing
1820
   * columns.  (This happens when the current commit doesn't have any
1821
   * children that we have already processed.)
1822
   */
1823
0
  for (i = 0; i < graph->num_columns; i++) {
1824
0
    struct column *col = &graph->columns[i];
1825
1826
0
    if (graph_needs_truncation(graph, i)) {
1827
0
      graph_line_addstr(&line, "~ ");
1828
0
      break;
1829
0
    }
1830
1831
0
    graph_line_write_column(&line, col, '|');
1832
1833
0
    if (col->commit == graph->commit && graph->num_parents > 2) {
1834
0
      int len = (graph->num_parents - 2) * 2;
1835
0
      graph_line_addchars(&line, ' ', len);
1836
0
    } else {
1837
0
      graph_line_addch(&line, ' ');
1838
0
    }
1839
0
  }
1840
1841
0
  graph_pad_horizontally(graph, &line);
1842
1843
  /*
1844
   * Update graph->prev_state since we have output a padding line
1845
   */
1846
0
  graph->prev_state = GRAPH_PADDING;
1847
0
}
1848
1849
int graph_is_commit_finished(struct git_graph const *graph)
1850
0
{
1851
0
  return (graph->state == GRAPH_PADDING);
1852
0
}
1853
1854
void graph_show_commit(struct git_graph *graph)
1855
0
{
1856
0
  struct strbuf msgbuf = STRBUF_INIT;
1857
0
  int shown_commit_line = 0;
1858
1859
0
  graph_show_line_prefix(default_diffopt);
1860
1861
0
  if (!graph)
1862
0
    return;
1863
1864
  /*
1865
   * When showing a diff of a merge against each of its parents, we
1866
   * are called once for each parent without graph_update having been
1867
   * called.  In this case, simply output a single padding line.
1868
   */
1869
0
  if (graph_is_commit_finished(graph)) {
1870
0
    graph_show_padding(graph);
1871
0
    shown_commit_line = 1;
1872
0
  }
1873
1874
0
  while (!shown_commit_line && !graph_is_commit_finished(graph)) {
1875
0
    shown_commit_line = graph_next_line(graph, &msgbuf);
1876
0
    fwrite(msgbuf.buf, sizeof(char), msgbuf.len,
1877
0
      graph->revs->diffopt.file);
1878
0
    if (!shown_commit_line) {
1879
0
      putc('\n', graph->revs->diffopt.file);
1880
0
      graph_show_line_prefix(&graph->revs->diffopt);
1881
0
    }
1882
0
    strbuf_setlen(&msgbuf, 0);
1883
0
  }
1884
1885
0
  strbuf_release(&msgbuf);
1886
0
}
1887
1888
void graph_show_oneline(struct git_graph *graph)
1889
0
{
1890
0
  struct strbuf msgbuf = STRBUF_INIT;
1891
1892
0
  graph_show_line_prefix(default_diffopt);
1893
1894
0
  if (!graph)
1895
0
    return;
1896
1897
0
  graph_next_line(graph, &msgbuf);
1898
0
  fwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);
1899
0
  strbuf_release(&msgbuf);
1900
0
}
1901
1902
void graph_show_padding(struct git_graph *graph)
1903
0
{
1904
0
  struct strbuf msgbuf = STRBUF_INIT;
1905
1906
0
  graph_show_line_prefix(default_diffopt);
1907
1908
0
  if (!graph)
1909
0
    return;
1910
1911
0
  graph_padding_line(graph, &msgbuf);
1912
0
  fwrite(msgbuf.buf, sizeof(char), msgbuf.len, graph->revs->diffopt.file);
1913
0
  strbuf_release(&msgbuf);
1914
0
}
1915
1916
int graph_show_remainder(struct git_graph *graph)
1917
0
{
1918
0
  struct strbuf msgbuf = STRBUF_INIT;
1919
0
  int shown = 0;
1920
1921
0
  graph_show_line_prefix(default_diffopt);
1922
1923
0
  if (!graph)
1924
0
    return 0;
1925
1926
0
  if (graph_is_commit_finished(graph))
1927
0
    return 0;
1928
1929
0
  for (;;) {
1930
0
    graph_next_line(graph, &msgbuf);
1931
0
    fwrite(msgbuf.buf, sizeof(char), msgbuf.len,
1932
0
      graph->revs->diffopt.file);
1933
0
    strbuf_setlen(&msgbuf, 0);
1934
0
    shown = 1;
1935
1936
0
    if (!graph_is_commit_finished(graph)) {
1937
0
      putc('\n', graph->revs->diffopt.file);
1938
0
      graph_show_line_prefix(&graph->revs->diffopt);
1939
0
    } else {
1940
0
      break;
1941
0
    }
1942
0
  }
1943
0
  strbuf_release(&msgbuf);
1944
1945
0
  return shown;
1946
0
}
1947
1948
static void graph_show_strbuf(struct git_graph *graph,
1949
            FILE *file,
1950
            struct strbuf const *sb)
1951
0
{
1952
0
  char *p;
1953
1954
  /*
1955
   * Print the strbuf line by line,
1956
   * and display the graph info before each line but the first.
1957
   */
1958
0
  p = sb->buf;
1959
0
  while (p) {
1960
0
    size_t len;
1961
0
    char *next_p = strchr(p, '\n');
1962
0
    if (next_p) {
1963
0
      next_p++;
1964
0
      len = next_p - p;
1965
0
    } else {
1966
0
      len = (sb->buf + sb->len) - p;
1967
0
    }
1968
0
    fwrite(p, sizeof(char), len, file);
1969
0
    if (next_p && *next_p != '\0')
1970
0
      graph_show_oneline(graph);
1971
0
    p = next_p;
1972
0
  }
1973
0
}
1974
1975
void graph_show_commit_msg(struct git_graph *graph,
1976
         FILE *file,
1977
         struct strbuf const *sb)
1978
0
{
1979
0
  int newline_terminated;
1980
1981
  /*
1982
   * Show the commit message
1983
   */
1984
0
  graph_show_strbuf(graph, file, sb);
1985
1986
0
  if (!graph)
1987
0
    return;
1988
1989
0
  newline_terminated = (sb->len && sb->buf[sb->len - 1] == '\n');
1990
1991
  /*
1992
   * If there is more output needed for this commit, show it now
1993
   */
1994
0
  if (!graph_is_commit_finished(graph)) {
1995
    /*
1996
     * If sb doesn't have a terminating newline, print one now,
1997
     * so we can start the remainder of the graph output on a
1998
     * new line.
1999
     */
2000
0
    if (!newline_terminated)
2001
0
      putc('\n', file);
2002
2003
0
    graph_show_remainder(graph);
2004
2005
    /*
2006
     * If sb ends with a newline, our output should too.
2007
     */
2008
0
    if (newline_terminated)
2009
0
      putc('\n', file);
2010
0
  }
2011
0
}