graph.c 34.8 KB
Newer Older
Adam Simpkins's avatar
Adam Simpkins committed
1 2
#include "cache.h"
#include "commit.h"
3
#include "color.h"
Adam Simpkins's avatar
Adam Simpkins committed
4 5 6 7
#include "graph.h"
#include "diff.h"
#include "revision.h"

8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/* Internal API */

/*
 * Output a padding line in the graph.
 * This is similar to graph_next_line().  However, it is guaranteed to
 * never print the current commit line.  Instead, if the commit line is
 * next, it will simply output a line of vertical padding, extending the
 * branch lines downwards, but leaving them otherwise unchanged.
 */
static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);

/*
 * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the
 * first will be prefixed with the graph output.
 *
 * If the strbuf ends with a newline, the output will end after this
 * newline.  A new graph line will not be printed after the final newline.
 * If the strbuf is empty, no output will be printed.
 *
27
 * Since the first line will not include the graph output, the caller is
28 29 30 31 32 33
 * responsible for printing this line's graph (perhaps via
 * graph_show_commit() or graph_show_oneline()) before calling
 * graph_show_strbuf().
 */
static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);

Adam Simpkins's avatar
Adam Simpkins committed
34 35 36 37 38 39 40 41 42 43 44 45 46
/*
 * TODO:
 * - Limit the number of columns, similar to the way gitk does.
 *   If we reach more than a specified number of columns, omit
 *   sections of some columns.
 */

struct column {
	/*
	 * The parent commit of this column.
	 */
	struct commit *commit;
	/*
47 48
	 * The color to (optionally) print this column in.  This is an
	 * index into column_colors.
Adam Simpkins's avatar
Adam Simpkins committed
49
	 */
50
	unsigned short color;
Adam Simpkins's avatar
Adam Simpkins committed
51 52 53 54 55 56 57 58 59 60 61
};

enum graph_state {
	GRAPH_PADDING,
	GRAPH_SKIP,
	GRAPH_PRE_COMMIT,
	GRAPH_COMMIT,
	GRAPH_POST_MERGE,
	GRAPH_COLLAPSING
};

62 63 64
static const char **column_colors;
static unsigned short column_colors_max;

65
void graph_set_column_colors(const char **colors, unsigned short colors_max)
66 67 68 69 70 71
{
	column_colors = colors;
	column_colors_max = colors_max;
}

static const char *column_get_color_code(unsigned short color)
72
{
73
	return column_colors[color];
74 75 76 77 78
}

static void strbuf_write_column(struct strbuf *sb, const struct column *c,
				char col_char)
{
79 80
	if (c->color < column_colors_max)
		strbuf_addstr(sb, column_get_color_code(c->color));
81
	strbuf_addch(sb, col_char);
82 83
	if (c->color < column_colors_max)
		strbuf_addstr(sb, column_get_color_code(column_colors_max));
84 85
}

Adam Simpkins's avatar
Adam Simpkins committed
86 87 88 89 90
struct git_graph {
	/*
	 * The commit currently being processed
	 */
	struct commit *commit;
91 92
	/* The rev-info used for the current traversal */
	struct rev_info *revs;
Adam Simpkins's avatar
Adam Simpkins committed
93
	/*
94 95 96 97 98
	 * The number of interesting parents that this commit has.
	 *
	 * Note that this is not the same as the actual number of parents.
	 * This count excludes parents that won't be printed in the graph
	 * output, as determined by graph_is_interesting().
Adam Simpkins's avatar
Adam Simpkins committed
99 100
	 */
	int num_parents;
101 102 103 104 105 106
	/*
	 * The width of the graph output for this commit.
	 * All rows for this commit are padded to this width, so that
	 * messages printed after the graph output are aligned.
	 */
	int width;
Adam Simpkins's avatar
Adam Simpkins committed
107 108 109 110 111 112 113 114 115 116
	/*
	 * The next expansion row to print
	 * when state is GRAPH_PRE_COMMIT
	 */
	int expansion_row;
	/*
	 * The current output state.
	 * This tells us what kind of line graph_next_line() should output.
	 */
	enum graph_state state;
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	/*
	 * The output state for the previous line of output.
	 * This is primarily used to determine how the first merge line
	 * should appear, based on the last line of the previous commit.
	 */
	enum graph_state prev_state;
	/*
	 * The index of the column that refers to this commit.
	 *
	 * If none of the incoming columns refer to this commit,
	 * this will be equal to num_columns.
	 */
	int commit_index;
	/*
	 * The commit_index for the previously displayed commit.
	 *
	 * This is used to determine how the first line of a merge
	 * graph output should appear, based on the last line of the
	 * previous commit.
	 */
	int prev_commit_index;
Adam Simpkins's avatar
Adam Simpkins committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
	/*
	 * The maximum number of columns that can be stored in the columns
	 * and new_columns arrays.  This is also half the number of entries
	 * that can be stored in the mapping and new_mapping arrays.
	 */
	int column_capacity;
	/*
	 * The number of columns (also called "branch lines" in some places)
	 */
	int num_columns;
	/*
	 * The number of columns in the new_columns array
	 */
	int num_new_columns;
	/*
	 * The number of entries in the mapping array
	 */
	int mapping_size;
	/*
	 * The column state before we output the current commit.
	 */
	struct column *columns;
	/*
	 * The new column state after we output the current commit.
	 * Only valid when state is GRAPH_COLLAPSING.
	 */
	struct column *new_columns;
	/*
	 * An array that tracks the current state of each
	 * character in the output line during state GRAPH_COLLAPSING.
	 * Each entry is -1 if this character is empty, or a non-negative
	 * integer if the character contains a branch line.  The value of
	 * the integer indicates the target position for this branch line.
	 * (I.e., this array maps the current column positions to their
	 * desired positions.)
	 *
	 * The maximum capacity of this array is always
	 * sizeof(int) * 2 * column_capacity.
	 */
	int *mapping;
	/*
	 * A temporary array for computing the next mapping state
	 * while we are outputting a mapping line.  This is stored as part
	 * of the git_graph simply so we don't have to allocate a new
	 * temporary array each time we have to output a collapsing line.
	 */
	int *new_mapping;
185 186 187 188 189
	/*
	 * The current default column color being used.  This is
	 * stored as an index into the array column_colors.
	 */
	unsigned short default_column_color;
Adam Simpkins's avatar
Adam Simpkins committed
190 191
};

192 193 194 195 196
static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void *data)
{
	struct git_graph *graph = data;
	static struct strbuf msgbuf = STRBUF_INIT;

197
	assert(opt);
198 199
	assert(graph);

200
	opt->output_prefix_length = graph->width;
201 202 203 204 205
	strbuf_reset(&msgbuf);
	graph_padding_line(graph, &msgbuf);
	return &msgbuf;
}

206
struct git_graph *graph_init(struct rev_info *opt)
Adam Simpkins's avatar
Adam Simpkins committed
207 208
{
	struct git_graph *graph = xmalloc(sizeof(struct git_graph));
209 210 211

	if (!column_colors)
		graph_set_column_colors(column_colors_ansi,
212
					column_colors_ansi_max);
213

Adam Simpkins's avatar
Adam Simpkins committed
214
	graph->commit = NULL;
215
	graph->revs = opt;
Adam Simpkins's avatar
Adam Simpkins committed
216 217 218
	graph->num_parents = 0;
	graph->expansion_row = 0;
	graph->state = GRAPH_PADDING;
219 220 221
	graph->prev_state = GRAPH_PADDING;
	graph->commit_index = 0;
	graph->prev_commit_index = 0;
Adam Simpkins's avatar
Adam Simpkins committed
222 223 224
	graph->num_columns = 0;
	graph->num_new_columns = 0;
	graph->mapping_size = 0;
225 226 227 228 229
	/*
	 * Start the column color at the maximum value, since we'll
	 * always increment it for the first commit we output.
	 * This way we start at 0 for the first commit.
	 */
230
	graph->default_column_color = column_colors_max - 1;
Adam Simpkins's avatar
Adam Simpkins committed
231 232 233 234 235 236 237 238 239 240 241 242 243

	/*
	 * Allocate a reasonably large default number of columns
	 * We'll automatically grow columns later if we need more room.
	 */
	graph->column_capacity = 30;
	graph->columns = xmalloc(sizeof(struct column) *
				 graph->column_capacity);
	graph->new_columns = xmalloc(sizeof(struct column) *
				     graph->column_capacity);
	graph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
	graph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);

244 245 246 247 248 249
	/*
	 * The diff output prefix callback, with this we can make
	 * all the diff output to align with the graph lines.
	 */
	opt->diffopt.output_prefix = diff_output_prefix_callback;
	opt->diffopt.output_prefix_data = graph;
250
	opt->diffopt.output_prefix_length = 0;
251

Adam Simpkins's avatar
Adam Simpkins committed
252 253 254
	return graph;
}

255 256 257 258 259 260
static void graph_update_state(struct git_graph *graph, enum graph_state s)
{
	graph->prev_state = graph->state;
	graph->state = s;
}

Adam Simpkins's avatar
Adam Simpkins committed
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
static void graph_ensure_capacity(struct git_graph *graph, int num_columns)
{
	if (graph->column_capacity >= num_columns)
		return;

	do {
		graph->column_capacity *= 2;
	} while (graph->column_capacity < num_columns);

	graph->columns = xrealloc(graph->columns,
				  sizeof(struct column) *
				  graph->column_capacity);
	graph->new_columns = xrealloc(graph->new_columns,
				      sizeof(struct column) *
				      graph->column_capacity);
	graph->mapping = xrealloc(graph->mapping,
				  sizeof(int) * 2 * graph->column_capacity);
	graph->new_mapping = xrealloc(graph->new_mapping,
				      sizeof(int) * 2 * graph->column_capacity);
}

282 283 284 285
/*
 * Returns 1 if the commit will be printed in the graph output,
 * and 0 otherwise.
 */
286
static int graph_is_interesting(struct git_graph *graph, struct commit *commit)
287
{
288 289 290 291 292 293
	/*
	 * If revs->boundary is set, commits whose children have
	 * been shown are always interesting, even if they have the
	 * UNINTERESTING or TREESAME flags set.
	 */
	if (graph->revs && graph->revs->boundary) {
294
		if (commit->object.flags & CHILD_SHOWN)
295 296 297
			return 1;
	}

298
	/*
299 300
	 * Otherwise, use get_commit_action() to see if this commit is
	 * interesting
301
	 */
302
	return get_commit_action(graph->revs, commit) == commit_show;
303 304
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
static struct commit_list *next_interesting_parent(struct git_graph *graph,
						   struct commit_list *orig)
{
	struct commit_list *list;

	/*
	 * If revs->first_parent_only is set, only the first
	 * parent is interesting.  None of the others are.
	 */
	if (graph->revs->first_parent_only)
		return NULL;

	/*
	 * Return the next interesting commit after orig
	 */
	for (list = orig->next; list; list = list->next) {
		if (graph_is_interesting(graph, list->item))
			return list;
	}

	return NULL;
}

static struct commit_list *first_interesting_parent(struct git_graph *graph)
{
	struct commit_list *parents = graph->commit->parents;

	/*
	 * If this commit has no parents, ignore it
	 */
	if (!parents)
		return NULL;

	/*
	 * If the first parent is interesting, return it
	 */
	if (graph_is_interesting(graph, parents->item))
		return parents;

	/*
	 * Otherwise, call next_interesting_parent() to get
	 * the next interesting parent
	 */
	return next_interesting_parent(graph, parents);
}

351 352
static unsigned short graph_get_current_column_color(const struct git_graph *graph)
{
353
	if (!want_color(graph->revs->diffopt.use_color))
354
		return column_colors_max;
355 356 357 358 359 360 361 362 363
	return graph->default_column_color;
}

/*
 * Update the graph's default column color.
 */
static void graph_increment_column_color(struct git_graph *graph)
{
	graph->default_column_color = (graph->default_column_color + 1) %
364
		column_colors_max;
365 366 367 368 369 370 371 372 373 374 375 376 377
}

static unsigned short graph_find_commit_color(const struct git_graph *graph,
					      const struct commit *commit)
{
	int i;
	for (i = 0; i < graph->num_columns; i++) {
		if (graph->columns[i].commit == commit)
			return graph->columns[i].color;
	}
	return graph_get_current_column_color(graph);
}

Adam Simpkins's avatar
Adam Simpkins committed
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
static void graph_insert_into_new_columns(struct git_graph *graph,
					  struct commit *commit,
					  int *mapping_index)
{
	int i;

	/*
	 * If the commit is already in the new_columns list, we don't need to
	 * add it.  Just update the mapping correctly.
	 */
	for (i = 0; i < graph->num_new_columns; i++) {
		if (graph->new_columns[i].commit == commit) {
			graph->mapping[*mapping_index] = i;
			*mapping_index += 2;
			return;
		}
	}

	/*
	 * This commit isn't already in new_columns.  Add it.
	 */
	graph->new_columns[graph->num_new_columns].commit = commit;
400
	graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit);
Adam Simpkins's avatar
Adam Simpkins committed
401 402 403 404 405
	graph->mapping[*mapping_index] = graph->num_new_columns;
	*mapping_index += 2;
	graph->num_new_columns++;
}

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
static void graph_update_width(struct git_graph *graph,
			       int is_commit_in_existing_columns)
{
	/*
	 * Compute the width needed to display the graph for this commit.
	 * This is the maximum width needed for any row.  All other rows
	 * will be padded to this width.
	 *
	 * Compute the number of columns in the widest row:
	 * Count each existing column (graph->num_columns), and each new
	 * column added by this commit.
	 */
	int max_cols = graph->num_columns + graph->num_parents;

	/*
421 422
	 * Even if the current commit has no parents to be printed, it
	 * still takes up a column for itself.
423 424 425 426 427
	 */
	if (graph->num_parents < 1)
		max_cols++;

	/*
428
	 * We added a column for the current commit as part of
429 430 431 432 433 434 435 436 437 438 439 440
	 * graph->num_parents.  If the current commit was already in
	 * graph->columns, then we have double counted it.
	 */
	if (is_commit_in_existing_columns)
		max_cols--;

	/*
	 * Each column takes up 2 spaces
	 */
	graph->width = max_cols * 2;
}

Adam Simpkins's avatar
Adam Simpkins committed
441 442 443 444 445 446
static void graph_update_columns(struct git_graph *graph)
{
	struct commit_list *parent;
	struct column *tmp_columns;
	int max_new_columns;
	int mapping_idx;
447
	int i, seen_this, is_commit_in_columns;
Adam Simpkins's avatar
Adam Simpkins committed
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492

	/*
	 * Swap graph->columns with graph->new_columns
	 * graph->columns contains the state for the previous commit,
	 * and new_columns now contains the state for our commit.
	 *
	 * We'll re-use the old columns array as storage to compute the new
	 * columns list for the commit after this one.
	 */
	tmp_columns = graph->columns;
	graph->columns = graph->new_columns;
	graph->num_columns = graph->num_new_columns;

	graph->new_columns = tmp_columns;
	graph->num_new_columns = 0;

	/*
	 * Now update new_columns and mapping with the information for the
	 * commit after this one.
	 *
	 * First, make sure we have enough room.  At most, there will
	 * be graph->num_columns + graph->num_parents columns for the next
	 * commit.
	 */
	max_new_columns = graph->num_columns + graph->num_parents;
	graph_ensure_capacity(graph, max_new_columns);

	/*
	 * Clear out graph->mapping
	 */
	graph->mapping_size = 2 * max_new_columns;
	for (i = 0; i < graph->mapping_size; i++)
		graph->mapping[i] = -1;

	/*
	 * Populate graph->new_columns and graph->mapping
	 *
	 * Some of the parents of this commit may already be in
	 * graph->columns.  If so, graph->new_columns should only contain a
	 * single entry for each such commit.  graph->mapping should
	 * contain information about where each current branch line is
	 * supposed to end up after the collapsing is performed.
	 */
	seen_this = 0;
	mapping_idx = 0;
493
	is_commit_in_columns = 1;
Adam Simpkins's avatar
Adam Simpkins committed
494 495 496 497 498
	for (i = 0; i <= graph->num_columns; i++) {
		struct commit *col_commit;
		if (i == graph->num_columns) {
			if (seen_this)
				break;
499
			is_commit_in_columns = 0;
Adam Simpkins's avatar
Adam Simpkins committed
500 501 502 503 504 505
			col_commit = graph->commit;
		} else {
			col_commit = graph->columns[i].commit;
		}

		if (col_commit == graph->commit) {
506
			int old_mapping_idx = mapping_idx;
Adam Simpkins's avatar
Adam Simpkins committed
507
			seen_this = 1;
508
			graph->commit_index = i;
509
			for (parent = first_interesting_parent(graph);
Adam Simpkins's avatar
Adam Simpkins committed
510
			     parent;
511
			     parent = next_interesting_parent(graph, parent)) {
512
				/*
513 514
				 * If this is a merge, or the start of a new
				 * childless column, increment the current
515 516
				 * color.
				 */
517 518
				if (graph->num_parents > 1 ||
				    !is_commit_in_columns) {
519
					graph_increment_column_color(graph);
520
				}
Adam Simpkins's avatar
Adam Simpkins committed
521 522 523 524
				graph_insert_into_new_columns(graph,
							      parent->item,
							      &mapping_idx);
			}
525 526 527 528 529 530 531 532
			/*
			 * We always need to increment mapping_idx by at
			 * least 2, even if it has no interesting parents.
			 * The current commit always takes up at least 2
			 * spaces.
			 */
			if (mapping_idx == old_mapping_idx)
				mapping_idx += 2;
Adam Simpkins's avatar
Adam Simpkins committed
533 534 535 536 537 538 539 540 541 542 543 544
		} else {
			graph_insert_into_new_columns(graph, col_commit,
						      &mapping_idx);
		}
	}

	/*
	 * Shrink mapping_size to be the minimum necessary
	 */
	while (graph->mapping_size > 1 &&
	       graph->mapping[graph->mapping_size - 1] < 0)
		graph->mapping_size--;
545 546 547 548 549

	/*
	 * Compute graph->width for this commit
	 */
	graph_update_width(graph, is_commit_in_columns);
Adam Simpkins's avatar
Adam Simpkins committed
550 551 552 553 554 555 556 557 558 559 560 561
}

void graph_update(struct git_graph *graph, struct commit *commit)
{
	struct commit_list *parent;

	/*
	 * Set the new commit
	 */
	graph->commit = commit;

	/*
562
	 * Count how many interesting parents this commit has
Adam Simpkins's avatar
Adam Simpkins committed
563 564
	 */
	graph->num_parents = 0;
565 566 567 568 569
	for (parent = first_interesting_parent(graph);
	     parent;
	     parent = next_interesting_parent(graph, parent))
	{
		graph->num_parents++;
570
	}
Adam Simpkins's avatar
Adam Simpkins committed
571

572 573 574 575 576 577 578
	/*
	 * Store the old commit_index in prev_commit_index.
	 * graph_update_columns() will update graph->commit_index for this
	 * commit.
	 */
	graph->prev_commit_index = graph->commit_index;

Adam Simpkins's avatar
Adam Simpkins committed
579 580 581 582 583 584 585 586 587 588
	/*
	 * Call graph_update_columns() to update
	 * columns, new_columns, and mapping.
	 */
	graph_update_columns(graph);

	graph->expansion_row = 0;

	/*
	 * Update graph->state.
589 590 591
	 * Note that we don't call graph_update_state() here, since
	 * we don't want to update graph->prev_state.  No line for
	 * graph->state was ever printed.
Adam Simpkins's avatar
Adam Simpkins committed
592 593 594 595 596
	 *
	 * If the previous commit didn't get to the GRAPH_PADDING state,
	 * it never finished its output.  Goto GRAPH_SKIP, to print out
	 * a line to indicate that portion of the graph is missing.
	 *
597 598 599 600
	 * If there are 3 or more parents, we may need to print extra rows
	 * before the commit, to expand the branch lines around it and make
	 * room for it.  We need to do this only if there is a branch row
	 * (or more) to the right of this commit.
Adam Simpkins's avatar
Adam Simpkins committed
601 602 603 604 605 606
	 *
	 * If there are less than 3 parents, we can immediately print the
	 * commit line.
	 */
	if (graph->state != GRAPH_PADDING)
		graph->state = GRAPH_SKIP;
607 608
	else if (graph->num_parents >= 3 &&
		 graph->commit_index < (graph->num_columns - 1))
Adam Simpkins's avatar
Adam Simpkins committed
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
		graph->state = GRAPH_PRE_COMMIT;
	else
		graph->state = GRAPH_COMMIT;
}

static int graph_is_mapping_correct(struct git_graph *graph)
{
	int i;

	/*
	 * The mapping is up to date if each entry is at its target,
	 * or is 1 greater than its target.
	 * (If it is 1 greater than the target, '/' will be printed, so it
	 * will look correct on the next row.)
	 */
	for (i = 0; i < graph->mapping_size; i++) {
		int target = graph->mapping[i];
		if (target < 0)
			continue;
		if (target == (i / 2))
			continue;
		return 0;
	}

	return 1;
}

636 637
static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb,
				   int chars_written)
Adam Simpkins's avatar
Adam Simpkins committed
638 639 640 641 642 643 644 645
{
	/*
	 * Add additional spaces to the end of the strbuf, so that all
	 * lines for a particular commit have the same width.
	 *
	 * This way, fields printed to the right of the graph will remain
	 * aligned for the entire commit.
	 */
646
	int extra;
647
	if (chars_written >= graph->width)
Adam Simpkins's avatar
Adam Simpkins committed
648 649
		return;

650
	extra = graph->width - chars_written;
Adam Simpkins's avatar
Adam Simpkins committed
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
	strbuf_addf(sb, "%*s", (int) extra, "");
}

static void graph_output_padding_line(struct git_graph *graph,
				      struct strbuf *sb)
{
	int i;

	/*
	 * We could conceivable be called with a NULL commit
	 * if our caller has a bug, and invokes graph_next_line()
	 * immediately after graph_init(), without first calling
	 * graph_update().  Return without outputting anything in this
	 * case.
	 */
	if (!graph->commit)
		return;

	/*
	 * Output a padding row, that leaves all branch lines unchanged
	 */
	for (i = 0; i < graph->num_new_columns; i++) {
673 674
		strbuf_write_column(sb, &graph->new_columns[i], '|');
		strbuf_addch(sb, ' ');
Adam Simpkins's avatar
Adam Simpkins committed
675 676
	}

677
	graph_pad_horizontally(graph, sb, graph->num_new_columns * 2);
Adam Simpkins's avatar
Adam Simpkins committed
678 679 680 681 682 683 684 685 686
}

static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
{
	/*
	 * Output an ellipsis to indicate that a portion
	 * of the graph is missing.
	 */
	strbuf_addstr(sb, "...");
687
	graph_pad_horizontally(graph, sb, 3);
Adam Simpkins's avatar
Adam Simpkins committed
688

689 690
	if (graph->num_parents >= 3 &&
	    graph->commit_index < (graph->num_columns - 1))
691
		graph_update_state(graph, GRAPH_PRE_COMMIT);
Adam Simpkins's avatar
Adam Simpkins committed
692
	else
693
		graph_update_state(graph, GRAPH_COMMIT);
Adam Simpkins's avatar
Adam Simpkins committed
694 695 696 697 698 699 700
}

static void graph_output_pre_commit_line(struct git_graph *graph,
					 struct strbuf *sb)
{
	int num_expansion_rows;
	int i, seen_this;
701
	int chars_written;
Adam Simpkins's avatar
Adam Simpkins committed
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723

	/*
	 * This function formats a row that increases the space around a commit
	 * with multiple parents, to make room for it.  It should only be
	 * called when there are 3 or more parents.
	 *
	 * We need 2 extra rows for every parent over 2.
	 */
	assert(graph->num_parents >= 3);
	num_expansion_rows = (graph->num_parents - 2) * 2;

	/*
	 * graph->expansion_row tracks the current expansion row we are on.
	 * It should be in the range [0, num_expansion_rows - 1]
	 */
	assert(0 <= graph->expansion_row &&
	       graph->expansion_row < num_expansion_rows);

	/*
	 * Output the row
	 */
	seen_this = 0;
724
	chars_written = 0;
Adam Simpkins's avatar
Adam Simpkins committed
725 726 727 728
	for (i = 0; i < graph->num_columns; i++) {
		struct column *col = &graph->columns[i];
		if (col->commit == graph->commit) {
			seen_this = 1;
729
			strbuf_write_column(sb, col, '|');
730 731
			strbuf_addf(sb, "%*s", graph->expansion_row, "");
			chars_written += 1 + graph->expansion_row;
732 733 734 735 736 737 738 739 740 741 742 743
		} else if (seen_this && (graph->expansion_row == 0)) {
			/*
			 * This is the first line of the pre-commit output.
			 * If the previous commit was a merge commit and
			 * ended in the GRAPH_POST_MERGE state, all branch
			 * lines after graph->prev_commit_index were
			 * printed as "\" on the previous line.  Continue
			 * to print them as "\" on this line.  Otherwise,
			 * print the branch lines as "|".
			 */
			if (graph->prev_state == GRAPH_POST_MERGE &&
			    graph->prev_commit_index < i)
744
				strbuf_write_column(sb, col, '\\');
745
			else
746 747
				strbuf_write_column(sb, col, '|');
			chars_written++;
748
		} else if (seen_this && (graph->expansion_row > 0)) {
749 750
			strbuf_write_column(sb, col, '\\');
			chars_written++;
Adam Simpkins's avatar
Adam Simpkins committed
751
		} else {
752 753
			strbuf_write_column(sb, col, '|');
			chars_written++;
Adam Simpkins's avatar
Adam Simpkins committed
754
		}
755 756
		strbuf_addch(sb, ' ');
		chars_written++;
Adam Simpkins's avatar
Adam Simpkins committed
757 758
	}

759
	graph_pad_horizontally(graph, sb, chars_written);
Adam Simpkins's avatar
Adam Simpkins committed
760 761 762 763 764 765 766

	/*
	 * Increment graph->expansion_row,
	 * and move to state GRAPH_COMMIT if necessary
	 */
	graph->expansion_row++;
	if (graph->expansion_row >= num_expansion_rows)
767
		graph_update_state(graph, GRAPH_COMMIT);
Adam Simpkins's avatar
Adam Simpkins committed
768 769
}

770 771 772 773 774 775 776 777 778 779 780 781 782
static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
{
	/*
	 * For boundary commits, print 'o'
	 * (We should only see boundary commits when revs->boundary is set.)
	 */
	if (graph->commit->object.flags & BOUNDARY) {
		assert(graph->revs->boundary);
		strbuf_addch(sb, 'o');
		return;
	}

	/*
783
	 * get_revision_mark() handles all other cases without assert()
784
	 */
785
	strbuf_addstr(sb, get_revision_mark(graph->revs, graph->commit));
786 787
}

788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
/*
 * Draw an octopus merge and return the number of characters written.
 */
static int graph_draw_octopus_merge(struct git_graph *graph,
				    struct strbuf *sb)
{
	/*
	 * Here dashless_commits represents the number of parents
	 * which don't need to have dashes (because their edges fit
	 * neatly under the commit).
	 */
	const int dashless_commits = 2;
	int col_num, i;
	int num_dashes =
		((graph->num_parents - dashless_commits) * 2) - 1;
	for (i = 0; i < num_dashes; i++) {
804
		col_num = (i / 2) + dashless_commits + graph->commit_index;
805 806
		strbuf_write_column(sb, &graph->new_columns[col_num], '-');
	}
807
	col_num = (i / 2) + dashless_commits + graph->commit_index;
808 809 810 811
	strbuf_write_column(sb, &graph->new_columns[col_num], '.');
	return num_dashes + 1;
}

812
static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
Adam Simpkins's avatar
Adam Simpkins committed
813 814
{
	int seen_this = 0;
815
	int i, chars_written;
Adam Simpkins's avatar
Adam Simpkins committed
816 817 818 819 820 821 822 823 824

	/*
	 * Output the row containing this commit
	 * Iterate up to and including graph->num_columns,
	 * since the current commit may not be in any of the existing
	 * columns.  (This happens when the current commit doesn't have any
	 * children that we have already processed.)
	 */
	seen_this = 0;
825
	chars_written = 0;
Adam Simpkins's avatar
Adam Simpkins committed
826
	for (i = 0; i <= graph->num_columns; i++) {
827
		struct column *col = &graph->columns[i];
Adam Simpkins's avatar
Adam Simpkins committed
828 829 830 831 832 833 834 835 836 837 838
		struct commit *col_commit;
		if (i == graph->num_columns) {
			if (seen_this)
				break;
			col_commit = graph->commit;
		} else {
			col_commit = graph->columns[i].commit;
		}

		if (col_commit == graph->commit) {
			seen_this = 1;
839
			graph_output_commit_char(graph, sb);
840
			chars_written++;
Adam Simpkins's avatar
Adam Simpkins committed
841

842
			if (graph->num_parents > 2)
843 844
				chars_written += graph_draw_octopus_merge(graph,
									  sb);
845
		} else if (seen_this && (graph->num_parents > 2)) {
846 847
			strbuf_write_column(sb, col, '\\');
			chars_written++;
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
		} else if (seen_this && (graph->num_parents == 2)) {
			/*
			 * This is a 2-way merge commit.
			 * There is no GRAPH_PRE_COMMIT stage for 2-way
			 * merges, so this is the first line of output
			 * for this commit.  Check to see what the previous
			 * line of output was.
			 *
			 * If it was GRAPH_POST_MERGE, the branch line
			 * coming into this commit may have been '\',
			 * and not '|' or '/'.  If so, output the branch
			 * line as '\' on this line, instead of '|'.  This
			 * makes the output look nicer.
			 */
			if (graph->prev_state == GRAPH_POST_MERGE &&
			    graph->prev_commit_index < i)
864
				strbuf_write_column(sb, col, '\\');
865
			else
866 867
				strbuf_write_column(sb, col, '|');
			chars_written++;
Adam Simpkins's avatar
Adam Simpkins committed
868
		} else {
869 870
			strbuf_write_column(sb, col, '|');
			chars_written++;
Adam Simpkins's avatar
Adam Simpkins committed
871
		}
872 873
		strbuf_addch(sb, ' ');
		chars_written++;
Adam Simpkins's avatar
Adam Simpkins committed
874 875
	}

876
	graph_pad_horizontally(graph, sb, chars_written);
Adam Simpkins's avatar
Adam Simpkins committed
877 878 879 880 881

	/*
	 * Update graph->state
	 */
	if (graph->num_parents > 1)
882
		graph_update_state(graph, GRAPH_POST_MERGE);
Adam Simpkins's avatar
Adam Simpkins committed
883
	else if (graph_is_mapping_correct(graph))
884
		graph_update_state(graph, GRAPH_PADDING);
Adam Simpkins's avatar
Adam Simpkins committed
885
	else
886
		graph_update_state(graph, GRAPH_COLLAPSING);
Adam Simpkins's avatar
Adam Simpkins committed
887 888
}

889 890 891 892 893 894 895 896
static struct column *find_new_column_by_commit(struct git_graph *graph,
						struct commit *commit)
{
	int i;
	for (i = 0; i < graph->num_new_columns; i++) {
		if (graph->new_columns[i].commit == commit)
			return &graph->new_columns[i];
	}
897
	return NULL;
898 899
}

900
static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
Adam Simpkins's avatar
Adam Simpkins committed
901 902
{
	int seen_this = 0;
903
	int i, j, chars_written;
Adam Simpkins's avatar
Adam Simpkins committed
904 905 906 907

	/*
	 * Output the post-merge row
	 */
908
	chars_written = 0;
Adam Simpkins's avatar
Adam Simpkins committed
909
	for (i = 0; i <= graph->num_columns; i++) {
910
		struct column *col = &graph->columns[i];
Adam Simpkins's avatar
Adam Simpkins committed
911 912 913 914 915 916
		struct commit *col_commit;
		if (i == graph->num_columns) {
			if (seen_this)
				break;
			col_commit = graph->commit;
		} else {
917
			col_commit = col->commit;
Adam Simpkins's avatar
Adam Simpkins committed
918 919 920
		}

		if (col_commit == graph->commit) {
921 922 923 924 925 926 927 928
			/*
			 * Since the current commit is a merge find
			 * the columns for the parent commits in
			 * new_columns and use those to format the
			 * edges.
			 */
			struct commit_list *parents = NULL;
			struct column *par_column;
Adam Simpkins's avatar
Adam Simpkins committed
929
			seen_this = 1;
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
			parents = first_interesting_parent(graph);
			assert(parents);
			par_column = find_new_column_by_commit(graph, parents->item);
			assert(par_column);

			strbuf_write_column(sb, par_column, '|');
			chars_written++;
			for (j = 0; j < graph->num_parents - 1; j++) {
				parents = next_interesting_parent(graph, parents);
				assert(parents);
				par_column = find_new_column_by_commit(graph, parents->item);
				assert(par_column);
				strbuf_write_column(sb, par_column, '\\');
				strbuf_addch(sb, ' ');
			}
			chars_written += j * 2;
946
		} else if (seen_this) {
947 948 949
			strbuf_write_column(sb, col, '\\');
			strbuf_addch(sb, ' ');
			chars_written += 2;
Adam Simpkins's avatar
Adam Simpkins committed
950
		} else {
951 952 953
			strbuf_write_column(sb, col, '|');
			strbuf_addch(sb, ' ');
			chars_written += 2;
Adam Simpkins's avatar
Adam Simpkins committed
954 955 956
		}
	}

957
	graph_pad_horizontally(graph, sb, chars_written);
Adam Simpkins's avatar
Adam Simpkins committed
958 959 960 961 962

	/*
	 * Update graph->state
	 */
	if (graph_is_mapping_correct(graph))
963
		graph_update_state(graph, GRAPH_PADDING);
Adam Simpkins's avatar
Adam Simpkins committed
964
	else
965
		graph_update_state(graph, GRAPH_COLLAPSING);
Adam Simpkins's avatar
Adam Simpkins committed
966 967
}

968
static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
Adam Simpkins's avatar
Adam Simpkins committed
969 970 971
{
	int i;
	int *tmp_mapping;
972 973 974
	short used_horizontal = 0;
	int horizontal_edge = -1;
	int horizontal_edge_target = -1;
Adam Simpkins's avatar
Adam Simpkins committed
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

	/*
	 * Clear out the new_mapping array
	 */
	for (i = 0; i < graph->mapping_size; i++)
		graph->new_mapping[i] = -1;

	for (i = 0; i < graph->mapping_size; i++) {
		int target = graph->mapping[i];
		if (target < 0)
			continue;

		/*
		 * Since update_columns() always inserts the leftmost
		 * column first, each branch's target location should
		 * always be either its current location or to the left of
		 * its current location.
		 *
		 * We never have to move branches to the right.  This makes
		 * the graph much more legible, since whenever branches
		 * cross, only one is moving directions.
		 */
		assert(target * 2 <= i);

		if (target * 2 == i) {
			/*
			 * This column is already in the
			 * correct place
			 */
			assert(graph->new_mapping[i] == -1);
			graph->new_mapping[i] = target;
		} else if (graph->new_mapping[i - 1] < 0) {
			/*
			 * Nothing is to the left.
			 * Move to the left by one
			 */
			graph->new_mapping[i - 1] = target;
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
			/*
			 * If there isn't already an edge moving horizontally
			 * select this one.
			 */
			if (horizontal_edge == -1) {
				int j;
				horizontal_edge = i;
				horizontal_edge_target = target;
				/*
				 * The variable target is the index of the graph
				 * column, and therefore target*2+3 is the
				 * actual screen column of the first horizontal
				 * line.
				 */
				for (j = (target * 2)+3; j < (i - 2); j += 2)
					graph->new_mapping[j] = target;
			}
Adam Simpkins's avatar
Adam Simpkins committed
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
		} else if (graph->new_mapping[i - 1] == target) {
			/*
			 * There is a branch line to our left
			 * already, and it is our target.  We
			 * combine with this line, since we share
			 * the same parent commit.
			 *
			 * We don't have to add anything to the
			 * output or new_mapping, since the
			 * existing branch line has already taken
			 * care of it.
			 */
		} else {
			/*
			 * There is a branch line to our left,
			 * but it isn't our target.  We need to
			 * cross over it.
			 *
			 * The space just to the left of this
			 * branch should always be empty.
1049 1050 1051
			 *
			 * The branch to the left of that space
			 * should be our eventual target.
Adam Simpkins's avatar
Adam Simpkins committed
1052 1053 1054
			 */
			assert(graph->new_mapping[i - 1] > target);
			assert(graph->new_mapping[i - 2] < 0);
1055
			assert(graph->new_mapping[i - 3] == target);
Adam Simpkins's avatar
Adam Simpkins committed
1056
			graph->new_mapping[i - 2] = target;
1057 1058 1059 1060 1061 1062 1063
			/*
			 * Mark this branch as the horizontal edge to
			 * prevent any other edges from moving
			 * horizontally.
			 */
			if (horizontal_edge == -1)
				horizontal_edge = i;
Adam Simpkins's avatar
Adam Simpkins committed
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
		}
	}

	/*
	 * The new mapping may be 1 smaller than the old mapping
	 */
	if (graph->new_mapping[graph->mapping_size - 1] < 0)
		graph->mapping_size--;

	/*
	 * Output out a line based on the new mapping info
	 */
	for (i = 0; i < graph->mapping_size; i++) {
		int target = graph->new_mapping[i];
		if (target < 0)
			strbuf_addch(sb, ' ');
		else if (target * 2 == i)
1081
			strbuf_write_column(sb, &graph->new_columns[target], '|');
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
		else if (target == horizontal_edge_target &&
			 i != horizontal_edge - 1) {
				/*
				 * Set the mappings for all but the
				 * first segment to -1 so that they
				 * won't continue into the next line.
				 */
				if (i != (target * 2)+3)
					graph->new_mapping[i] = -1;
				used_horizontal = 1;
			strbuf_write_column(sb, &graph->new_columns[target], '_');
		} else {
			if (used_horizontal && i < horizontal_edge)
				graph->new_mapping[i] = -1;
1096
			strbuf_write_column(sb, &graph->new_columns[target], '/');
1097 1098

		}
Adam Simpkins's avatar
Adam Simpkins committed
1099 1100
	}

1101
	graph_pad_horizontally(graph, sb, graph->mapping_size);
Adam Simpkins's avatar
Adam Simpkins committed
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115

	/*
	 * Swap mapping and new_mapping
	 */
	tmp_mapping = graph->mapping;
	graph->mapping = graph->new_mapping;
	graph->new_mapping = tmp_mapping;

	/*
	 * If graph->mapping indicates that all of the branch lines
	 * are already in the correct positions, we are done.
	 * Otherwise, we need to collapse some branch lines together.
	 */
	if (graph_is_mapping_correct(graph))
1116
		graph_update_state(graph, GRAPH_PADDING);
Adam Simpkins's avatar
Adam Simpkins committed
1117 1118
}

1119
int graph_next_line(struct git_graph *graph, struct strbuf *sb)
Adam Simpkins's avatar
Adam Simpkins committed
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
{
	switch (graph->state) {
	case GRAPH_PADDING:
		graph_output_padding_line(graph, sb);
		return 0;
	case GRAPH_SKIP:
		graph_output_skip_line(graph, sb);
		return 0;
	case GRAPH_PRE_COMMIT:
		graph_output_pre_commit_line(graph, sb);
		return 0;
	case GRAPH_COMMIT:
		graph_output_commit_line(graph, sb);
		return 1;
	case GRAPH_POST_MERGE:
		graph_output_post_merge_line(graph, sb);
		return 0;
	case GRAPH_COLLAPSING:
		graph_output_collapsing_line(graph, sb);
		return 0;
	}

	assert(0);
	return 0;
}

1146
static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
Adam Simpkins's avatar
Adam Simpkins committed
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
{
	int i, j;

	if (graph->state != GRAPH_COMMIT) {
		graph_next_line(graph, sb);
		return;
	}

	/*
	 * Output the row containing this commit
	 * Iterate up to and including graph->num_columns,
	 * since the current commit may not be in any of the existing
	 * columns.  (This happens when the current commit doesn't have any
	 * children that we have already processed.)
	 */
	for (i = 0; i < graph->num_columns; i++) {
1163 1164
		struct column *col = &graph->columns[i];
		struct commit *col_commit = col->commit;
Adam Simpkins's avatar
Adam Simpkins committed
1165
		if (col_commit == graph->commit) {
1166
			strbuf_write_column(sb, col, '|');
Adam Simpkins's avatar
Adam Simpkins committed
1167 1168 1169 1170 1171 1172 1173 1174 1175

			if (graph->num_parents < 3)
				strbuf_addch(sb, ' ');
			else {
				int num_spaces = ((graph->num_parents - 2) * 2);
				for (j = 0; j < num_spaces; j++)
					strbuf_addch(sb, ' ');
			}
		} else {
1176 1177
			strbuf_write_column(sb, col, '|');
			strbuf_addch(sb, ' ');
Adam Simpkins's avatar
Adam Simpkins committed
1178 1179 1180
		}
	}

1181
	graph_pad_horizontally(graph, sb, graph->num_columns);
1182 1183 1184 1185 1186

	/*
	 * Update graph->prev_state since we have output a padding line
	 */
	graph->prev_state = GRAPH_PADDING;
Adam Simpkins's avatar
Adam Simpkins committed
1187 1188 1189 1190 1191 1192 1193 1194 1195
}

int graph_is_commit_finished(struct git_graph const *graph)
{
	return (graph->state == GRAPH_PADDING);
}

void graph_show_commit(struct git_graph *graph)
{
1196
	struct strbuf msgbuf = STRBUF_INIT;
Adam Simpkins's avatar
Adam Simpkins committed
1197 1198 1199 1200 1201
	int shown_commit_line = 0;

	if (!graph)
		return;

1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
	/*
	 * When showing a diff of a merge against each of its parents, we
	 * are called once for each parent without graph_update having been
	 * called.  In this case, simply output a single padding line.
	 */
	if (graph_is_commit_finished(graph)) {
		graph_show_padding(graph);
		shown_commit_line = 1;
	}

1212
	while (!shown_commit_line && !graph_is_commit_finished(graph)) {
Adam Simpkins's avatar
Adam Simpkins committed
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224
		shown_commit_line = graph_next_line(graph, &msgbuf);
		fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
		if (!shown_commit_line)
			putchar('\n');
		strbuf_setlen(&msgbuf, 0);
	}

	strbuf_release(&msgbuf);
}

void graph_show_oneline(struct git_graph *graph)
{
1225
	struct strbuf msgbuf = STRBUF_INIT;
Adam Simpkins's avatar
Adam Simpkins committed
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236

	if (!graph)
		return;

	graph_next_line(graph, &msgbuf);
	fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
	strbuf_release(&msgbuf);
}

void graph_show_padding(struct git_graph *graph)
{
1237
	struct strbuf msgbuf = STRBUF_INIT;
Adam Simpkins's avatar
Adam Simpkins committed
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248

	if (!graph)
		return;

	graph_padding_line(graph, &msgbuf);
	fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
	strbuf_release(&msgbuf);
}

int graph_show_remainder(struct git_graph *graph)
{
1249
	struct strbuf msgbuf = STRBUF_INIT;
Adam Simpkins's avatar
Adam Simpkins committed
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
	int shown = 0;

	if (!graph)
		return 0;

	if (graph_is_commit_finished(graph))
		return 0;

	for (;;) {
		graph_next_line(graph, &msgbuf);
		fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
		strbuf_setlen(&msgbuf, 0);
		shown = 1;

		if (!graph_is_commit_finished(graph))
			putchar('\n');
		else
			break;
	}
	strbuf_release(&msgbuf);

	return shown;
}


1275
static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
Adam Simpkins's avatar
Adam Simpkins committed
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349
{
	char *p;

	if (!graph) {
		fwrite(sb->buf, sizeof(char), sb->len, stdout);
		return;
	}

	/*
	 * Print the strbuf line by line,
	 * and display the graph info before each line but the first.
	 */
	p = sb->buf;
	while (p) {
		size_t len;
		char *next_p = strchr(p, '\n');
		if (next_p) {
			next_p++;
			len = next_p - p;
		} else {
			len = (sb->buf + sb->len) - p;
		}
		fwrite(p, sizeof(char), len, stdout);
		if (next_p && *next_p != '\0')
			graph_show_oneline(graph);
		p = next_p;
	}
}

void graph_show_commit_msg(struct git_graph *graph,
			   struct strbuf const *sb)
{
	int newline_terminated;

	if (!graph) {
		/*
		 * If there's no graph, just print the message buffer.
		 *
		 * The message buffer for CMIT_FMT_ONELINE and
		 * CMIT_FMT_USERFORMAT are already missing a terminating
		 * newline.  All of the other formats should have it.
		 */
		fwrite(sb->buf, sizeof(char), sb->len, stdout);
		return;
	}

	newline_terminated = (sb->len && sb->buf[sb->len - 1] == '\n');

	/*
	 * Show the commit message
	 */
	graph_show_strbuf(graph, sb);

	/*
	 * If there is more output needed for this commit, show it now
	 */
	if (!graph_is_commit_finished(graph)) {
		/*
		 * If sb doesn't have a terminating newline, print one now,
		 * so we can start the remainder of the graph output on a
		 * new line.
		 */
		if (!newline_terminated)
			putchar('\n');

		graph_show_remainder(graph);

		/*
		 * If sb ends with a newline, our output should too.
		 */
		if (newline_terminated)
			putchar('\n');
	}
}