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 const char*column_colors_ansi[] = { 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 GIT_COLOR_RESET, 79}; 80 81#define COLUMN_COLORS_ANSI_MAX (ARRAY_SIZE(column_colors_ansi) - 1) 82 83static const char**column_colors; 84static unsigned short column_colors_max; 85 86voidgraph_set_column_colors(const char**colors,unsigned short colors_max) 87{ 88 column_colors = colors; 89 column_colors_max = colors_max; 90} 91 92static const char*column_get_color_code(unsigned short color) 93{ 94return column_colors[color]; 95} 96 97static voidstrbuf_write_column(struct strbuf *sb,const struct column *c, 98char col_char) 99{ 100if(c->color < column_colors_max) 101strbuf_addstr(sb,column_get_color_code(c->color)); 102strbuf_addch(sb, col_char); 103if(c->color < column_colors_max) 104strbuf_addstr(sb,column_get_color_code(column_colors_max)); 105} 106 107struct git_graph { 108/* 109 * The commit currently being processed 110 */ 111struct commit *commit; 112/* The rev-info used for the current traversal */ 113struct rev_info *revs; 114/* 115 * The number of interesting parents that this commit has. 116 * 117 * Note that this is not the same as the actual number of parents. 118 * This count excludes parents that won't be printed in the graph 119 * output, as determined by graph_is_interesting(). 120 */ 121int num_parents; 122/* 123 * The width of the graph output for this commit. 124 * All rows for this commit are padded to this width, so that 125 * messages printed after the graph output are aligned. 126 */ 127int width; 128/* 129 * The next expansion row to print 130 * when state is GRAPH_PRE_COMMIT 131 */ 132int expansion_row; 133/* 134 * The current output state. 135 * This tells us what kind of line graph_next_line() should output. 136 */ 137enum graph_state state; 138/* 139 * The output state for the previous line of output. 140 * This is primarily used to determine how the first merge line 141 * should appear, based on the last line of the previous commit. 142 */ 143enum graph_state prev_state; 144/* 145 * The index of the column that refers to this commit. 146 * 147 * If none of the incoming columns refer to this commit, 148 * this will be equal to num_columns. 149 */ 150int commit_index; 151/* 152 * The commit_index for the previously displayed commit. 153 * 154 * This is used to determine how the first line of a merge 155 * graph output should appear, based on the last line of the 156 * previous commit. 157 */ 158int prev_commit_index; 159/* 160 * The maximum number of columns that can be stored in the columns 161 * and new_columns arrays. This is also half the number of entries 162 * that can be stored in the mapping and new_mapping arrays. 163 */ 164int column_capacity; 165/* 166 * The number of columns (also called "branch lines" in some places) 167 */ 168int num_columns; 169/* 170 * The number of columns in the new_columns array 171 */ 172int num_new_columns; 173/* 174 * The number of entries in the mapping array 175 */ 176int mapping_size; 177/* 178 * The column state before we output the current commit. 179 */ 180struct column *columns; 181/* 182 * The new column state after we output the current commit. 183 * Only valid when state is GRAPH_COLLAPSING. 184 */ 185struct column *new_columns; 186/* 187 * An array that tracks the current state of each 188 * character in the output line during state GRAPH_COLLAPSING. 189 * Each entry is -1 if this character is empty, or a non-negative 190 * integer if the character contains a branch line. The value of 191 * the integer indicates the target position for this branch line. 192 * (I.e., this array maps the current column positions to their 193 * desired positions.) 194 * 195 * The maximum capacity of this array is always 196 * sizeof(int) * 2 * column_capacity. 197 */ 198int*mapping; 199/* 200 * A temporary array for computing the next mapping state 201 * while we are outputting a mapping line. This is stored as part 202 * of the git_graph simply so we don't have to allocate a new 203 * temporary array each time we have to output a collapsing line. 204 */ 205int*new_mapping; 206/* 207 * The current default column color being used. This is 208 * stored as an index into the array column_colors. 209 */ 210unsigned short default_column_color; 211}; 212 213static struct strbuf *diff_output_prefix_callback(struct diff_options *opt,void*data) 214{ 215struct git_graph *graph = data; 216static struct strbuf msgbuf = STRBUF_INIT; 217 218assert(graph); 219 220strbuf_reset(&msgbuf); 221graph_padding_line(graph, &msgbuf); 222return&msgbuf; 223} 224 225struct git_graph *graph_init(struct rev_info *opt) 226{ 227struct git_graph *graph =xmalloc(sizeof(struct git_graph)); 228 229if(!column_colors) 230graph_set_column_colors(column_colors_ansi, 231 COLUMN_COLORS_ANSI_MAX); 232 233 graph->commit = NULL; 234 graph->revs = opt; 235 graph->num_parents =0; 236 graph->expansion_row =0; 237 graph->state = GRAPH_PADDING; 238 graph->prev_state = GRAPH_PADDING; 239 graph->commit_index =0; 240 graph->prev_commit_index =0; 241 graph->num_columns =0; 242 graph->num_new_columns =0; 243 graph->mapping_size =0; 244/* 245 * Start the column color at the maximum value, since we'll 246 * always increment it for the first commit we output. 247 * This way we start at 0 for the first commit. 248 */ 249 graph->default_column_color = column_colors_max -1; 250 251/* 252 * Allocate a reasonably large default number of columns 253 * We'll automatically grow columns later if we need more room. 254 */ 255 graph->column_capacity =30; 256 graph->columns =xmalloc(sizeof(struct column) * 257 graph->column_capacity); 258 graph->new_columns =xmalloc(sizeof(struct column) * 259 graph->column_capacity); 260 graph->mapping =xmalloc(sizeof(int) *2* graph->column_capacity); 261 graph->new_mapping =xmalloc(sizeof(int) *2* graph->column_capacity); 262 263/* 264 * The diff output prefix callback, with this we can make 265 * all the diff output to align with the graph lines. 266 */ 267 opt->diffopt.output_prefix = diff_output_prefix_callback; 268 opt->diffopt.output_prefix_data = graph; 269 270return graph; 271} 272 273static voidgraph_update_state(struct git_graph *graph,enum graph_state s) 274{ 275 graph->prev_state = graph->state; 276 graph->state = s; 277} 278 279static voidgraph_ensure_capacity(struct git_graph *graph,int num_columns) 280{ 281if(graph->column_capacity >= num_columns) 282return; 283 284do{ 285 graph->column_capacity *=2; 286}while(graph->column_capacity < num_columns); 287 288 graph->columns =xrealloc(graph->columns, 289sizeof(struct column) * 290 graph->column_capacity); 291 graph->new_columns =xrealloc(graph->new_columns, 292sizeof(struct column) * 293 graph->column_capacity); 294 graph->mapping =xrealloc(graph->mapping, 295sizeof(int) *2* graph->column_capacity); 296 graph->new_mapping =xrealloc(graph->new_mapping, 297sizeof(int) *2* graph->column_capacity); 298} 299 300/* 301 * Returns 1 if the commit will be printed in the graph output, 302 * and 0 otherwise. 303 */ 304static intgraph_is_interesting(struct git_graph *graph,struct commit *commit) 305{ 306/* 307 * If revs->boundary is set, commits whose children have 308 * been shown are always interesting, even if they have the 309 * UNINTERESTING or TREESAME flags set. 310 */ 311if(graph->revs && graph->revs->boundary) { 312if(commit->object.flags & CHILD_SHOWN) 313return1; 314} 315 316/* 317 * Otherwise, use get_commit_action() to see if this commit is 318 * interesting 319 */ 320returnget_commit_action(graph->revs, commit) == commit_show; 321} 322 323static struct commit_list *next_interesting_parent(struct git_graph *graph, 324struct commit_list *orig) 325{ 326struct commit_list *list; 327 328/* 329 * If revs->first_parent_only is set, only the first 330 * parent is interesting. None of the others are. 331 */ 332if(graph->revs->first_parent_only) 333return NULL; 334 335/* 336 * Return the next interesting commit after orig 337 */ 338for(list = orig->next; list; list = list->next) { 339if(graph_is_interesting(graph, list->item)) 340return list; 341} 342 343return NULL; 344} 345 346static struct commit_list *first_interesting_parent(struct git_graph *graph) 347{ 348struct commit_list *parents = graph->commit->parents; 349 350/* 351 * If this commit has no parents, ignore it 352 */ 353if(!parents) 354return NULL; 355 356/* 357 * If the first parent is interesting, return it 358 */ 359if(graph_is_interesting(graph, parents->item)) 360return parents; 361 362/* 363 * Otherwise, call next_interesting_parent() to get 364 * the next interesting parent 365 */ 366returnnext_interesting_parent(graph, parents); 367} 368 369static unsigned shortgraph_get_current_column_color(const struct git_graph *graph) 370{ 371if(!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF)) 372return column_colors_max; 373return graph->default_column_color; 374} 375 376/* 377 * Update the graph's default column color. 378 */ 379static voidgraph_increment_column_color(struct git_graph *graph) 380{ 381 graph->default_column_color = (graph->default_column_color +1) % 382 column_colors_max; 383} 384 385static unsigned shortgraph_find_commit_color(const struct git_graph *graph, 386const struct commit *commit) 387{ 388int i; 389for(i =0; i < graph->num_columns; i++) { 390if(graph->columns[i].commit == commit) 391return graph->columns[i].color; 392} 393returngraph_get_current_column_color(graph); 394} 395 396static voidgraph_insert_into_new_columns(struct git_graph *graph, 397struct commit *commit, 398int*mapping_index) 399{ 400int i; 401 402/* 403 * If the commit is already in the new_columns list, we don't need to 404 * add it. Just update the mapping correctly. 405 */ 406for(i =0; i < graph->num_new_columns; i++) { 407if(graph->new_columns[i].commit == commit) { 408 graph->mapping[*mapping_index] = i; 409*mapping_index +=2; 410return; 411} 412} 413 414/* 415 * This commit isn't already in new_columns. Add it. 416 */ 417 graph->new_columns[graph->num_new_columns].commit = commit; 418 graph->new_columns[graph->num_new_columns].color =graph_find_commit_color(graph, commit); 419 graph->mapping[*mapping_index] = graph->num_new_columns; 420*mapping_index +=2; 421 graph->num_new_columns++; 422} 423 424static voidgraph_update_width(struct git_graph *graph, 425int is_commit_in_existing_columns) 426{ 427/* 428 * Compute the width needed to display the graph for this commit. 429 * This is the maximum width needed for any row. All other rows 430 * will be padded to this width. 431 * 432 * Compute the number of columns in the widest row: 433 * Count each existing column (graph->num_columns), and each new 434 * column added by this commit. 435 */ 436int max_cols = graph->num_columns + graph->num_parents; 437 438/* 439 * Even if the current commit has no parents to be printed, it 440 * still takes up a column for itself. 441 */ 442if(graph->num_parents <1) 443 max_cols++; 444 445/* 446 * We added a column for the current commit as part of 447 * graph->num_parents. If the current commit was already in 448 * graph->columns, then we have double counted it. 449 */ 450if(is_commit_in_existing_columns) 451 max_cols--; 452 453/* 454 * Each column takes up 2 spaces 455 */ 456 graph->width = max_cols *2; 457} 458 459static voidgraph_update_columns(struct git_graph *graph) 460{ 461struct commit_list *parent; 462struct column *tmp_columns; 463int max_new_columns; 464int mapping_idx; 465int i, seen_this, is_commit_in_columns; 466 467/* 468 * Swap graph->columns with graph->new_columns 469 * graph->columns contains the state for the previous commit, 470 * and new_columns now contains the state for our commit. 471 * 472 * We'll re-use the old columns array as storage to compute the new 473 * columns list for the commit after this one. 474 */ 475 tmp_columns = graph->columns; 476 graph->columns = graph->new_columns; 477 graph->num_columns = graph->num_new_columns; 478 479 graph->new_columns = tmp_columns; 480 graph->num_new_columns =0; 481 482/* 483 * Now update new_columns and mapping with the information for the 484 * commit after this one. 485 * 486 * First, make sure we have enough room. At most, there will 487 * be graph->num_columns + graph->num_parents columns for the next 488 * commit. 489 */ 490 max_new_columns = graph->num_columns + graph->num_parents; 491graph_ensure_capacity(graph, max_new_columns); 492 493/* 494 * Clear out graph->mapping 495 */ 496 graph->mapping_size =2* max_new_columns; 497for(i =0; i < graph->mapping_size; i++) 498 graph->mapping[i] = -1; 499 500/* 501 * Populate graph->new_columns and graph->mapping 502 * 503 * Some of the parents of this commit may already be in 504 * graph->columns. If so, graph->new_columns should only contain a 505 * single entry for each such commit. graph->mapping should 506 * contain information about where each current branch line is 507 * supposed to end up after the collapsing is performed. 508 */ 509 seen_this =0; 510 mapping_idx =0; 511 is_commit_in_columns =1; 512for(i =0; i <= graph->num_columns; i++) { 513struct commit *col_commit; 514if(i == graph->num_columns) { 515if(seen_this) 516break; 517 is_commit_in_columns =0; 518 col_commit = graph->commit; 519}else{ 520 col_commit = graph->columns[i].commit; 521} 522 523if(col_commit == graph->commit) { 524int old_mapping_idx = mapping_idx; 525 seen_this =1; 526 graph->commit_index = i; 527for(parent =first_interesting_parent(graph); 528 parent; 529 parent =next_interesting_parent(graph, parent)) { 530/* 531 * If this is a merge, or the start of a new 532 * childless column, increment the current 533 * color. 534 */ 535if(graph->num_parents >1|| 536!is_commit_in_columns) { 537graph_increment_column_color(graph); 538} 539graph_insert_into_new_columns(graph, 540 parent->item, 541&mapping_idx); 542} 543/* 544 * We always need to increment mapping_idx by at 545 * least 2, even if it has no interesting parents. 546 * The current commit always takes up at least 2 547 * spaces. 548 */ 549if(mapping_idx == old_mapping_idx) 550 mapping_idx +=2; 551}else{ 552graph_insert_into_new_columns(graph, col_commit, 553&mapping_idx); 554} 555} 556 557/* 558 * Shrink mapping_size to be the minimum necessary 559 */ 560while(graph->mapping_size >1&& 561 graph->mapping[graph->mapping_size -1] <0) 562 graph->mapping_size--; 563 564/* 565 * Compute graph->width for this commit 566 */ 567graph_update_width(graph, is_commit_in_columns); 568} 569 570voidgraph_update(struct git_graph *graph,struct commit *commit) 571{ 572struct commit_list *parent; 573 574/* 575 * Set the new commit 576 */ 577 graph->commit = commit; 578 579/* 580 * Count how many interesting parents this commit has 581 */ 582 graph->num_parents =0; 583for(parent =first_interesting_parent(graph); 584 parent; 585 parent =next_interesting_parent(graph, parent)) 586{ 587 graph->num_parents++; 588} 589 590/* 591 * Store the old commit_index in prev_commit_index. 592 * graph_update_columns() will update graph->commit_index for this 593 * commit. 594 */ 595 graph->prev_commit_index = graph->commit_index; 596 597/* 598 * Call graph_update_columns() to update 599 * columns, new_columns, and mapping. 600 */ 601graph_update_columns(graph); 602 603 graph->expansion_row =0; 604 605/* 606 * Update graph->state. 607 * Note that we don't call graph_update_state() here, since 608 * we don't want to update graph->prev_state. No line for 609 * graph->state was ever printed. 610 * 611 * If the previous commit didn't get to the GRAPH_PADDING state, 612 * it never finished its output. Goto GRAPH_SKIP, to print out 613 * a line to indicate that portion of the graph is missing. 614 * 615 * If there are 3 or more parents, we may need to print extra rows 616 * before the commit, to expand the branch lines around it and make 617 * room for it. We need to do this only if there is a branch row 618 * (or more) to the right of this commit. 619 * 620 * If there are less than 3 parents, we can immediately print the 621 * commit line. 622 */ 623if(graph->state != GRAPH_PADDING) 624 graph->state = GRAPH_SKIP; 625else if(graph->num_parents >=3&& 626 graph->commit_index < (graph->num_columns -1)) 627 graph->state = GRAPH_PRE_COMMIT; 628else 629 graph->state = GRAPH_COMMIT; 630} 631 632static intgraph_is_mapping_correct(struct git_graph *graph) 633{ 634int i; 635 636/* 637 * The mapping is up to date if each entry is at its target, 638 * or is 1 greater than its target. 639 * (If it is 1 greater than the target, '/' will be printed, so it 640 * will look correct on the next row.) 641 */ 642for(i =0; i < graph->mapping_size; i++) { 643int target = graph->mapping[i]; 644if(target <0) 645continue; 646if(target == (i /2)) 647continue; 648return0; 649} 650 651return1; 652} 653 654static voidgraph_pad_horizontally(struct git_graph *graph,struct strbuf *sb, 655int chars_written) 656{ 657/* 658 * Add additional spaces to the end of the strbuf, so that all 659 * lines for a particular commit have the same width. 660 * 661 * This way, fields printed to the right of the graph will remain 662 * aligned for the entire commit. 663 */ 664int extra; 665if(chars_written >= graph->width) 666return; 667 668 extra = graph->width - chars_written; 669strbuf_addf(sb,"%*s", (int) extra,""); 670} 671 672static voidgraph_output_padding_line(struct git_graph *graph, 673struct strbuf *sb) 674{ 675int i; 676 677/* 678 * We could conceivable be called with a NULL commit 679 * if our caller has a bug, and invokes graph_next_line() 680 * immediately after graph_init(), without first calling 681 * graph_update(). Return without outputting anything in this 682 * case. 683 */ 684if(!graph->commit) 685return; 686 687/* 688 * Output a padding row, that leaves all branch lines unchanged 689 */ 690for(i =0; i < graph->num_new_columns; i++) { 691strbuf_write_column(sb, &graph->new_columns[i],'|'); 692strbuf_addch(sb,' '); 693} 694 695graph_pad_horizontally(graph, sb, graph->num_new_columns *2); 696} 697 698static voidgraph_output_skip_line(struct git_graph *graph,struct strbuf *sb) 699{ 700/* 701 * Output an ellipsis to indicate that a portion 702 * of the graph is missing. 703 */ 704strbuf_addstr(sb,"..."); 705graph_pad_horizontally(graph, sb,3); 706 707if(graph->num_parents >=3&& 708 graph->commit_index < (graph->num_columns -1)) 709graph_update_state(graph, GRAPH_PRE_COMMIT); 710else 711graph_update_state(graph, GRAPH_COMMIT); 712} 713 714static voidgraph_output_pre_commit_line(struct git_graph *graph, 715struct strbuf *sb) 716{ 717int num_expansion_rows; 718int i, seen_this; 719int chars_written; 720 721/* 722 * This function formats a row that increases the space around a commit 723 * with multiple parents, to make room for it. It should only be 724 * called when there are 3 or more parents. 725 * 726 * We need 2 extra rows for every parent over 2. 727 */ 728assert(graph->num_parents >=3); 729 num_expansion_rows = (graph->num_parents -2) *2; 730 731/* 732 * graph->expansion_row tracks the current expansion row we are on. 733 * It should be in the range [0, num_expansion_rows - 1] 734 */ 735assert(0<= graph->expansion_row && 736 graph->expansion_row < num_expansion_rows); 737 738/* 739 * Output the row 740 */ 741 seen_this =0; 742 chars_written =0; 743for(i =0; i < graph->num_columns; i++) { 744struct column *col = &graph->columns[i]; 745if(col->commit == graph->commit) { 746 seen_this =1; 747strbuf_write_column(sb, col,'|'); 748strbuf_addf(sb,"%*s", graph->expansion_row,""); 749 chars_written +=1+ graph->expansion_row; 750}else if(seen_this && (graph->expansion_row ==0)) { 751/* 752 * This is the first line of the pre-commit output. 753 * If the previous commit was a merge commit and 754 * ended in the GRAPH_POST_MERGE state, all branch 755 * lines after graph->prev_commit_index were 756 * printed as "\" on the previous line. Continue 757 * to print them as "\" on this line. Otherwise, 758 * print the branch lines as "|". 759 */ 760if(graph->prev_state == GRAPH_POST_MERGE && 761 graph->prev_commit_index < i) 762strbuf_write_column(sb, col,'\\'); 763else 764strbuf_write_column(sb, col,'|'); 765 chars_written++; 766}else if(seen_this && (graph->expansion_row >0)) { 767strbuf_write_column(sb, col,'\\'); 768 chars_written++; 769}else{ 770strbuf_write_column(sb, col,'|'); 771 chars_written++; 772} 773strbuf_addch(sb,' '); 774 chars_written++; 775} 776 777graph_pad_horizontally(graph, sb, chars_written); 778 779/* 780 * Increment graph->expansion_row, 781 * and move to state GRAPH_COMMIT if necessary 782 */ 783 graph->expansion_row++; 784if(graph->expansion_row >= num_expansion_rows) 785graph_update_state(graph, GRAPH_COMMIT); 786} 787 788static voidgraph_output_commit_char(struct git_graph *graph,struct strbuf *sb) 789{ 790/* 791 * For boundary commits, print 'o' 792 * (We should only see boundary commits when revs->boundary is set.) 793 */ 794if(graph->commit->object.flags & BOUNDARY) { 795assert(graph->revs->boundary); 796strbuf_addch(sb,'o'); 797return; 798} 799 800/* 801 * get_revision_mark() handles all other cases without assert() 802 */ 803strbuf_addstr(sb,get_revision_mark(graph->revs, graph->commit)); 804} 805 806/* 807 * Draw an octopus merge and return the number of characters written. 808 */ 809static intgraph_draw_octopus_merge(struct git_graph *graph, 810struct strbuf *sb) 811{ 812/* 813 * Here dashless_commits represents the number of parents 814 * which don't need to have dashes (because their edges fit 815 * neatly under the commit). 816 */ 817const int dashless_commits =2; 818int col_num, i; 819int num_dashes = 820((graph->num_parents - dashless_commits) *2) -1; 821for(i =0; i < num_dashes; i++) { 822 col_num = (i /2) + dashless_commits; 823strbuf_write_column(sb, &graph->new_columns[col_num],'-'); 824} 825 col_num = (i /2) + dashless_commits; 826strbuf_write_column(sb, &graph->new_columns[col_num],'.'); 827return num_dashes +1; 828} 829 830static voidgraph_output_commit_line(struct git_graph *graph,struct strbuf *sb) 831{ 832int seen_this =0; 833int i, chars_written; 834 835/* 836 * Output the row containing this commit 837 * Iterate up to and including graph->num_columns, 838 * since the current commit may not be in any of the existing 839 * columns. (This happens when the current commit doesn't have any 840 * children that we have already processed.) 841 */ 842 seen_this =0; 843 chars_written =0; 844for(i =0; i <= graph->num_columns; i++) { 845struct column *col = &graph->columns[i]; 846struct commit *col_commit; 847if(i == graph->num_columns) { 848if(seen_this) 849break; 850 col_commit = graph->commit; 851}else{ 852 col_commit = graph->columns[i].commit; 853} 854 855if(col_commit == graph->commit) { 856 seen_this =1; 857graph_output_commit_char(graph, sb); 858 chars_written++; 859 860if(graph->num_parents >2) 861 chars_written +=graph_draw_octopus_merge(graph, 862 sb); 863}else if(seen_this && (graph->num_parents >2)) { 864strbuf_write_column(sb, col,'\\'); 865 chars_written++; 866}else if(seen_this && (graph->num_parents ==2)) { 867/* 868 * This is a 2-way merge commit. 869 * There is no GRAPH_PRE_COMMIT stage for 2-way 870 * merges, so this is the first line of output 871 * for this commit. Check to see what the previous 872 * line of output was. 873 * 874 * If it was GRAPH_POST_MERGE, the branch line 875 * coming into this commit may have been '\', 876 * and not '|' or '/'. If so, output the branch 877 * line as '\' on this line, instead of '|'. This 878 * makes the output look nicer. 879 */ 880if(graph->prev_state == GRAPH_POST_MERGE && 881 graph->prev_commit_index < i) 882strbuf_write_column(sb, col,'\\'); 883else 884strbuf_write_column(sb, col,'|'); 885 chars_written++; 886}else{ 887strbuf_write_column(sb, col,'|'); 888 chars_written++; 889} 890strbuf_addch(sb,' '); 891 chars_written++; 892} 893 894graph_pad_horizontally(graph, sb, chars_written); 895 896/* 897 * Update graph->state 898 */ 899if(graph->num_parents >1) 900graph_update_state(graph, GRAPH_POST_MERGE); 901else if(graph_is_mapping_correct(graph)) 902graph_update_state(graph, GRAPH_PADDING); 903else 904graph_update_state(graph, GRAPH_COLLAPSING); 905} 906 907static struct column *find_new_column_by_commit(struct git_graph *graph, 908struct commit *commit) 909{ 910int i; 911for(i =0; i < graph->num_new_columns; i++) { 912if(graph->new_columns[i].commit == commit) 913return&graph->new_columns[i]; 914} 915return NULL; 916} 917 918static voidgraph_output_post_merge_line(struct git_graph *graph,struct strbuf *sb) 919{ 920int seen_this =0; 921int i, j, chars_written; 922 923/* 924 * Output the post-merge row 925 */ 926 chars_written =0; 927for(i =0; i <= graph->num_columns; i++) { 928struct column *col = &graph->columns[i]; 929struct commit *col_commit; 930if(i == graph->num_columns) { 931if(seen_this) 932break; 933 col_commit = graph->commit; 934}else{ 935 col_commit = col->commit; 936} 937 938if(col_commit == graph->commit) { 939/* 940 * Since the current commit is a merge find 941 * the columns for the parent commits in 942 * new_columns and use those to format the 943 * edges. 944 */ 945struct commit_list *parents = NULL; 946struct column *par_column; 947 seen_this =1; 948 parents =first_interesting_parent(graph); 949assert(parents); 950 par_column =find_new_column_by_commit(graph, parents->item); 951assert(par_column); 952 953strbuf_write_column(sb, par_column,'|'); 954 chars_written++; 955for(j =0; j < graph->num_parents -1; j++) { 956 parents =next_interesting_parent(graph, parents); 957assert(parents); 958 par_column =find_new_column_by_commit(graph, parents->item); 959assert(par_column); 960strbuf_write_column(sb, par_column,'\\'); 961strbuf_addch(sb,' '); 962} 963 chars_written += j *2; 964}else if(seen_this) { 965strbuf_write_column(sb, col,'\\'); 966strbuf_addch(sb,' '); 967 chars_written +=2; 968}else{ 969strbuf_write_column(sb, col,'|'); 970strbuf_addch(sb,' '); 971 chars_written +=2; 972} 973} 974 975graph_pad_horizontally(graph, sb, chars_written); 976 977/* 978 * Update graph->state 979 */ 980if(graph_is_mapping_correct(graph)) 981graph_update_state(graph, GRAPH_PADDING); 982else 983graph_update_state(graph, GRAPH_COLLAPSING); 984} 985 986static voidgraph_output_collapsing_line(struct git_graph *graph,struct strbuf *sb) 987{ 988int i; 989int*tmp_mapping; 990short used_horizontal =0; 991int horizontal_edge = -1; 992int horizontal_edge_target = -1; 993 994/* 995 * Clear out the new_mapping array 996 */ 997for(i =0; i < graph->mapping_size; i++) 998 graph->new_mapping[i] = -1; 9991000for(i =0; i < graph->mapping_size; i++) {1001int target = graph->mapping[i];1002if(target <0)1003continue;10041005/*1006 * Since update_columns() always inserts the leftmost1007 * column first, each branch's target location should1008 * always be either its current location or to the left of1009 * its current location.1010 *1011 * We never have to move branches to the right. This makes1012 * the graph much more legible, since whenever branches1013 * cross, only one is moving directions.1014 */1015assert(target *2<= i);10161017if(target *2== i) {1018/*1019 * This column is already in the1020 * correct place1021 */1022assert(graph->new_mapping[i] == -1);1023 graph->new_mapping[i] = target;1024}else if(graph->new_mapping[i -1] <0) {1025/*1026 * Nothing is to the left.1027 * Move to the left by one1028 */1029 graph->new_mapping[i -1] = target;1030/*1031 * If there isn't already an edge moving horizontally1032 * select this one.1033 */1034if(horizontal_edge == -1) {1035int j;1036 horizontal_edge = i;1037 horizontal_edge_target = target;1038/*1039 * The variable target is the index of the graph1040 * column, and therefore target*2+3 is the1041 * actual screen column of the first horizontal1042 * line.1043 */1044for(j = (target *2)+3; j < (i -2); j +=2)1045 graph->new_mapping[j] = target;1046}1047}else if(graph->new_mapping[i -1] == target) {1048/*1049 * There is a branch line to our left1050 * already, and it is our target. We1051 * combine with this line, since we share1052 * the same parent commit.1053 *1054 * We don't have to add anything to the1055 * output or new_mapping, since the1056 * existing branch line has already taken1057 * care of it.1058 */1059}else{1060/*1061 * There is a branch line to our left,1062 * but it isn't our target. We need to1063 * cross over it.1064 *1065 * The space just to the left of this1066 * branch should always be empty.1067 *1068 * The branch to the left of that space1069 * should be our eventual target.1070 */1071assert(graph->new_mapping[i -1] > target);1072assert(graph->new_mapping[i -2] <0);1073assert(graph->new_mapping[i -3] == target);1074 graph->new_mapping[i -2] = target;1075/*1076 * Mark this branch as the horizontal edge to1077 * prevent any other edges from moving1078 * horizontally.1079 */1080if(horizontal_edge == -1)1081 horizontal_edge = i;1082}1083}10841085/*1086 * The new mapping may be 1 smaller than the old mapping1087 */1088if(graph->new_mapping[graph->mapping_size -1] <0)1089 graph->mapping_size--;10901091/*1092 * Output out a line based on the new mapping info1093 */1094for(i =0; i < graph->mapping_size; i++) {1095int target = graph->new_mapping[i];1096if(target <0)1097strbuf_addch(sb,' ');1098else if(target *2== i)1099strbuf_write_column(sb, &graph->new_columns[target],'|');1100else if(target == horizontal_edge_target &&1101 i != horizontal_edge -1) {1102/*1103 * Set the mappings for all but the1104 * first segment to -1 so that they1105 * won't continue into the next line.1106 */1107if(i != (target *2)+3)1108 graph->new_mapping[i] = -1;1109 used_horizontal =1;1110strbuf_write_column(sb, &graph->new_columns[target],'_');1111}else{1112if(used_horizontal && i < horizontal_edge)1113 graph->new_mapping[i] = -1;1114strbuf_write_column(sb, &graph->new_columns[target],'/');11151116}1117}11181119graph_pad_horizontally(graph, sb, graph->mapping_size);11201121/*1122 * Swap mapping and new_mapping1123 */1124 tmp_mapping = graph->mapping;1125 graph->mapping = graph->new_mapping;1126 graph->new_mapping = tmp_mapping;11271128/*1129 * If graph->mapping indicates that all of the branch lines1130 * are already in the correct positions, we are done.1131 * Otherwise, we need to collapse some branch lines together.1132 */1133if(graph_is_mapping_correct(graph))1134graph_update_state(graph, GRAPH_PADDING);1135}11361137intgraph_next_line(struct git_graph *graph,struct strbuf *sb)1138{1139switch(graph->state) {1140case GRAPH_PADDING:1141graph_output_padding_line(graph, sb);1142return0;1143case GRAPH_SKIP:1144graph_output_skip_line(graph, sb);1145return0;1146case GRAPH_PRE_COMMIT:1147graph_output_pre_commit_line(graph, sb);1148return0;1149case GRAPH_COMMIT:1150graph_output_commit_line(graph, sb);1151return1;1152case GRAPH_POST_MERGE:1153graph_output_post_merge_line(graph, sb);1154return0;1155case GRAPH_COLLAPSING:1156graph_output_collapsing_line(graph, sb);1157return0;1158}11591160assert(0);1161return0;1162}11631164static voidgraph_padding_line(struct git_graph *graph,struct strbuf *sb)1165{1166int i, j;11671168if(graph->state != GRAPH_COMMIT) {1169graph_next_line(graph, sb);1170return;1171}11721173/*1174 * Output the row containing this commit1175 * Iterate up to and including graph->num_columns,1176 * since the current commit may not be in any of the existing1177 * columns. (This happens when the current commit doesn't have any1178 * children that we have already processed.)1179 */1180for(i =0; i < graph->num_columns; i++) {1181struct column *col = &graph->columns[i];1182struct commit *col_commit = col->commit;1183if(col_commit == graph->commit) {1184strbuf_write_column(sb, col,'|');11851186if(graph->num_parents <3)1187strbuf_addch(sb,' ');1188else{1189int num_spaces = ((graph->num_parents -2) *2);1190for(j =0; j < num_spaces; j++)1191strbuf_addch(sb,' ');1192}1193}else{1194strbuf_write_column(sb, col,'|');1195strbuf_addch(sb,' ');1196}1197}11981199graph_pad_horizontally(graph, sb, graph->num_columns);12001201/*1202 * Update graph->prev_state since we have output a padding line1203 */1204 graph->prev_state = GRAPH_PADDING;1205}12061207intgraph_is_commit_finished(struct git_graph const*graph)1208{1209return(graph->state == GRAPH_PADDING);1210}12111212voidgraph_show_commit(struct git_graph *graph)1213{1214struct strbuf msgbuf = STRBUF_INIT;1215int shown_commit_line =0;12161217if(!graph)1218return;12191220while(!shown_commit_line) {1221 shown_commit_line =graph_next_line(graph, &msgbuf);1222fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout);1223if(!shown_commit_line)1224putchar('\n');1225strbuf_setlen(&msgbuf,0);1226}12271228strbuf_release(&msgbuf);1229}12301231voidgraph_show_oneline(struct git_graph *graph)1232{1233struct strbuf msgbuf = STRBUF_INIT;12341235if(!graph)1236return;12371238graph_next_line(graph, &msgbuf);1239fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout);1240strbuf_release(&msgbuf);1241}12421243voidgraph_show_padding(struct git_graph *graph)1244{1245struct strbuf msgbuf = STRBUF_INIT;12461247if(!graph)1248return;12491250graph_padding_line(graph, &msgbuf);1251fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout);1252strbuf_release(&msgbuf);1253}12541255intgraph_show_remainder(struct git_graph *graph)1256{1257struct strbuf msgbuf = STRBUF_INIT;1258int shown =0;12591260if(!graph)1261return0;12621263if(graph_is_commit_finished(graph))1264return0;12651266for(;;) {1267graph_next_line(graph, &msgbuf);1268fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout);1269strbuf_setlen(&msgbuf,0);1270 shown =1;12711272if(!graph_is_commit_finished(graph))1273putchar('\n');1274else1275break;1276}1277strbuf_release(&msgbuf);12781279return shown;1280}128112821283static voidgraph_show_strbuf(struct git_graph *graph,struct strbuf const*sb)1284{1285char*p;12861287if(!graph) {1288fwrite(sb->buf,sizeof(char), sb->len, stdout);1289return;1290}12911292/*1293 * Print the strbuf line by line,1294 * and display the graph info before each line but the first.1295 */1296 p = sb->buf;1297while(p) {1298size_t len;1299char*next_p =strchr(p,'\n');1300if(next_p) {1301 next_p++;1302 len = next_p - p;1303}else{1304 len = (sb->buf + sb->len) - p;1305}1306fwrite(p,sizeof(char), len, stdout);1307if(next_p && *next_p !='\0')1308graph_show_oneline(graph);1309 p = next_p;1310}1311}13121313voidgraph_show_commit_msg(struct git_graph *graph,1314struct strbuf const*sb)1315{1316int newline_terminated;13171318if(!graph) {1319/*1320 * If there's no graph, just print the message buffer.1321 *1322 * The message buffer for CMIT_FMT_ONELINE and1323 * CMIT_FMT_USERFORMAT are already missing a terminating1324 * newline. All of the other formats should have it.1325 */1326fwrite(sb->buf,sizeof(char), sb->len, stdout);1327return;1328}13291330 newline_terminated = (sb->len && sb->buf[sb->len -1] =='\n');13311332/*1333 * Show the commit message1334 */1335graph_show_strbuf(graph, sb);13361337/*1338 * If there is more output needed for this commit, show it now1339 */1340if(!graph_is_commit_finished(graph)) {1341/*1342 * If sb doesn't have a terminating newline, print one now,1343 * so we can start the remainder of the graph output on a1344 * new line.1345 */1346if(!newline_terminated)1347putchar('\n');13481349graph_show_remainder(graph);13501351/*1352 * If sb ends with a newline, our output should too.1353 */1354if(newline_terminated)1355putchar('\n');1356}1357}