1#include"cache.h" 2#include"commit.h" 3#include"color.h" 4#include"graph.h" 5#include"diff.h" 6#include"revision.h" 7 8/* Internal API */ 9 10/* 11 * Output a padding line in the graph. 12 * This is similar to graph_next_line(). However, it is guaranteed to 13 * never print the current commit line. Instead, if the commit line is 14 * next, it will simply output a line of vertical padding, extending the 15 * branch lines downwards, but leaving them otherwise unchanged. 16 */ 17static voidgraph_padding_line(struct git_graph *graph,struct strbuf *sb); 18 19/* 20 * Print a strbuf to stdout. If the graph is non-NULL, all lines but the 21 * first will be prefixed with the graph output. 22 * 23 * If the strbuf ends with a newline, the output will end after this 24 * newline. A new graph line will not be printed after the final newline. 25 * If the strbuf is empty, no output will be printed. 26 * 27 * Since the first line will not include the graph output, the caller is 28 * responsible for printing this line's graph (perhaps via 29 * graph_show_commit() or graph_show_oneline()) before calling 30 * graph_show_strbuf(). 31 */ 32static voidgraph_show_strbuf(struct git_graph *graph,struct strbuf const*sb); 33 34/* 35 * TODO: 36 * - Limit the number of columns, similar to the way gitk does. 37 * If we reach more than a specified number of columns, omit 38 * sections of some columns. 39 */ 40 41struct column { 42/* 43 * The parent commit of this column. 44 */ 45struct commit *commit; 46/* 47 * The color to (optionally) print this column in. This is an 48 * index into column_colors. 49 */ 50unsigned short color; 51}; 52 53enum graph_state { 54 GRAPH_PADDING, 55 GRAPH_SKIP, 56 GRAPH_PRE_COMMIT, 57 GRAPH_COMMIT, 58 GRAPH_POST_MERGE, 59 GRAPH_COLLAPSING 60}; 61 62/* 63 * The list of available column colors. 64 */ 65static char column_colors[][COLOR_MAXLEN] = { 66 GIT_COLOR_RED, 67 GIT_COLOR_GREEN, 68 GIT_COLOR_YELLOW, 69 GIT_COLOR_BLUE, 70 GIT_COLOR_MAGENTA, 71 GIT_COLOR_CYAN, 72 GIT_COLOR_BOLD_RED, 73 GIT_COLOR_BOLD_GREEN, 74 GIT_COLOR_BOLD_YELLOW, 75 GIT_COLOR_BOLD_BLUE, 76 GIT_COLOR_BOLD_MAGENTA, 77 GIT_COLOR_BOLD_CYAN, 78}; 79 80#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors)) 81 82static const char*column_get_color_code(const struct column *c) 83{ 84return column_colors[c->color]; 85} 86 87static voidstrbuf_write_column(struct strbuf *sb,const struct column *c, 88char col_char) 89{ 90if(c->color < COLUMN_COLORS_MAX) 91strbuf_addstr(sb,column_get_color_code(c)); 92strbuf_addch(sb, col_char); 93if(c->color < COLUMN_COLORS_MAX) 94strbuf_addstr(sb, GIT_COLOR_RESET); 95} 96 97struct git_graph { 98/* 99 * The commit currently being processed 100 */ 101struct commit *commit; 102/* The rev-info used for the current traversal */ 103struct rev_info *revs; 104/* 105 * The number of interesting parents that this commit has. 106 * 107 * Note that this is not the same as the actual number of parents. 108 * This count excludes parents that won't be printed in the graph 109 * output, as determined by graph_is_interesting(). 110 */ 111int num_parents; 112/* 113 * The width of the graph output for this commit. 114 * All rows for this commit are padded to this width, so that 115 * messages printed after the graph output are aligned. 116 */ 117int width; 118/* 119 * The next expansion row to print 120 * when state is GRAPH_PRE_COMMIT 121 */ 122int expansion_row; 123/* 124 * The current output state. 125 * This tells us what kind of line graph_next_line() should output. 126 */ 127enum graph_state state; 128/* 129 * The output state for the previous line of output. 130 * This is primarily used to determine how the first merge line 131 * should appear, based on the last line of the previous commit. 132 */ 133enum graph_state prev_state; 134/* 135 * The index of the column that refers to this commit. 136 * 137 * If none of the incoming columns refer to this commit, 138 * this will be equal to num_columns. 139 */ 140int commit_index; 141/* 142 * The commit_index for the previously displayed commit. 143 * 144 * This is used to determine how the first line of a merge 145 * graph output should appear, based on the last line of the 146 * previous commit. 147 */ 148int prev_commit_index; 149/* 150 * The maximum number of columns that can be stored in the columns 151 * and new_columns arrays. This is also half the number of entries 152 * that can be stored in the mapping and new_mapping arrays. 153 */ 154int column_capacity; 155/* 156 * The number of columns (also called "branch lines" in some places) 157 */ 158int num_columns; 159/* 160 * The number of columns in the new_columns array 161 */ 162int num_new_columns; 163/* 164 * The number of entries in the mapping array 165 */ 166int mapping_size; 167/* 168 * The column state before we output the current commit. 169 */ 170struct column *columns; 171/* 172 * The new column state after we output the current commit. 173 * Only valid when state is GRAPH_COLLAPSING. 174 */ 175struct column *new_columns; 176/* 177 * An array that tracks the current state of each 178 * character in the output line during state GRAPH_COLLAPSING. 179 * Each entry is -1 if this character is empty, or a non-negative 180 * integer if the character contains a branch line. The value of 181 * the integer indicates the target position for this branch line. 182 * (I.e., this array maps the current column positions to their 183 * desired positions.) 184 * 185 * The maximum capacity of this array is always 186 * sizeof(int) * 2 * column_capacity. 187 */ 188int*mapping; 189/* 190 * A temporary array for computing the next mapping state 191 * while we are outputting a mapping line. This is stored as part 192 * of the git_graph simply so we don't have to allocate a new 193 * temporary array each time we have to output a collapsing line. 194 */ 195int*new_mapping; 196/* 197 * The current default column color being used. This is 198 * stored as an index into the array column_colors. 199 */ 200unsigned short default_column_color; 201}; 202 203static struct strbuf *diff_output_prefix_callback(struct diff_options *opt,void*data) 204{ 205struct git_graph *graph = data; 206static struct strbuf msgbuf = STRBUF_INIT; 207 208assert(graph); 209 210strbuf_reset(&msgbuf); 211graph_padding_line(graph, &msgbuf); 212return&msgbuf; 213} 214 215struct git_graph *graph_init(struct rev_info *opt) 216{ 217struct git_graph *graph =xmalloc(sizeof(struct git_graph)); 218 graph->commit = NULL; 219 graph->revs = opt; 220 graph->num_parents =0; 221 graph->expansion_row =0; 222 graph->state = GRAPH_PADDING; 223 graph->prev_state = GRAPH_PADDING; 224 graph->commit_index =0; 225 graph->prev_commit_index =0; 226 graph->num_columns =0; 227 graph->num_new_columns =0; 228 graph->mapping_size =0; 229/* 230 * Start the column color at the maximum value, since we'll 231 * always increment it for the first commit we output. 232 * This way we start at 0 for the first commit. 233 */ 234 graph->default_column_color = COLUMN_COLORS_MAX -1; 235 236/* 237 * Allocate a reasonably large default number of columns 238 * We'll automatically grow columns later if we need more room. 239 */ 240 graph->column_capacity =30; 241 graph->columns =xmalloc(sizeof(struct column) * 242 graph->column_capacity); 243 graph->new_columns =xmalloc(sizeof(struct column) * 244 graph->column_capacity); 245 graph->mapping =xmalloc(sizeof(int) *2* graph->column_capacity); 246 graph->new_mapping =xmalloc(sizeof(int) *2* graph->column_capacity); 247 248/* 249 * The diff output prefix callback, with this we can make 250 * all the diff output to align with the graph lines. 251 */ 252 opt->diffopt.output_prefix = diff_output_prefix_callback; 253 opt->diffopt.output_prefix_data = graph; 254 255return graph; 256} 257 258static voidgraph_update_state(struct git_graph *graph,enum graph_state s) 259{ 260 graph->prev_state = graph->state; 261 graph->state = s; 262} 263 264static voidgraph_ensure_capacity(struct git_graph *graph,int num_columns) 265{ 266if(graph->column_capacity >= num_columns) 267return; 268 269do{ 270 graph->column_capacity *=2; 271}while(graph->column_capacity < num_columns); 272 273 graph->columns =xrealloc(graph->columns, 274sizeof(struct column) * 275 graph->column_capacity); 276 graph->new_columns =xrealloc(graph->new_columns, 277sizeof(struct column) * 278 graph->column_capacity); 279 graph->mapping =xrealloc(graph->mapping, 280sizeof(int) *2* graph->column_capacity); 281 graph->new_mapping =xrealloc(graph->new_mapping, 282sizeof(int) *2* graph->column_capacity); 283} 284 285/* 286 * Returns 1 if the commit will be printed in the graph output, 287 * and 0 otherwise. 288 */ 289static intgraph_is_interesting(struct git_graph *graph,struct commit *commit) 290{ 291/* 292 * If revs->boundary is set, commits whose children have 293 * been shown are always interesting, even if they have the 294 * UNINTERESTING or TREESAME flags set. 295 */ 296if(graph->revs && graph->revs->boundary) { 297if(commit->object.flags & CHILD_SHOWN) 298return1; 299} 300 301/* 302 * Otherwise, use get_commit_action() to see if this commit is 303 * interesting 304 */ 305returnget_commit_action(graph->revs, commit) == commit_show; 306} 307 308static struct commit_list *next_interesting_parent(struct git_graph *graph, 309struct commit_list *orig) 310{ 311struct commit_list *list; 312 313/* 314 * If revs->first_parent_only is set, only the first 315 * parent is interesting. None of the others are. 316 */ 317if(graph->revs->first_parent_only) 318return NULL; 319 320/* 321 * Return the next interesting commit after orig 322 */ 323for(list = orig->next; list; list = list->next) { 324if(graph_is_interesting(graph, list->item)) 325return list; 326} 327 328return NULL; 329} 330 331static struct commit_list *first_interesting_parent(struct git_graph *graph) 332{ 333struct commit_list *parents = graph->commit->parents; 334 335/* 336 * If this commit has no parents, ignore it 337 */ 338if(!parents) 339return NULL; 340 341/* 342 * If the first parent is interesting, return it 343 */ 344if(graph_is_interesting(graph, parents->item)) 345return parents; 346 347/* 348 * Otherwise, call next_interesting_parent() to get 349 * the next interesting parent 350 */ 351returnnext_interesting_parent(graph, parents); 352} 353 354static unsigned shortgraph_get_current_column_color(const struct git_graph *graph) 355{ 356if(!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF)) 357return COLUMN_COLORS_MAX; 358return graph->default_column_color; 359} 360 361/* 362 * Update the graph's default column color. 363 */ 364static voidgraph_increment_column_color(struct git_graph *graph) 365{ 366 graph->default_column_color = (graph->default_column_color +1) % 367 COLUMN_COLORS_MAX; 368} 369 370static unsigned shortgraph_find_commit_color(const struct git_graph *graph, 371const struct commit *commit) 372{ 373int i; 374for(i =0; i < graph->num_columns; i++) { 375if(graph->columns[i].commit == commit) 376return graph->columns[i].color; 377} 378returngraph_get_current_column_color(graph); 379} 380 381static voidgraph_insert_into_new_columns(struct git_graph *graph, 382struct commit *commit, 383int*mapping_index) 384{ 385int i; 386 387/* 388 * If the commit is already in the new_columns list, we don't need to 389 * add it. Just update the mapping correctly. 390 */ 391for(i =0; i < graph->num_new_columns; i++) { 392if(graph->new_columns[i].commit == commit) { 393 graph->mapping[*mapping_index] = i; 394*mapping_index +=2; 395return; 396} 397} 398 399/* 400 * This commit isn't already in new_columns. Add it. 401 */ 402 graph->new_columns[graph->num_new_columns].commit = commit; 403 graph->new_columns[graph->num_new_columns].color =graph_find_commit_color(graph, commit); 404 graph->mapping[*mapping_index] = graph->num_new_columns; 405*mapping_index +=2; 406 graph->num_new_columns++; 407} 408 409static voidgraph_update_width(struct git_graph *graph, 410int is_commit_in_existing_columns) 411{ 412/* 413 * Compute the width needed to display the graph for this commit. 414 * This is the maximum width needed for any row. All other rows 415 * will be padded to this width. 416 * 417 * Compute the number of columns in the widest row: 418 * Count each existing column (graph->num_columns), and each new 419 * column added by this commit. 420 */ 421int max_cols = graph->num_columns + graph->num_parents; 422 423/* 424 * Even if the current commit has no parents to be printed, it 425 * still takes up a column for itself. 426 */ 427if(graph->num_parents <1) 428 max_cols++; 429 430/* 431 * We added a column for the the current commit as part of 432 * graph->num_parents. If the current commit was already in 433 * graph->columns, then we have double counted it. 434 */ 435if(is_commit_in_existing_columns) 436 max_cols--; 437 438/* 439 * Each column takes up 2 spaces 440 */ 441 graph->width = max_cols *2; 442} 443 444static voidgraph_update_columns(struct git_graph *graph) 445{ 446struct commit_list *parent; 447struct column *tmp_columns; 448int max_new_columns; 449int mapping_idx; 450int i, seen_this, is_commit_in_columns; 451 452/* 453 * Swap graph->columns with graph->new_columns 454 * graph->columns contains the state for the previous commit, 455 * and new_columns now contains the state for our commit. 456 * 457 * We'll re-use the old columns array as storage to compute the new 458 * columns list for the commit after this one. 459 */ 460 tmp_columns = graph->columns; 461 graph->columns = graph->new_columns; 462 graph->num_columns = graph->num_new_columns; 463 464 graph->new_columns = tmp_columns; 465 graph->num_new_columns =0; 466 467/* 468 * Now update new_columns and mapping with the information for the 469 * commit after this one. 470 * 471 * First, make sure we have enough room. At most, there will 472 * be graph->num_columns + graph->num_parents columns for the next 473 * commit. 474 */ 475 max_new_columns = graph->num_columns + graph->num_parents; 476graph_ensure_capacity(graph, max_new_columns); 477 478/* 479 * Clear out graph->mapping 480 */ 481 graph->mapping_size =2* max_new_columns; 482for(i =0; i < graph->mapping_size; i++) 483 graph->mapping[i] = -1; 484 485/* 486 * Populate graph->new_columns and graph->mapping 487 * 488 * Some of the parents of this commit may already be in 489 * graph->columns. If so, graph->new_columns should only contain a 490 * single entry for each such commit. graph->mapping should 491 * contain information about where each current branch line is 492 * supposed to end up after the collapsing is performed. 493 */ 494 seen_this =0; 495 mapping_idx =0; 496 is_commit_in_columns =1; 497for(i =0; i <= graph->num_columns; i++) { 498struct commit *col_commit; 499if(i == graph->num_columns) { 500if(seen_this) 501break; 502 is_commit_in_columns =0; 503 col_commit = graph->commit; 504}else{ 505 col_commit = graph->columns[i].commit; 506} 507 508if(col_commit == graph->commit) { 509int old_mapping_idx = mapping_idx; 510 seen_this =1; 511 graph->commit_index = i; 512for(parent =first_interesting_parent(graph); 513 parent; 514 parent =next_interesting_parent(graph, parent)) { 515/* 516 * If this is a merge, or the start of a new 517 * childless column, increment the current 518 * color. 519 */ 520if(graph->num_parents >1|| 521!is_commit_in_columns) { 522graph_increment_column_color(graph); 523} 524graph_insert_into_new_columns(graph, 525 parent->item, 526&mapping_idx); 527} 528/* 529 * We always need to increment mapping_idx by at 530 * least 2, even if it has no interesting parents. 531 * The current commit always takes up at least 2 532 * spaces. 533 */ 534if(mapping_idx == old_mapping_idx) 535 mapping_idx +=2; 536}else{ 537graph_insert_into_new_columns(graph, col_commit, 538&mapping_idx); 539} 540} 541 542/* 543 * Shrink mapping_size to be the minimum necessary 544 */ 545while(graph->mapping_size >1&& 546 graph->mapping[graph->mapping_size -1] <0) 547 graph->mapping_size--; 548 549/* 550 * Compute graph->width for this commit 551 */ 552graph_update_width(graph, is_commit_in_columns); 553} 554 555voidgraph_update(struct git_graph *graph,struct commit *commit) 556{ 557struct commit_list *parent; 558 559/* 560 * Set the new commit 561 */ 562 graph->commit = commit; 563 564/* 565 * Count how many interesting parents this commit has 566 */ 567 graph->num_parents =0; 568for(parent =first_interesting_parent(graph); 569 parent; 570 parent =next_interesting_parent(graph, parent)) 571{ 572 graph->num_parents++; 573} 574 575/* 576 * Store the old commit_index in prev_commit_index. 577 * graph_update_columns() will update graph->commit_index for this 578 * commit. 579 */ 580 graph->prev_commit_index = graph->commit_index; 581 582/* 583 * Call graph_update_columns() to update 584 * columns, new_columns, and mapping. 585 */ 586graph_update_columns(graph); 587 588 graph->expansion_row =0; 589 590/* 591 * Update graph->state. 592 * Note that we don't call graph_update_state() here, since 593 * we don't want to update graph->prev_state. No line for 594 * graph->state was ever printed. 595 * 596 * If the previous commit didn't get to the GRAPH_PADDING state, 597 * it never finished its output. Goto GRAPH_SKIP, to print out 598 * a line to indicate that portion of the graph is missing. 599 * 600 * If there are 3 or more parents, we may need to print extra rows 601 * before the commit, to expand the branch lines around it and make 602 * room for it. We need to do this only if there is a branch row 603 * (or more) to the right of this commit. 604 * 605 * If there are less than 3 parents, we can immediately print the 606 * commit line. 607 */ 608if(graph->state != GRAPH_PADDING) 609 graph->state = GRAPH_SKIP; 610else if(graph->num_parents >=3&& 611 graph->commit_index < (graph->num_columns -1)) 612 graph->state = GRAPH_PRE_COMMIT; 613else 614 graph->state = GRAPH_COMMIT; 615} 616 617static intgraph_is_mapping_correct(struct git_graph *graph) 618{ 619int i; 620 621/* 622 * The mapping is up to date if each entry is at its target, 623 * or is 1 greater than its target. 624 * (If it is 1 greater than the target, '/' will be printed, so it 625 * will look correct on the next row.) 626 */ 627for(i =0; i < graph->mapping_size; i++) { 628int target = graph->mapping[i]; 629if(target <0) 630continue; 631if(target == (i /2)) 632continue; 633return0; 634} 635 636return1; 637} 638 639static voidgraph_pad_horizontally(struct git_graph *graph,struct strbuf *sb, 640int chars_written) 641{ 642/* 643 * Add additional spaces to the end of the strbuf, so that all 644 * lines for a particular commit have the same width. 645 * 646 * This way, fields printed to the right of the graph will remain 647 * aligned for the entire commit. 648 */ 649int extra; 650if(chars_written >= graph->width) 651return; 652 653 extra = graph->width - chars_written; 654strbuf_addf(sb,"%*s", (int) extra,""); 655} 656 657static voidgraph_output_padding_line(struct git_graph *graph, 658struct strbuf *sb) 659{ 660int i; 661 662/* 663 * We could conceivable be called with a NULL commit 664 * if our caller has a bug, and invokes graph_next_line() 665 * immediately after graph_init(), without first calling 666 * graph_update(). Return without outputting anything in this 667 * case. 668 */ 669if(!graph->commit) 670return; 671 672/* 673 * Output a padding row, that leaves all branch lines unchanged 674 */ 675for(i =0; i < graph->num_new_columns; i++) { 676strbuf_write_column(sb, &graph->new_columns[i],'|'); 677strbuf_addch(sb,' '); 678} 679 680graph_pad_horizontally(graph, sb, graph->num_new_columns *2); 681} 682 683static voidgraph_output_skip_line(struct git_graph *graph,struct strbuf *sb) 684{ 685/* 686 * Output an ellipsis to indicate that a portion 687 * of the graph is missing. 688 */ 689strbuf_addstr(sb,"..."); 690graph_pad_horizontally(graph, sb,3); 691 692if(graph->num_parents >=3&& 693 graph->commit_index < (graph->num_columns -1)) 694graph_update_state(graph, GRAPH_PRE_COMMIT); 695else 696graph_update_state(graph, GRAPH_COMMIT); 697} 698 699static voidgraph_output_pre_commit_line(struct git_graph *graph, 700struct strbuf *sb) 701{ 702int num_expansion_rows; 703int i, seen_this; 704int chars_written; 705 706/* 707 * This function formats a row that increases the space around a commit 708 * with multiple parents, to make room for it. It should only be 709 * called when there are 3 or more parents. 710 * 711 * We need 2 extra rows for every parent over 2. 712 */ 713assert(graph->num_parents >=3); 714 num_expansion_rows = (graph->num_parents -2) *2; 715 716/* 717 * graph->expansion_row tracks the current expansion row we are on. 718 * It should be in the range [0, num_expansion_rows - 1] 719 */ 720assert(0<= graph->expansion_row && 721 graph->expansion_row < num_expansion_rows); 722 723/* 724 * Output the row 725 */ 726 seen_this =0; 727 chars_written =0; 728for(i =0; i < graph->num_columns; i++) { 729struct column *col = &graph->columns[i]; 730if(col->commit == graph->commit) { 731 seen_this =1; 732strbuf_write_column(sb, col,'|'); 733strbuf_addf(sb,"%*s", graph->expansion_row,""); 734 chars_written +=1+ graph->expansion_row; 735}else if(seen_this && (graph->expansion_row ==0)) { 736/* 737 * This is the first line of the pre-commit output. 738 * If the previous commit was a merge commit and 739 * ended in the GRAPH_POST_MERGE state, all branch 740 * lines after graph->prev_commit_index were 741 * printed as "\" on the previous line. Continue 742 * to print them as "\" on this line. Otherwise, 743 * print the branch lines as "|". 744 */ 745if(graph->prev_state == GRAPH_POST_MERGE && 746 graph->prev_commit_index < i) 747strbuf_write_column(sb, col,'\\'); 748else 749strbuf_write_column(sb, col,'|'); 750 chars_written++; 751}else if(seen_this && (graph->expansion_row >0)) { 752strbuf_write_column(sb, col,'\\'); 753 chars_written++; 754}else{ 755strbuf_write_column(sb, col,'|'); 756 chars_written++; 757} 758strbuf_addch(sb,' '); 759 chars_written++; 760} 761 762graph_pad_horizontally(graph, sb, chars_written); 763 764/* 765 * Increment graph->expansion_row, 766 * and move to state GRAPH_COMMIT if necessary 767 */ 768 graph->expansion_row++; 769if(graph->expansion_row >= num_expansion_rows) 770graph_update_state(graph, GRAPH_COMMIT); 771} 772 773static voidgraph_output_commit_char(struct git_graph *graph,struct strbuf *sb) 774{ 775/* 776 * For boundary commits, print 'o' 777 * (We should only see boundary commits when revs->boundary is set.) 778 */ 779if(graph->commit->object.flags & BOUNDARY) { 780assert(graph->revs->boundary); 781strbuf_addch(sb,'o'); 782return; 783} 784 785/* 786 * If revs->left_right is set, print '<' for commits that 787 * come from the left side, and '>' for commits from the right 788 * side. 789 */ 790if(graph->revs && graph->revs->left_right) { 791if(graph->commit->object.flags & SYMMETRIC_LEFT) 792strbuf_addch(sb,'<'); 793else 794strbuf_addch(sb,'>'); 795return; 796} 797 798/* 799 * Print '*' in all other cases 800 */ 801strbuf_addch(sb,'*'); 802} 803 804/* 805 * Draw an octopus merge and return the number of characters written. 806 */ 807static intgraph_draw_octopus_merge(struct git_graph *graph, 808struct strbuf *sb) 809{ 810/* 811 * Here dashless_commits represents the number of parents 812 * which don't need to have dashes (because their edges fit 813 * neatly under the commit). 814 */ 815const int dashless_commits =2; 816int col_num, i; 817int num_dashes = 818((graph->num_parents - dashless_commits) *2) -1; 819for(i =0; i < num_dashes; i++) { 820 col_num = (i /2) + dashless_commits; 821strbuf_write_column(sb, &graph->new_columns[col_num],'-'); 822} 823 col_num = (i /2) + dashless_commits; 824strbuf_write_column(sb, &graph->new_columns[col_num],'.'); 825return num_dashes +1; 826} 827 828static voidgraph_output_commit_line(struct git_graph *graph,struct strbuf *sb) 829{ 830int seen_this =0; 831int i, chars_written; 832 833/* 834 * Output the row containing this commit 835 * Iterate up to and including graph->num_columns, 836 * since the current commit may not be in any of the existing 837 * columns. (This happens when the current commit doesn't have any 838 * children that we have already processed.) 839 */ 840 seen_this =0; 841 chars_written =0; 842for(i =0; i <= graph->num_columns; i++) { 843struct column *col = &graph->columns[i]; 844struct commit *col_commit; 845if(i == graph->num_columns) { 846if(seen_this) 847break; 848 col_commit = graph->commit; 849}else{ 850 col_commit = graph->columns[i].commit; 851} 852 853if(col_commit == graph->commit) { 854 seen_this =1; 855graph_output_commit_char(graph, sb); 856 chars_written++; 857 858if(graph->num_parents >2) 859 chars_written +=graph_draw_octopus_merge(graph, 860 sb); 861}else if(seen_this && (graph->num_parents >2)) { 862strbuf_write_column(sb, col,'\\'); 863 chars_written++; 864}else if(seen_this && (graph->num_parents ==2)) { 865/* 866 * This is a 2-way merge commit. 867 * There is no GRAPH_PRE_COMMIT stage for 2-way 868 * merges, so this is the first line of output 869 * for this commit. Check to see what the previous 870 * line of output was. 871 * 872 * If it was GRAPH_POST_MERGE, the branch line 873 * coming into this commit may have been '\', 874 * and not '|' or '/'. If so, output the branch 875 * line as '\' on this line, instead of '|'. This 876 * makes the output look nicer. 877 */ 878if(graph->prev_state == GRAPH_POST_MERGE && 879 graph->prev_commit_index < i) 880strbuf_write_column(sb, col,'\\'); 881else 882strbuf_write_column(sb, col,'|'); 883 chars_written++; 884}else{ 885strbuf_write_column(sb, col,'|'); 886 chars_written++; 887} 888strbuf_addch(sb,' '); 889 chars_written++; 890} 891 892graph_pad_horizontally(graph, sb, chars_written); 893 894/* 895 * Update graph->state 896 */ 897if(graph->num_parents >1) 898graph_update_state(graph, GRAPH_POST_MERGE); 899else if(graph_is_mapping_correct(graph)) 900graph_update_state(graph, GRAPH_PADDING); 901else 902graph_update_state(graph, GRAPH_COLLAPSING); 903} 904 905static struct column *find_new_column_by_commit(struct git_graph *graph, 906struct commit *commit) 907{ 908int i; 909for(i =0; i < graph->num_new_columns; i++) { 910if(graph->new_columns[i].commit == commit) 911return&graph->new_columns[i]; 912} 913return NULL; 914} 915 916static voidgraph_output_post_merge_line(struct git_graph *graph,struct strbuf *sb) 917{ 918int seen_this =0; 919int i, j, chars_written; 920 921/* 922 * Output the post-merge row 923 */ 924 chars_written =0; 925for(i =0; i <= graph->num_columns; i++) { 926struct column *col = &graph->columns[i]; 927struct commit *col_commit; 928if(i == graph->num_columns) { 929if(seen_this) 930break; 931 col_commit = graph->commit; 932}else{ 933 col_commit = col->commit; 934} 935 936if(col_commit == graph->commit) { 937/* 938 * Since the current commit is a merge find 939 * the columns for the parent commits in 940 * new_columns and use those to format the 941 * edges. 942 */ 943struct commit_list *parents = NULL; 944struct column *par_column; 945 seen_this =1; 946 parents =first_interesting_parent(graph); 947assert(parents); 948 par_column =find_new_column_by_commit(graph, parents->item); 949assert(par_column); 950 951strbuf_write_column(sb, par_column,'|'); 952 chars_written++; 953for(j =0; j < graph->num_parents -1; j++) { 954 parents =next_interesting_parent(graph, parents); 955assert(parents); 956 par_column =find_new_column_by_commit(graph, parents->item); 957assert(par_column); 958strbuf_write_column(sb, par_column,'\\'); 959strbuf_addch(sb,' '); 960} 961 chars_written += j *2; 962}else if(seen_this) { 963strbuf_write_column(sb, col,'\\'); 964strbuf_addch(sb,' '); 965 chars_written +=2; 966}else{ 967strbuf_write_column(sb, col,'|'); 968strbuf_addch(sb,' '); 969 chars_written +=2; 970} 971} 972 973graph_pad_horizontally(graph, sb, chars_written); 974 975/* 976 * Update graph->state 977 */ 978if(graph_is_mapping_correct(graph)) 979graph_update_state(graph, GRAPH_PADDING); 980else 981graph_update_state(graph, GRAPH_COLLAPSING); 982} 983 984static voidgraph_output_collapsing_line(struct git_graph *graph,struct strbuf *sb) 985{ 986int i; 987int*tmp_mapping; 988short used_horizontal =0; 989int horizontal_edge = -1; 990int horizontal_edge_target = -1; 991 992/* 993 * Clear out the new_mapping array 994 */ 995for(i =0; i < graph->mapping_size; i++) 996 graph->new_mapping[i] = -1; 997 998for(i =0; i < graph->mapping_size; i++) { 999int target = graph->mapping[i];1000if(target <0)1001continue;10021003/*1004 * Since update_columns() always inserts the leftmost1005 * column first, each branch's target location should1006 * always be either its current location or to the left of1007 * its current location.1008 *1009 * We never have to move branches to the right. This makes1010 * the graph much more legible, since whenever branches1011 * cross, only one is moving directions.1012 */1013assert(target *2<= i);10141015if(target *2== i) {1016/*1017 * This column is already in the1018 * correct place1019 */1020assert(graph->new_mapping[i] == -1);1021 graph->new_mapping[i] = target;1022}else if(graph->new_mapping[i -1] <0) {1023/*1024 * Nothing is to the left.1025 * Move to the left by one1026 */1027 graph->new_mapping[i -1] = target;1028/*1029 * If there isn't already an edge moving horizontally1030 * select this one.1031 */1032if(horizontal_edge == -1) {1033int j;1034 horizontal_edge = i;1035 horizontal_edge_target = target;1036/*1037 * The variable target is the index of the graph1038 * column, and therefore target*2+3 is the1039 * actual screen column of the first horizontal1040 * line.1041 */1042for(j = (target *2)+3; j < (i -2); j +=2)1043 graph->new_mapping[j] = target;1044}1045}else if(graph->new_mapping[i -1] == target) {1046/*1047 * There is a branch line to our left1048 * already, and it is our target. We1049 * combine with this line, since we share1050 * the same parent commit.1051 *1052 * We don't have to add anything to the1053 * output or new_mapping, since the1054 * existing branch line has already taken1055 * care of it.1056 */1057}else{1058/*1059 * There is a branch line to our left,1060 * but it isn't our target. We need to1061 * cross over it.1062 *1063 * The space just to the left of this1064 * branch should always be empty.1065 *1066 * The branch to the left of that space1067 * should be our eventual target.1068 */1069assert(graph->new_mapping[i -1] > target);1070assert(graph->new_mapping[i -2] <0);1071assert(graph->new_mapping[i -3] == target);1072 graph->new_mapping[i -2] = target;1073/*1074 * Mark this branch as the horizontal edge to1075 * prevent any other edges from moving1076 * horizontally.1077 */1078if(horizontal_edge == -1)1079 horizontal_edge = i;1080}1081}10821083/*1084 * The new mapping may be 1 smaller than the old mapping1085 */1086if(graph->new_mapping[graph->mapping_size -1] <0)1087 graph->mapping_size--;10881089/*1090 * Output out a line based on the new mapping info1091 */1092for(i =0; i < graph->mapping_size; i++) {1093int target = graph->new_mapping[i];1094if(target <0)1095strbuf_addch(sb,' ');1096else if(target *2== i)1097strbuf_write_column(sb, &graph->new_columns[target],'|');1098else if(target == horizontal_edge_target &&1099 i != horizontal_edge -1) {1100/*1101 * Set the mappings for all but the1102 * first segment to -1 so that they1103 * won't continue into the next line.1104 */1105if(i != (target *2)+3)1106 graph->new_mapping[i] = -1;1107 used_horizontal =1;1108strbuf_write_column(sb, &graph->new_columns[target],'_');1109}else{1110if(used_horizontal && i < horizontal_edge)1111 graph->new_mapping[i] = -1;1112strbuf_write_column(sb, &graph->new_columns[target],'/');11131114}1115}11161117graph_pad_horizontally(graph, sb, graph->mapping_size);11181119/*1120 * Swap mapping and new_mapping1121 */1122 tmp_mapping = graph->mapping;1123 graph->mapping = graph->new_mapping;1124 graph->new_mapping = tmp_mapping;11251126/*1127 * If graph->mapping indicates that all of the branch lines1128 * are already in the correct positions, we are done.1129 * Otherwise, we need to collapse some branch lines together.1130 */1131if(graph_is_mapping_correct(graph))1132graph_update_state(graph, GRAPH_PADDING);1133}11341135intgraph_next_line(struct git_graph *graph,struct strbuf *sb)1136{1137switch(graph->state) {1138case GRAPH_PADDING:1139graph_output_padding_line(graph, sb);1140return0;1141case GRAPH_SKIP:1142graph_output_skip_line(graph, sb);1143return0;1144case GRAPH_PRE_COMMIT:1145graph_output_pre_commit_line(graph, sb);1146return0;1147case GRAPH_COMMIT:1148graph_output_commit_line(graph, sb);1149return1;1150case GRAPH_POST_MERGE:1151graph_output_post_merge_line(graph, sb);1152return0;1153case GRAPH_COLLAPSING:1154graph_output_collapsing_line(graph, sb);1155return0;1156}11571158assert(0);1159return0;1160}11611162static voidgraph_padding_line(struct git_graph *graph,struct strbuf *sb)1163{1164int i, j;11651166if(graph->state != GRAPH_COMMIT) {1167graph_next_line(graph, sb);1168return;1169}11701171/*1172 * Output the row containing this commit1173 * Iterate up to and including graph->num_columns,1174 * since the current commit may not be in any of the existing1175 * columns. (This happens when the current commit doesn't have any1176 * children that we have already processed.)1177 */1178for(i =0; i < graph->num_columns; i++) {1179struct column *col = &graph->columns[i];1180struct commit *col_commit = col->commit;1181if(col_commit == graph->commit) {1182strbuf_write_column(sb, col,'|');11831184if(graph->num_parents <3)1185strbuf_addch(sb,' ');1186else{1187int num_spaces = ((graph->num_parents -2) *2);1188for(j =0; j < num_spaces; j++)1189strbuf_addch(sb,' ');1190}1191}else{1192strbuf_write_column(sb, col,'|');1193strbuf_addch(sb,' ');1194}1195}11961197graph_pad_horizontally(graph, sb, graph->num_columns);11981199/*1200 * Update graph->prev_state since we have output a padding line1201 */1202 graph->prev_state = GRAPH_PADDING;1203}12041205intgraph_is_commit_finished(struct git_graph const*graph)1206{1207return(graph->state == GRAPH_PADDING);1208}12091210voidgraph_show_commit(struct git_graph *graph)1211{1212struct strbuf msgbuf = STRBUF_INIT;1213int shown_commit_line =0;12141215if(!graph)1216return;12171218while(!shown_commit_line) {1219 shown_commit_line =graph_next_line(graph, &msgbuf);1220fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout);1221if(!shown_commit_line)1222putchar('\n');1223strbuf_setlen(&msgbuf,0);1224}12251226strbuf_release(&msgbuf);1227}12281229voidgraph_show_oneline(struct git_graph *graph)1230{1231struct strbuf msgbuf = STRBUF_INIT;12321233if(!graph)1234return;12351236graph_next_line(graph, &msgbuf);1237fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout);1238strbuf_release(&msgbuf);1239}12401241voidgraph_show_padding(struct git_graph *graph)1242{1243struct strbuf msgbuf = STRBUF_INIT;12441245if(!graph)1246return;12471248graph_padding_line(graph, &msgbuf);1249fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout);1250strbuf_release(&msgbuf);1251}12521253intgraph_show_remainder(struct git_graph *graph)1254{1255struct strbuf msgbuf = STRBUF_INIT;1256int shown =0;12571258if(!graph)1259return0;12601261if(graph_is_commit_finished(graph))1262return0;12631264for(;;) {1265graph_next_line(graph, &msgbuf);1266fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout);1267strbuf_setlen(&msgbuf,0);1268 shown =1;12691270if(!graph_is_commit_finished(graph))1271putchar('\n');1272else1273break;1274}1275strbuf_release(&msgbuf);12761277return shown;1278}127912801281static voidgraph_show_strbuf(struct git_graph *graph,struct strbuf const*sb)1282{1283char*p;12841285if(!graph) {1286fwrite(sb->buf,sizeof(char), sb->len, stdout);1287return;1288}12891290/*1291 * Print the strbuf line by line,1292 * and display the graph info before each line but the first.1293 */1294 p = sb->buf;1295while(p) {1296size_t len;1297char*next_p =strchr(p,'\n');1298if(next_p) {1299 next_p++;1300 len = next_p - p;1301}else{1302 len = (sb->buf + sb->len) - p;1303}1304fwrite(p,sizeof(char), len, stdout);1305if(next_p && *next_p !='\0')1306graph_show_oneline(graph);1307 p = next_p;1308}1309}13101311voidgraph_show_commit_msg(struct git_graph *graph,1312struct strbuf const*sb)1313{1314int newline_terminated;13151316if(!graph) {1317/*1318 * If there's no graph, just print the message buffer.1319 *1320 * The message buffer for CMIT_FMT_ONELINE and1321 * CMIT_FMT_USERFORMAT are already missing a terminating1322 * newline. All of the other formats should have it.1323 */1324fwrite(sb->buf,sizeof(char), sb->len, stdout);1325return;1326}13271328 newline_terminated = (sb->len && sb->buf[sb->len -1] =='\n');13291330/*1331 * Show the commit message1332 */1333graph_show_strbuf(graph, sb);13341335/*1336 * If there is more output needed for this commit, show it now1337 */1338if(!graph_is_commit_finished(graph)) {1339/*1340 * If sb doesn't have a terminating newline, print one now,1341 * so we can start the remainder of the graph output on a1342 * new line.1343 */1344if(!newline_terminated)1345putchar('\n');13461347graph_show_remainder(graph);13481349/*1350 * If sb ends with a newline, our output should too.1351 */1352if(newline_terminated)1353putchar('\n');1354}1355}