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