1#include"cache.h" 2#include"commit.h" 3#include"graph.h" 4#include"diff.h" 5#include"revision.h" 6 7/* 8 * TODO: 9 * - Add colors to the graph. 10 * Pick a color for each column, and print all characters 11 * in that column with the specified color. 12 * 13 * - Limit the number of columns, similar to the way gitk does. 14 * If we reach more than a specified number of columns, omit 15 * sections of some columns. 16 * 17 * - The output during the GRAPH_PRE_COMMIT and GRAPH_COLLAPSING states 18 * could be made more compact by printing horizontal lines, instead of 19 * long diagonal lines. For example, during collapsing, something like 20 * this: instead of this: 21 * | | | | | | | | | | 22 * | |_|_|/ | | | |/ 23 * |/| | | | | |/| 24 * | | | | | |/| | 25 * |/| | | 26 * | | | | 27 * 28 * If there are several parallel diagonal lines, they will need to be 29 * replaced with horizontal lines on subsequent rows. 30 */ 31 32struct column { 33/* 34 * The parent commit of this column. 35 */ 36struct commit *commit; 37/* 38 * XXX: Once we add support for colors, struct column could also 39 * contain the color of its branch line. 40 */ 41}; 42 43enum graph_state { 44 GRAPH_PADDING, 45 GRAPH_SKIP, 46 GRAPH_PRE_COMMIT, 47 GRAPH_COMMIT, 48 GRAPH_POST_MERGE, 49 GRAPH_COLLAPSING 50}; 51 52struct git_graph { 53/* 54 * The commit currently being processed 55 */ 56struct commit *commit; 57/* 58 * The number of parents this commit has. 59 * (Stored so we don't have to walk over them each time we need 60 * this number) 61 */ 62int num_parents; 63/* 64 * The width of the graph output for this commit. 65 * All rows for this commit are padded to this width, so that 66 * messages printed after the graph output are aligned. 67 */ 68int width; 69/* 70 * The next expansion row to print 71 * when state is GRAPH_PRE_COMMIT 72 */ 73int expansion_row; 74/* 75 * The current output state. 76 * This tells us what kind of line graph_next_line() should output. 77 */ 78enum graph_state state; 79/* 80 * The maximum number of columns that can be stored in the columns 81 * and new_columns arrays. This is also half the number of entries 82 * that can be stored in the mapping and new_mapping arrays. 83 */ 84int column_capacity; 85/* 86 * The number of columns (also called "branch lines" in some places) 87 */ 88int num_columns; 89/* 90 * The number of columns in the new_columns array 91 */ 92int num_new_columns; 93/* 94 * The number of entries in the mapping array 95 */ 96int mapping_size; 97/* 98 * The column state before we output the current commit. 99 */ 100struct column *columns; 101/* 102 * The new column state after we output the current commit. 103 * Only valid when state is GRAPH_COLLAPSING. 104 */ 105struct column *new_columns; 106/* 107 * An array that tracks the current state of each 108 * character in the output line during state GRAPH_COLLAPSING. 109 * Each entry is -1 if this character is empty, or a non-negative 110 * integer if the character contains a branch line. The value of 111 * the integer indicates the target position for this branch line. 112 * (I.e., this array maps the current column positions to their 113 * desired positions.) 114 * 115 * The maximum capacity of this array is always 116 * sizeof(int) * 2 * column_capacity. 117 */ 118int*mapping; 119/* 120 * A temporary array for computing the next mapping state 121 * while we are outputting a mapping line. This is stored as part 122 * of the git_graph simply so we don't have to allocate a new 123 * temporary array each time we have to output a collapsing line. 124 */ 125int*new_mapping; 126}; 127 128struct git_graph *graph_init(void) 129{ 130struct git_graph *graph =xmalloc(sizeof(struct git_graph)); 131 graph->commit = NULL; 132 graph->num_parents =0; 133 graph->expansion_row =0; 134 graph->state = GRAPH_PADDING; 135 graph->num_columns =0; 136 graph->num_new_columns =0; 137 graph->mapping_size =0; 138 139/* 140 * Allocate a reasonably large default number of columns 141 * We'll automatically grow columns later if we need more room. 142 */ 143 graph->column_capacity =30; 144 graph->columns =xmalloc(sizeof(struct column) * 145 graph->column_capacity); 146 graph->new_columns =xmalloc(sizeof(struct column) * 147 graph->column_capacity); 148 graph->mapping =xmalloc(sizeof(int) *2* graph->column_capacity); 149 graph->new_mapping =xmalloc(sizeof(int) *2* graph->column_capacity); 150 151return graph; 152} 153 154voidgraph_release(struct git_graph *graph) 155{ 156free(graph->columns); 157free(graph->new_columns); 158free(graph->mapping); 159free(graph); 160} 161 162static voidgraph_ensure_capacity(struct git_graph *graph,int num_columns) 163{ 164if(graph->column_capacity >= num_columns) 165return; 166 167do{ 168 graph->column_capacity *=2; 169}while(graph->column_capacity < num_columns); 170 171 graph->columns =xrealloc(graph->columns, 172sizeof(struct column) * 173 graph->column_capacity); 174 graph->new_columns =xrealloc(graph->new_columns, 175sizeof(struct column) * 176 graph->column_capacity); 177 graph->mapping =xrealloc(graph->mapping, 178sizeof(int) *2* graph->column_capacity); 179 graph->new_mapping =xrealloc(graph->new_mapping, 180sizeof(int) *2* graph->column_capacity); 181} 182 183static voidgraph_insert_into_new_columns(struct git_graph *graph, 184struct commit *commit, 185int*mapping_index) 186{ 187int i; 188 189/* 190 * Ignore uinteresting and pruned commits 191 */ 192if(commit->object.flags & (UNINTERESTING | TREESAME)) 193{ 194*mapping_index +=2; 195return; 196} 197 198/* 199 * If the commit is already in the new_columns list, we don't need to 200 * add it. Just update the mapping correctly. 201 */ 202for(i =0; i < graph->num_new_columns; i++) { 203if(graph->new_columns[i].commit == commit) { 204 graph->mapping[*mapping_index] = i; 205*mapping_index +=2; 206return; 207} 208} 209 210/* 211 * This commit isn't already in new_columns. Add it. 212 */ 213 graph->new_columns[graph->num_new_columns].commit = commit; 214 graph->mapping[*mapping_index] = graph->num_new_columns; 215*mapping_index +=2; 216 graph->num_new_columns++; 217} 218 219static voidgraph_update_width(struct git_graph *graph, 220int is_commit_in_existing_columns) 221{ 222/* 223 * Compute the width needed to display the graph for this commit. 224 * This is the maximum width needed for any row. All other rows 225 * will be padded to this width. 226 * 227 * Compute the number of columns in the widest row: 228 * Count each existing column (graph->num_columns), and each new 229 * column added by this commit. 230 */ 231int max_cols = graph->num_columns + graph->num_parents; 232 233/* 234 * Even if the current commit has no parents, it still takes up a 235 * column for itself. 236 */ 237if(graph->num_parents <1) 238 max_cols++; 239 240/* 241 * We added a column for the the current commit as part of 242 * graph->num_parents. If the current commit was already in 243 * graph->columns, then we have double counted it. 244 */ 245if(is_commit_in_existing_columns) 246 max_cols--; 247 248/* 249 * Each column takes up 2 spaces 250 */ 251 graph->width = max_cols *2; 252} 253 254static voidgraph_update_columns(struct git_graph *graph) 255{ 256struct commit_list *parent; 257struct column *tmp_columns; 258int max_new_columns; 259int mapping_idx; 260int i, seen_this, is_commit_in_columns; 261 262/* 263 * Swap graph->columns with graph->new_columns 264 * graph->columns contains the state for the previous commit, 265 * and new_columns now contains the state for our commit. 266 * 267 * We'll re-use the old columns array as storage to compute the new 268 * columns list for the commit after this one. 269 */ 270 tmp_columns = graph->columns; 271 graph->columns = graph->new_columns; 272 graph->num_columns = graph->num_new_columns; 273 274 graph->new_columns = tmp_columns; 275 graph->num_new_columns =0; 276 277/* 278 * Now update new_columns and mapping with the information for the 279 * commit after this one. 280 * 281 * First, make sure we have enough room. At most, there will 282 * be graph->num_columns + graph->num_parents columns for the next 283 * commit. 284 */ 285 max_new_columns = graph->num_columns + graph->num_parents; 286graph_ensure_capacity(graph, max_new_columns); 287 288/* 289 * Clear out graph->mapping 290 */ 291 graph->mapping_size =2* max_new_columns; 292for(i =0; i < graph->mapping_size; i++) 293 graph->mapping[i] = -1; 294 295/* 296 * Populate graph->new_columns and graph->mapping 297 * 298 * Some of the parents of this commit may already be in 299 * graph->columns. If so, graph->new_columns should only contain a 300 * single entry for each such commit. graph->mapping should 301 * contain information about where each current branch line is 302 * supposed to end up after the collapsing is performed. 303 */ 304 seen_this =0; 305 mapping_idx =0; 306 is_commit_in_columns =1; 307for(i =0; i <= graph->num_columns; i++) { 308struct commit *col_commit; 309if(i == graph->num_columns) { 310if(seen_this) 311break; 312 is_commit_in_columns =0; 313 col_commit = graph->commit; 314}else{ 315 col_commit = graph->columns[i].commit; 316} 317 318if(col_commit == graph->commit) { 319 seen_this =1; 320for(parent = graph->commit->parents; 321 parent; 322 parent = parent->next) { 323graph_insert_into_new_columns(graph, 324 parent->item, 325&mapping_idx); 326} 327}else{ 328graph_insert_into_new_columns(graph, col_commit, 329&mapping_idx); 330} 331} 332 333/* 334 * Shrink mapping_size to be the minimum necessary 335 */ 336while(graph->mapping_size >1&& 337 graph->mapping[graph->mapping_size -1] <0) 338 graph->mapping_size--; 339 340/* 341 * Compute graph->width for this commit 342 */ 343graph_update_width(graph, is_commit_in_columns); 344} 345 346voidgraph_update(struct git_graph *graph,struct commit *commit) 347{ 348struct commit_list *parent; 349 350/* 351 * Set the new commit 352 */ 353 graph->commit = commit; 354 355/* 356 * Count how many parents this commit has 357 */ 358 graph->num_parents =0; 359for(parent = commit->parents; parent; parent = parent->next) 360 graph->num_parents++; 361 362/* 363 * Call graph_update_columns() to update 364 * columns, new_columns, and mapping. 365 */ 366graph_update_columns(graph); 367 368 graph->expansion_row =0; 369 370/* 371 * Update graph->state. 372 * 373 * If the previous commit didn't get to the GRAPH_PADDING state, 374 * it never finished its output. Goto GRAPH_SKIP, to print out 375 * a line to indicate that portion of the graph is missing. 376 * 377 * Otherwise, if there are 3 or more parents, we need to print 378 * extra rows before the commit, to expand the branch lines around 379 * it and make room for it. 380 * 381 * If there are less than 3 parents, we can immediately print the 382 * commit line. 383 */ 384if(graph->state != GRAPH_PADDING) 385 graph->state = GRAPH_SKIP; 386else if(graph->num_parents >=3) 387 graph->state = GRAPH_PRE_COMMIT; 388else 389 graph->state = GRAPH_COMMIT; 390} 391 392static intgraph_is_mapping_correct(struct git_graph *graph) 393{ 394int i; 395 396/* 397 * The mapping is up to date if each entry is at its target, 398 * or is 1 greater than its target. 399 * (If it is 1 greater than the target, '/' will be printed, so it 400 * will look correct on the next row.) 401 */ 402for(i =0; i < graph->mapping_size; i++) { 403int target = graph->mapping[i]; 404if(target <0) 405continue; 406if(target == (i /2)) 407continue; 408return0; 409} 410 411return1; 412} 413 414static voidgraph_pad_horizontally(struct git_graph *graph,struct strbuf *sb) 415{ 416/* 417 * Add additional spaces to the end of the strbuf, so that all 418 * lines for a particular commit have the same width. 419 * 420 * This way, fields printed to the right of the graph will remain 421 * aligned for the entire commit. 422 */ 423int extra; 424if(sb->len >= graph->width) 425return; 426 427 extra = graph->width - sb->len; 428strbuf_addf(sb,"%*s", (int) extra,""); 429} 430 431static voidgraph_output_padding_line(struct git_graph *graph, 432struct strbuf *sb) 433{ 434int i; 435 436/* 437 * We could conceivable be called with a NULL commit 438 * if our caller has a bug, and invokes graph_next_line() 439 * immediately after graph_init(), without first calling 440 * graph_update(). Return without outputting anything in this 441 * case. 442 */ 443if(!graph->commit) 444return; 445 446/* 447 * Output a padding row, that leaves all branch lines unchanged 448 */ 449for(i =0; i < graph->num_new_columns; i++) { 450strbuf_addstr(sb,"| "); 451} 452 453graph_pad_horizontally(graph, sb); 454} 455 456static voidgraph_output_skip_line(struct git_graph *graph,struct strbuf *sb) 457{ 458/* 459 * Output an ellipsis to indicate that a portion 460 * of the graph is missing. 461 */ 462strbuf_addstr(sb,"..."); 463graph_pad_horizontally(graph, sb); 464 465if(graph->num_parents >=3) 466 graph->state = GRAPH_PRE_COMMIT; 467else 468 graph->state = GRAPH_COMMIT; 469} 470 471static voidgraph_output_pre_commit_line(struct git_graph *graph, 472struct strbuf *sb) 473{ 474int num_expansion_rows; 475int i, seen_this; 476 477/* 478 * This function formats a row that increases the space around a commit 479 * with multiple parents, to make room for it. It should only be 480 * called when there are 3 or more parents. 481 * 482 * We need 2 extra rows for every parent over 2. 483 */ 484assert(graph->num_parents >=3); 485 num_expansion_rows = (graph->num_parents -2) *2; 486 487/* 488 * graph->expansion_row tracks the current expansion row we are on. 489 * It should be in the range [0, num_expansion_rows - 1] 490 */ 491assert(0<= graph->expansion_row && 492 graph->expansion_row < num_expansion_rows); 493 494/* 495 * Output the row 496 */ 497 seen_this =0; 498for(i =0; i < graph->num_columns; i++) { 499struct column *col = &graph->columns[i]; 500if(col->commit == graph->commit) { 501 seen_this =1; 502strbuf_addf(sb,"| %*s", graph->expansion_row,""); 503}else if(seen_this) { 504strbuf_addstr(sb,"\\"); 505}else{ 506strbuf_addstr(sb,"| "); 507} 508} 509 510graph_pad_horizontally(graph, sb); 511 512/* 513 * Increment graph->expansion_row, 514 * and move to state GRAPH_COMMIT if necessary 515 */ 516 graph->expansion_row++; 517if(graph->expansion_row >= num_expansion_rows) 518 graph->state = GRAPH_COMMIT; 519} 520 521voidgraph_output_commit_line(struct git_graph *graph,struct strbuf *sb) 522{ 523int seen_this =0; 524int i, j; 525 526/* 527 * Output the row containing this commit 528 * Iterate up to and including graph->num_columns, 529 * since the current commit may not be in any of the existing 530 * columns. (This happens when the current commit doesn't have any 531 * children that we have already processed.) 532 */ 533 seen_this =0; 534for(i =0; i <= graph->num_columns; i++) { 535struct commit *col_commit; 536if(i == graph->num_columns) { 537if(seen_this) 538break; 539 col_commit = graph->commit; 540}else{ 541 col_commit = graph->columns[i].commit; 542} 543 544if(col_commit == graph->commit) { 545 seen_this =1; 546if(graph->num_parents >1) 547strbuf_addch(sb,'M'); 548else 549strbuf_addch(sb,'*'); 550 551if(graph->num_parents <2) 552strbuf_addch(sb,' '); 553else if(graph->num_parents ==2) 554strbuf_addstr(sb," "); 555else{ 556int num_dashes = 557((graph->num_parents -2) *2) -1; 558for(j =0; j < num_dashes; j++) 559strbuf_addch(sb,'-'); 560strbuf_addstr(sb,". "); 561} 562}else if(seen_this && (graph->num_parents >1)) { 563strbuf_addstr(sb,"\\"); 564}else{ 565strbuf_addstr(sb,"| "); 566} 567} 568 569graph_pad_horizontally(graph, sb); 570 571/* 572 * Update graph->state 573 */ 574if(graph->num_parents >1) 575 graph->state = GRAPH_POST_MERGE; 576else if(graph_is_mapping_correct(graph)) 577 graph->state = GRAPH_PADDING; 578else 579 graph->state = GRAPH_COLLAPSING; 580} 581 582voidgraph_output_post_merge_line(struct git_graph *graph,struct strbuf *sb) 583{ 584int seen_this =0; 585int i, j; 586 587/* 588 * Output the post-merge row 589 */ 590for(i =0; i <= graph->num_columns; i++) { 591struct commit *col_commit; 592if(i == graph->num_columns) { 593if(seen_this) 594break; 595 col_commit = graph->commit; 596}else{ 597 col_commit = graph->columns[i].commit; 598} 599 600if(col_commit == graph->commit) { 601 seen_this =1; 602strbuf_addch(sb,'|'); 603for(j =0; j < graph->num_parents -1; j++) 604strbuf_addstr(sb,"\\"); 605if(graph->num_parents ==2) 606strbuf_addch(sb,' '); 607}else if(seen_this && (graph->num_parents >2)) { 608strbuf_addstr(sb,"\\"); 609}else{ 610strbuf_addstr(sb,"| "); 611} 612} 613 614graph_pad_horizontally(graph, sb); 615 616/* 617 * Update graph->state 618 */ 619if(graph_is_mapping_correct(graph)) 620 graph->state = GRAPH_PADDING; 621else 622 graph->state = GRAPH_COLLAPSING; 623} 624 625voidgraph_output_collapsing_line(struct git_graph *graph,struct strbuf *sb) 626{ 627int i; 628int*tmp_mapping; 629 630/* 631 * Clear out the new_mapping array 632 */ 633for(i =0; i < graph->mapping_size; i++) 634 graph->new_mapping[i] = -1; 635 636for(i =0; i < graph->mapping_size; i++) { 637int target = graph->mapping[i]; 638if(target <0) 639continue; 640 641/* 642 * Since update_columns() always inserts the leftmost 643 * column first, each branch's target location should 644 * always be either its current location or to the left of 645 * its current location. 646 * 647 * We never have to move branches to the right. This makes 648 * the graph much more legible, since whenever branches 649 * cross, only one is moving directions. 650 */ 651assert(target *2<= i); 652 653if(target *2== i) { 654/* 655 * This column is already in the 656 * correct place 657 */ 658assert(graph->new_mapping[i] == -1); 659 graph->new_mapping[i] = target; 660}else if(graph->new_mapping[i -1] <0) { 661/* 662 * Nothing is to the left. 663 * Move to the left by one 664 */ 665 graph->new_mapping[i -1] = target; 666}else if(graph->new_mapping[i -1] == target) { 667/* 668 * There is a branch line to our left 669 * already, and it is our target. We 670 * combine with this line, since we share 671 * the same parent commit. 672 * 673 * We don't have to add anything to the 674 * output or new_mapping, since the 675 * existing branch line has already taken 676 * care of it. 677 */ 678}else{ 679/* 680 * There is a branch line to our left, 681 * but it isn't our target. We need to 682 * cross over it. 683 * 684 * The space just to the left of this 685 * branch should always be empty. 686 */ 687assert(graph->new_mapping[i -1] > target); 688assert(graph->new_mapping[i -2] <0); 689 graph->new_mapping[i -2] = target; 690} 691} 692 693/* 694 * The new mapping may be 1 smaller than the old mapping 695 */ 696if(graph->new_mapping[graph->mapping_size -1] <0) 697 graph->mapping_size--; 698 699/* 700 * Output out a line based on the new mapping info 701 */ 702for(i =0; i < graph->mapping_size; i++) { 703int target = graph->new_mapping[i]; 704if(target <0) 705strbuf_addch(sb,' '); 706else if(target *2== i) 707strbuf_addch(sb,'|'); 708else 709strbuf_addch(sb,'/'); 710} 711 712graph_pad_horizontally(graph, sb); 713 714/* 715 * Swap mapping and new_mapping 716 */ 717 tmp_mapping = graph->mapping; 718 graph->mapping = graph->new_mapping; 719 graph->new_mapping = tmp_mapping; 720 721/* 722 * If graph->mapping indicates that all of the branch lines 723 * are already in the correct positions, we are done. 724 * Otherwise, we need to collapse some branch lines together. 725 */ 726if(graph_is_mapping_correct(graph)) 727 graph->state = GRAPH_PADDING; 728} 729 730intgraph_next_line(struct git_graph *graph,struct strbuf *sb) 731{ 732switch(graph->state) { 733case GRAPH_PADDING: 734graph_output_padding_line(graph, sb); 735return0; 736case GRAPH_SKIP: 737graph_output_skip_line(graph, sb); 738return0; 739case GRAPH_PRE_COMMIT: 740graph_output_pre_commit_line(graph, sb); 741return0; 742case GRAPH_COMMIT: 743graph_output_commit_line(graph, sb); 744return1; 745case GRAPH_POST_MERGE: 746graph_output_post_merge_line(graph, sb); 747return0; 748case GRAPH_COLLAPSING: 749graph_output_collapsing_line(graph, sb); 750return0; 751} 752 753assert(0); 754return0; 755} 756 757voidgraph_padding_line(struct git_graph *graph,struct strbuf *sb) 758{ 759int i, j; 760 761if(graph->state != GRAPH_COMMIT) { 762graph_next_line(graph, sb); 763return; 764} 765 766/* 767 * Output the row containing this commit 768 * Iterate up to and including graph->num_columns, 769 * since the current commit may not be in any of the existing 770 * columns. (This happens when the current commit doesn't have any 771 * children that we have already processed.) 772 */ 773for(i =0; i < graph->num_columns; i++) { 774struct commit *col_commit = graph->columns[i].commit; 775if(col_commit == graph->commit) { 776strbuf_addch(sb,'|'); 777 778if(graph->num_parents <3) 779strbuf_addch(sb,' '); 780else{ 781int num_spaces = ((graph->num_parents -2) *2); 782for(j =0; j < num_spaces; j++) 783strbuf_addch(sb,' '); 784} 785}else{ 786strbuf_addstr(sb,"| "); 787} 788} 789 790graph_pad_horizontally(graph, sb); 791} 792 793intgraph_is_commit_finished(struct git_graph const*graph) 794{ 795return(graph->state == GRAPH_PADDING); 796} 797 798voidgraph_show_commit(struct git_graph *graph) 799{ 800struct strbuf msgbuf; 801int shown_commit_line =0; 802 803if(!graph) 804return; 805 806strbuf_init(&msgbuf,0); 807 808while(!shown_commit_line) { 809 shown_commit_line =graph_next_line(graph, &msgbuf); 810fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout); 811if(!shown_commit_line) 812putchar('\n'); 813strbuf_setlen(&msgbuf,0); 814} 815 816strbuf_release(&msgbuf); 817} 818 819voidgraph_show_oneline(struct git_graph *graph) 820{ 821struct strbuf msgbuf; 822 823if(!graph) 824return; 825 826strbuf_init(&msgbuf,0); 827graph_next_line(graph, &msgbuf); 828fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout); 829strbuf_release(&msgbuf); 830} 831 832voidgraph_show_padding(struct git_graph *graph) 833{ 834struct strbuf msgbuf; 835 836if(!graph) 837return; 838 839strbuf_init(&msgbuf,0); 840graph_padding_line(graph, &msgbuf); 841fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout); 842strbuf_release(&msgbuf); 843} 844 845intgraph_show_remainder(struct git_graph *graph) 846{ 847struct strbuf msgbuf; 848int shown =0; 849 850if(!graph) 851return0; 852 853if(graph_is_commit_finished(graph)) 854return0; 855 856strbuf_init(&msgbuf,0); 857for(;;) { 858graph_next_line(graph, &msgbuf); 859fwrite(msgbuf.buf,sizeof(char), msgbuf.len, stdout); 860strbuf_setlen(&msgbuf,0); 861 shown =1; 862 863if(!graph_is_commit_finished(graph)) 864putchar('\n'); 865else 866break; 867} 868strbuf_release(&msgbuf); 869 870return shown; 871} 872 873 874voidgraph_show_strbuf(struct git_graph *graph,struct strbuf const*sb) 875{ 876char*p; 877 878if(!graph) { 879fwrite(sb->buf,sizeof(char), sb->len, stdout); 880return; 881} 882 883/* 884 * Print the strbuf line by line, 885 * and display the graph info before each line but the first. 886 */ 887 p = sb->buf; 888while(p) { 889size_t len; 890char*next_p =strchr(p,'\n'); 891if(next_p) { 892 next_p++; 893 len = next_p - p; 894}else{ 895 len = (sb->buf + sb->len) - p; 896} 897fwrite(p,sizeof(char), len, stdout); 898if(next_p && *next_p !='\0') 899graph_show_oneline(graph); 900 p = next_p; 901} 902} 903 904voidgraph_show_commit_msg(struct git_graph *graph, 905struct strbuf const*sb) 906{ 907int newline_terminated; 908 909if(!graph) { 910/* 911 * If there's no graph, just print the message buffer. 912 * 913 * The message buffer for CMIT_FMT_ONELINE and 914 * CMIT_FMT_USERFORMAT are already missing a terminating 915 * newline. All of the other formats should have it. 916 */ 917fwrite(sb->buf,sizeof(char), sb->len, stdout); 918return; 919} 920 921 newline_terminated = (sb->len && sb->buf[sb->len -1] =='\n'); 922 923/* 924 * Show the commit message 925 */ 926graph_show_strbuf(graph, sb); 927 928/* 929 * If there is more output needed for this commit, show it now 930 */ 931if(!graph_is_commit_finished(graph)) { 932/* 933 * If sb doesn't have a terminating newline, print one now, 934 * so we can start the remainder of the graph output on a 935 * new line. 936 */ 937if(!newline_terminated) 938putchar('\n'); 939 940graph_show_remainder(graph); 941 942/* 943 * If sb ends with a newline, our output should too. 944 */ 945if(newline_terminated) 946putchar('\n'); 947} 948}