graph.c 34.3 KB
Newer Older
A
Adam Simpkins 已提交
1 2
#include "cache.h"
#include "commit.h"
3
#include "color.h"
A
Adam Simpkins 已提交
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 27 28 29 30 31 32 33 34 35 36 37
/* Internal API */

/*
 * Output the next line for a graph.
 * This formats the next graph line into the specified strbuf.  It is not
 * terminated with a newline.
 *
 * Returns 1 if the line includes the current commit, and 0 otherwise.
 * graph_next_line() will return 1 exactly once for each time
 * graph_update() is called.
 */
static int graph_next_line(struct git_graph *graph, struct strbuf *sb);

/*
 * 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.
 *
M
Mike Ralphson 已提交
38
 * Since the first line will not include the graph output, the caller is
39 40 41 42 43 44
 * 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);

A
Adam Simpkins 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57
/*
 * 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;
	/*
58 59
	 * The color to (optionally) print this column in.  This is an
	 * index into column_colors.
A
Adam Simpkins 已提交
60
	 */
61
	unsigned short color;
A
Adam Simpkins 已提交
62 63 64 65 66 67 68 69 70 71 72
};

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

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
/*
 * The list of available column colors.
 */
static char column_colors[][COLOR_MAXLEN] = {
	GIT_COLOR_RED,
	GIT_COLOR_GREEN,
	GIT_COLOR_YELLOW,
	GIT_COLOR_BLUE,
	GIT_COLOR_MAGENTA,
	GIT_COLOR_CYAN,
	GIT_COLOR_BOLD GIT_COLOR_RED,
	GIT_COLOR_BOLD GIT_COLOR_GREEN,
	GIT_COLOR_BOLD GIT_COLOR_YELLOW,
	GIT_COLOR_BOLD GIT_COLOR_BLUE,
	GIT_COLOR_BOLD GIT_COLOR_MAGENTA,
	GIT_COLOR_BOLD GIT_COLOR_CYAN,
};

#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))

static const char *column_get_color_code(const struct column *c)
{
	return column_colors[c->color];
}

static void strbuf_write_column(struct strbuf *sb, const struct column *c,
				char col_char)
{
	if (c->color < COLUMN_COLORS_MAX)
		strbuf_addstr(sb, column_get_color_code(c));
	strbuf_addch(sb, col_char);
	if (c->color < COLUMN_COLORS_MAX)
		strbuf_addstr(sb, GIT_COLOR_RESET);
}

A
Adam Simpkins 已提交
108 109 110 111 112
struct git_graph {
	/*
	 * The commit currently being processed
	 */
	struct commit *commit;
113 114
	/* The rev-info used for the current traversal */
	struct rev_info *revs;
A
Adam Simpkins 已提交
115
	/*
116 117 118 119 120
	 * 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().
A
Adam Simpkins 已提交
121 122
	 */
	int num_parents;
123 124 125 126 127 128
	/*
	 * 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;
A
Adam Simpkins 已提交
129 130 131 132 133 134 135 136 137 138
	/*
	 * 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;
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
	/*
	 * 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;
A
Adam Simpkins 已提交
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 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
	/*
	 * 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;
207 208 209 210 211
	/*
	 * The current default column color being used.  This is
	 * stored as an index into the array column_colors.
	 */
	unsigned short default_column_color;
A
Adam Simpkins 已提交
212 213
};

214
struct git_graph *graph_init(struct rev_info *opt)
A
Adam Simpkins 已提交
215 216 217
{
	struct git_graph *graph = xmalloc(sizeof(struct git_graph));
	graph->commit = NULL;
218
	graph->revs = opt;
A
Adam Simpkins 已提交
219 220 221
	graph->num_parents = 0;
	graph->expansion_row = 0;
	graph->state = GRAPH_PADDING;
222 223 224
	graph->prev_state = GRAPH_PADDING;
	graph->commit_index = 0;
	graph->prev_commit_index = 0;
A
Adam Simpkins 已提交
225 226 227
	graph->num_columns = 0;
	graph->num_new_columns = 0;
	graph->mapping_size = 0;
228
	graph->default_column_color = 0;
A
Adam Simpkins 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

	/*
	 * 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);

	return graph;
}

245 246 247 248 249 250
static void graph_update_state(struct git_graph *graph, enum graph_state s)
{
	graph->prev_state = graph->state;
	graph->state = s;
}

A
Adam Simpkins 已提交
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
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);
}

272 273 274 275
/*
 * Returns 1 if the commit will be printed in the graph output,
 * and 0 otherwise.
 */
276
static int graph_is_interesting(struct git_graph *graph, struct commit *commit)
277
{
278 279 280 281 282 283
	/*
	 * 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) {
284
		if (commit->object.flags & CHILD_SHOWN)
285 286 287
			return 1;
	}

288
	/*
289 290
	 * Otherwise, use get_commit_action() to see if this commit is
	 * interesting
291
	 */
292
	return get_commit_action(graph->revs, commit) == commit_show;
293 294
}

295 296 297 298 299 300 301 302 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
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);
}

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
static unsigned short graph_get_current_column_color(const struct git_graph *graph)
{
	if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
		return COLUMN_COLORS_MAX;
	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) %
		COLUMN_COLORS_MAX;
}

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);
}

A
Adam Simpkins 已提交
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
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;
390
	graph->new_columns[graph->num_new_columns].color = graph_find_commit_color(graph, commit);
A
Adam Simpkins 已提交
391 392 393 394 395
	graph->mapping[*mapping_index] = graph->num_new_columns;
	*mapping_index += 2;
	graph->num_new_columns++;
}

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
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;

	/*
411 412
	 * Even if the current commit has no parents to be printed, it
	 * still takes up a column for itself.
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
	 */
	if (graph->num_parents < 1)
		max_cols++;

	/*
	 * We added a column for the the current commit as part of
	 * 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;
}

A
Adam Simpkins 已提交
431 432 433 434 435 436
static void graph_update_columns(struct git_graph *graph)
{
	struct commit_list *parent;
	struct column *tmp_columns;
	int max_new_columns;
	int mapping_idx;
437
	int i, seen_this, is_commit_in_columns;
A
Adam Simpkins 已提交
438 439 440 441 442 443 444 445 446 447 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

	/*
	 * 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;
483
	is_commit_in_columns = 1;
A
Adam Simpkins 已提交
484 485 486 487 488
	for (i = 0; i <= graph->num_columns; i++) {
		struct commit *col_commit;
		if (i == graph->num_columns) {
			if (seen_this)
				break;
489
			is_commit_in_columns = 0;
A
Adam Simpkins 已提交
490 491 492 493 494 495
			col_commit = graph->commit;
		} else {
			col_commit = graph->columns[i].commit;
		}

		if (col_commit == graph->commit) {
496
			int old_mapping_idx = mapping_idx;
A
Adam Simpkins 已提交
497
			seen_this = 1;
498
			graph->commit_index = i;
499
			for (parent = first_interesting_parent(graph);
A
Adam Simpkins 已提交
500
			     parent;
501
			     parent = next_interesting_parent(graph, parent)) {
502 503 504 505 506 507
				/*
				 * If this is a merge increment the current
				 * color.
				 */
				if (graph->num_parents > 1)
					graph_increment_column_color(graph);
A
Adam Simpkins 已提交
508 509 510 511
				graph_insert_into_new_columns(graph,
							      parent->item,
							      &mapping_idx);
			}
512 513 514 515 516 517 518 519
			/*
			 * 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;
A
Adam Simpkins 已提交
520 521 522 523 524 525 526 527 528 529 530 531
		} 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--;
532 533 534 535 536

	/*
	 * Compute graph->width for this commit
	 */
	graph_update_width(graph, is_commit_in_columns);
A
Adam Simpkins 已提交
537 538 539 540 541 542 543 544 545 546 547 548
}

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

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

	/*
549
	 * Count how many interesting parents this commit has
A
Adam Simpkins 已提交
550 551
	 */
	graph->num_parents = 0;
552 553 554 555 556
	for (parent = first_interesting_parent(graph);
	     parent;
	     parent = next_interesting_parent(graph, parent))
	{
		graph->num_parents++;
557
	}
A
Adam Simpkins 已提交
558

559 560 561 562 563 564 565
	/*
	 * 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;

A
Adam Simpkins 已提交
566 567 568 569 570 571 572 573 574 575
	/*
	 * Call graph_update_columns() to update
	 * columns, new_columns, and mapping.
	 */
	graph_update_columns(graph);

	graph->expansion_row = 0;

	/*
	 * Update graph->state.
576 577 578
	 * 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.
A
Adam Simpkins 已提交
579 580 581 582 583
	 *
	 * 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.
	 *
584 585 586 587
	 * 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.
A
Adam Simpkins 已提交
588 589 590 591 592 593
	 *
	 * If there are less than 3 parents, we can immediately print the
	 * commit line.
	 */
	if (graph->state != GRAPH_PADDING)
		graph->state = GRAPH_SKIP;
594 595
	else if (graph->num_parents >= 3 &&
		 graph->commit_index < (graph->num_columns - 1))
A
Adam Simpkins 已提交
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
		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;
}

623 624
static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb,
				   int chars_written)
A
Adam Simpkins 已提交
625 626 627 628 629 630 631 632
{
	/*
	 * 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.
	 */
633
	int extra;
634
	if (chars_written >= graph->width)
A
Adam Simpkins 已提交
635 636
		return;

637
	extra = graph->width - chars_written;
A
Adam Simpkins 已提交
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
	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++) {
660 661
		strbuf_write_column(sb, &graph->new_columns[i], '|');
		strbuf_addch(sb, ' ');
A
Adam Simpkins 已提交
662 663
	}

664
	graph_pad_horizontally(graph, sb, graph->num_new_columns * 2);
A
Adam Simpkins 已提交
665 666 667 668 669 670 671 672 673
}

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, "...");
674
	graph_pad_horizontally(graph, sb, 3);
A
Adam Simpkins 已提交
675

676 677
	if (graph->num_parents >= 3 &&
	    graph->commit_index < (graph->num_columns - 1))
678
		graph_update_state(graph, GRAPH_PRE_COMMIT);
A
Adam Simpkins 已提交
679
	else
680
		graph_update_state(graph, GRAPH_COMMIT);
A
Adam Simpkins 已提交
681 682 683 684 685 686 687
}

static void graph_output_pre_commit_line(struct git_graph *graph,
					 struct strbuf *sb)
{
	int num_expansion_rows;
	int i, seen_this;
688
	int chars_written;
A
Adam Simpkins 已提交
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710

	/*
	 * 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;
711
	chars_written = 0;
A
Adam Simpkins 已提交
712 713 714 715
	for (i = 0; i < graph->num_columns; i++) {
		struct column *col = &graph->columns[i];
		if (col->commit == graph->commit) {
			seen_this = 1;
716
			strbuf_write_column(sb, col, '|');
717 718
			strbuf_addf(sb, "%*s", graph->expansion_row, "");
			chars_written += 1 + graph->expansion_row;
719 720 721 722 723 724 725 726 727 728 729 730
		} 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)
731
				strbuf_write_column(sb, col, '\\');
732
			else
733 734
				strbuf_write_column(sb, col, '|');
			chars_written++;
735
		} else if (seen_this && (graph->expansion_row > 0)) {
736 737
			strbuf_write_column(sb, col, '\\');
			chars_written++;
A
Adam Simpkins 已提交
738
		} else {
739 740
			strbuf_write_column(sb, col, '|');
			chars_written++;
A
Adam Simpkins 已提交
741
		}
742 743
		strbuf_addch(sb, ' ');
		chars_written++;
A
Adam Simpkins 已提交
744 745
	}

746
	graph_pad_horizontally(graph, sb, chars_written);
A
Adam Simpkins 已提交
747 748 749 750 751 752 753

	/*
	 * Increment graph->expansion_row,
	 * and move to state GRAPH_COMMIT if necessary
	 */
	graph->expansion_row++;
	if (graph->expansion_row >= num_expansion_rows)
754
		graph_update_state(graph, GRAPH_COMMIT);
A
Adam Simpkins 已提交
755 756
}

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
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;
	}

	/*
	 * If revs->left_right is set, print '<' for commits that
	 * come from the left side, and '>' for commits from the right
	 * side.
	 */
	if (graph->revs && graph->revs->left_right) {
		if (graph->commit->object.flags & SYMMETRIC_LEFT)
			strbuf_addch(sb, '<');
		else
			strbuf_addch(sb, '>');
		return;
	}

	/*
	 * Print '*' in all other cases
	 */
	strbuf_addch(sb, '*');
}

788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
/*
 * 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++) {
		col_num = (i / 2) + dashless_commits;
		strbuf_write_column(sb, &graph->new_columns[col_num], '-');
	}
	col_num = (i / 2) + dashless_commits;
	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)
A
Adam Simpkins 已提交
813 814
{
	int seen_this = 0;
815
	int i, chars_written;
A
Adam Simpkins 已提交
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;
A
Adam Simpkins 已提交
826
	for (i = 0; i <= graph->num_columns; i++) {
827
		struct column *col = &graph->columns[i];
A
Adam Simpkins 已提交
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++;
A
Adam Simpkins 已提交
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++;
A
Adam Simpkins 已提交
868
		} else {
869 870
			strbuf_write_column(sb, col, '|');
			chars_written++;
A
Adam Simpkins 已提交
871
		}
872 873
		strbuf_addch(sb, ' ');
		chars_written++;
A
Adam Simpkins 已提交
874 875
	}

876
	graph_pad_horizontally(graph, sb, chars_written);
A
Adam Simpkins 已提交
877 878 879 880 881

	/*
	 * Update graph->state
	 */
	if (graph->num_parents > 1)
882
		graph_update_state(graph, GRAPH_POST_MERGE);
A
Adam Simpkins 已提交
883
	else if (graph_is_mapping_correct(graph))
884
		graph_update_state(graph, GRAPH_PADDING);
A
Adam Simpkins 已提交
885
	else
886
		graph_update_state(graph, GRAPH_COLLAPSING);
A
Adam Simpkins 已提交
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)
A
Adam Simpkins 已提交
901 902
{
	int seen_this = 0;
903
	int i, j, chars_written;
A
Adam Simpkins 已提交
904 905 906 907

	/*
	 * Output the post-merge row
	 */
908
	chars_written = 0;
A
Adam Simpkins 已提交
909
	for (i = 0; i <= graph->num_columns; i++) {
910
		struct column *col = &graph->columns[i];
A
Adam Simpkins 已提交
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;
A
Adam Simpkins 已提交
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;
A
Adam Simpkins 已提交
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;
A
Adam Simpkins 已提交
950
		} else {
951 952 953
			strbuf_write_column(sb, col, '|');
			strbuf_addch(sb, ' ');
			chars_written += 2;
A
Adam Simpkins 已提交
954 955 956
		}
	}

957
	graph_pad_horizontally(graph, sb, chars_written);
A
Adam Simpkins 已提交
958 959 960 961 962

	/*
	 * Update graph->state
	 */
	if (graph_is_mapping_correct(graph))
963
		graph_update_state(graph, GRAPH_PADDING);
A
Adam Simpkins 已提交
964
	else
965
		graph_update_state(graph, GRAPH_COLLAPSING);
A
Adam Simpkins 已提交
966 967
}

968
static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
A
Adam Simpkins 已提交
969 970 971
{
	int i;
	int *tmp_mapping;
972 973 974
	short used_horizontal = 0;
	int horizontal_edge = -1;
	int horizontal_edge_target = -1;
A
Adam Simpkins 已提交
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;
			}
A
Adam Simpkins 已提交
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.
A
Adam Simpkins 已提交
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);
A
Adam Simpkins 已提交
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;
A
Adam Simpkins 已提交
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

		}
A
Adam Simpkins 已提交
1099 1100
	}

1101
	graph_pad_horizontally(graph, sb, graph->mapping_size);
A
Adam Simpkins 已提交
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);
A
Adam Simpkins 已提交
1117 1118
}

1119
static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
A
Adam Simpkins 已提交
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)
A
Adam Simpkins 已提交
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;
A
Adam Simpkins 已提交
1165
		if (col_commit == graph->commit) {
1166
			strbuf_write_column(sb, col, '|');
A
Adam Simpkins 已提交
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, ' ');
A
Adam Simpkins 已提交
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;
A
Adam Simpkins 已提交
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;
A
Adam Simpkins 已提交
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
	int shown_commit_line = 0;

	if (!graph)
		return;

	while (!shown_commit_line) {
		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)
{
1215
	struct strbuf msgbuf = STRBUF_INIT;
A
Adam Simpkins 已提交
1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226

	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)
{
1227
	struct strbuf msgbuf = STRBUF_INIT;
A
Adam Simpkins 已提交
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238

	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)
{
1239
	struct strbuf msgbuf = STRBUF_INIT;
A
Adam Simpkins 已提交
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
	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;
}


1265
static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
A
Adam Simpkins 已提交
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 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
{
	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');
	}
}