Fossil SCM
Add a --tcl option to the 3-way-merge command.
Commit
97ab5604ea9fbde58c3d320fc6ef5b9a08637152c1b2ccb00d62863d7c3420bc
Parent
c4df699fd11fc32…
2 files changed
+1
-1
+187
-7
+1
-1
| --- src/diff.tcl | ||
| +++ src/diff.tcl | ||
| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line |
| 2 | 2 | # to this file, then runs this file using "tclsh" in order to display the |
| 3 | 3 | # graphical diff in a separate window. A typical "set fossilcmd" line |
| 4 | 4 | # looks like this: |
| 5 | 5 | # |
| 6 | -# set fossilcmd {| "./fossil" diff --html -y -i -v} | |
| 6 | +# set fossilcmd {| "./fossil" diff --tcl -i -v} | |
| 7 | 7 | # |
| 8 | 8 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 9 | 9 | # |
| 10 | 10 | set prog { |
| 11 | 11 | package require Tk |
| 12 | 12 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -1,11 +1,11 @@ | |
| 1 | # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line |
| 2 | # to this file, then runs this file using "tclsh" in order to display the |
| 3 | # graphical diff in a separate window. A typical "set fossilcmd" line |
| 4 | # looks like this: |
| 5 | # |
| 6 | # set fossilcmd {| "./fossil" diff --html -y -i -v} |
| 7 | # |
| 8 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 9 | # |
| 10 | set prog { |
| 11 | package require Tk |
| 12 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -1,11 +1,11 @@ | |
| 1 | # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line |
| 2 | # to this file, then runs this file using "tclsh" in order to display the |
| 3 | # graphical diff in a separate window. A typical "set fossilcmd" line |
| 4 | # looks like this: |
| 5 | # |
| 6 | # set fossilcmd {| "./fossil" diff --tcl -i -v} |
| 7 | # |
| 8 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 9 | # |
| 10 | set prog { |
| 11 | package require Tk |
| 12 |
+187
-7
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -277,11 +277,10 @@ | ||
| 277 | 277 | p->xConflict = dbgConflict; |
| 278 | 278 | p->xEnd = dbgStartEnd; |
| 279 | 279 | p->xDestroy = dbgDestroy; |
| 280 | 280 | } |
| 281 | 281 | |
| 282 | - | |
| 283 | 282 | /************************* MergeBuilderText **********************************/ |
| 284 | 283 | /* This version of MergeBuilder actually performs a merge on file and puts |
| 285 | 284 | ** the result in pOut |
| 286 | 285 | */ |
| 287 | 286 | static void txtStart(MergeBuilder *p){ |
| @@ -341,10 +340,188 @@ | ||
| 341 | 340 | p->xChngV1 = txtChngV1; |
| 342 | 341 | p->xChngV2 = txtChngV2; |
| 343 | 342 | p->xChngBoth = txtChngBoth; |
| 344 | 343 | p->xConflict = txtConflict; |
| 345 | 344 | } |
| 345 | + | |
| 346 | +/************************* MergeBuilderTcl **********************************/ | |
| 347 | +/* Generate merge output formatted for reading by a TCL script. | |
| 348 | +** | |
| 349 | +** The output consists of lines of text, each with 4 tokens. The tokens | |
| 350 | +** represent the content for one line from baseline, v1, v2, and output | |
| 351 | +** respectively. The first character of each token provides auxiliary | |
| 352 | +** information: | |
| 353 | +** | |
| 354 | +** . This line is omitted. | |
| 355 | +** T Literal text follows that should have a \n terminator. | |
| 356 | +** R Literal text follows that needs a \r\n terminator. | |
| 357 | +** Z Literal text without a line terminator. | |
| 358 | +** 1 Text is a copy of token 1 | |
| 359 | +** 2 Use data from data-token 2 | |
| 360 | +** 3 Use data from data-token 3 | |
| 361 | +*/ | |
| 362 | + | |
| 363 | +/* Copy one line of text from pIn and append to pOut, encoded as TCL */ | |
| 364 | +static void tclLineOfText(Blob *pOut, Blob *pIn){ | |
| 365 | + int i, j, k; | |
| 366 | + for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){} | |
| 367 | + if( i==pIn->nUsed ){ | |
| 368 | + blob_append(pOut, "\"Z", 2); | |
| 369 | + k = i; | |
| 370 | + }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){ | |
| 371 | + blob_append(pOut, "\"R", 2); | |
| 372 | + k = i-1; | |
| 373 | + i++; | |
| 374 | + }else{ | |
| 375 | + blob_append(pOut, "\"T", 2); | |
| 376 | + k = i; | |
| 377 | + i++; | |
| 378 | + } | |
| 379 | + for(j=pIn->iCursor; j<k; j++){ | |
| 380 | + char c = pIn->aData[j]; | |
| 381 | + if( c=='\\' ){ | |
| 382 | + blob_append(pOut, "\\\\", 2); | |
| 383 | + }else if( c=='"' ){ | |
| 384 | + blob_append(pOut, "\\\"", 2); | |
| 385 | + }else if( c<' ' || c>0x7e ){ | |
| 386 | + char z[5]; | |
| 387 | + z[0] = '\\'; | |
| 388 | + z[1] = "01234567"[(c>>6)&0x3]; | |
| 389 | + z[2] = "01234567"[(c>>3)&0x7]; | |
| 390 | + z[3] = "01234567"[c&0x7]; | |
| 391 | + z[4] = 0; | |
| 392 | + blob_append(pOut, z, 4); | |
| 393 | + }else{ | |
| 394 | + blob_append_char(pOut, c); | |
| 395 | + } | |
| 396 | + } | |
| 397 | + pIn->iCursor = i; | |
| 398 | + blob_append_char(pOut, '"'); | |
| 399 | +} | |
| 400 | +static void tclSame(MergeBuilder *p, unsigned int N){ | |
| 401 | + int i; | |
| 402 | + for(i=0; i<N; i++){ | |
| 403 | + tclLineOfText(p->pOut, p->pPivot); | |
| 404 | + blob_append(p->pOut, " 1 1 1\n", 7); | |
| 405 | + } | |
| 406 | + p->lnPivot += N; | |
| 407 | + p->lnV1 += N; | |
| 408 | + p->lnV2 += N; | |
| 409 | + blob_copy_lines(0, p->pV1, N); | |
| 410 | + blob_copy_lines(0, p->pV2, N); | |
| 411 | +} | |
| 412 | +static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){ | |
| 413 | + int i; | |
| 414 | + for(i=0; i<nPivot && i<nV1; i++){ | |
| 415 | + tclLineOfText(p->pOut, p->pPivot); | |
| 416 | + blob_append_char(p->pOut, ' '); | |
| 417 | + tclLineOfText(p->pOut, p->pV1); | |
| 418 | + blob_append(p->pOut, " 1 2\n", 5); | |
| 419 | + } | |
| 420 | + while( i<nPivot ){ | |
| 421 | + tclLineOfText(p->pOut, p->pPivot); | |
| 422 | + blob_append(p->pOut, " . 1 .\n", 7); | |
| 423 | + i++; | |
| 424 | + } | |
| 425 | + while( i<nV1 ){ | |
| 426 | + blob_append(p->pOut, ". ", 2); | |
| 427 | + tclLineOfText(p->pOut, p->pV1); | |
| 428 | + blob_append(p->pOut, " . 2\n", 5); | |
| 429 | + i++; | |
| 430 | + } | |
| 431 | + p->lnPivot += nPivot; | |
| 432 | + p->lnV1 += nV1; | |
| 433 | + p->lnV2 += nPivot; | |
| 434 | + blob_copy_lines(0, p->pV2, nPivot); | |
| 435 | +} | |
| 436 | +static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){ | |
| 437 | + int i; | |
| 438 | + for(i=0; i<nPivot && i<nV2; i++){ | |
| 439 | + tclLineOfText(p->pOut, p->pPivot); | |
| 440 | + blob_append(p->pOut, " 1 ", 3); | |
| 441 | + tclLineOfText(p->pOut, p->pV2); | |
| 442 | + blob_append(p->pOut, " 3\n", 3); | |
| 443 | + } | |
| 444 | + while( i<nPivot ){ | |
| 445 | + tclLineOfText(p->pOut, p->pPivot); | |
| 446 | + blob_append(p->pOut, " 1 . .\n", 7); | |
| 447 | + i++; | |
| 448 | + } | |
| 449 | + while( i<nV2 ){ | |
| 450 | + blob_append(p->pOut, ". . ", 4); | |
| 451 | + tclLineOfText(p->pOut, p->pV2); | |
| 452 | + blob_append(p->pOut, " 3\n", 3); | |
| 453 | + i++; | |
| 454 | + } | |
| 455 | + p->lnPivot += nPivot; | |
| 456 | + p->lnV1 += nPivot; | |
| 457 | + p->lnV2 += nV2; | |
| 458 | + blob_copy_lines(0, p->pV1, nPivot); | |
| 459 | +} | |
| 460 | +static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){ | |
| 461 | + int i; | |
| 462 | + for(i=0; i<nPivot && i<nV; i++){ | |
| 463 | + tclLineOfText(p->pOut, p->pPivot); | |
| 464 | + blob_append_char(p->pOut, ' '); | |
| 465 | + tclLineOfText(p->pOut, p->pV1); | |
| 466 | + blob_append(p->pOut, " 2 2\n", 5); | |
| 467 | + } | |
| 468 | + while( i<nPivot ){ | |
| 469 | + tclLineOfText(p->pOut, p->pPivot); | |
| 470 | + blob_append(p->pOut, " . . .\n", 7); | |
| 471 | + i++; | |
| 472 | + } | |
| 473 | + while( i<nV ){ | |
| 474 | + blob_append(p->pOut, ". ", 2); | |
| 475 | + tclLineOfText(p->pOut, p->pV1); | |
| 476 | + blob_append(p->pOut, " 2 2\n", 5); | |
| 477 | + i++; | |
| 478 | + } | |
| 479 | + p->lnPivot += nPivot; | |
| 480 | + p->lnV1 += nV; | |
| 481 | + p->lnV2 += nV; | |
| 482 | + blob_copy_lines(0, p->pV2, nV); | |
| 483 | +} | |
| 484 | +static void tclConflict( | |
| 485 | + MergeBuilder *p, | |
| 486 | + unsigned int nPivot, | |
| 487 | + unsigned int nV1, | |
| 488 | + unsigned int nV2 | |
| 489 | +){ | |
| 490 | + int mx = nPivot; | |
| 491 | + int i; | |
| 492 | + if( nV1>mx ) mx = nV1; | |
| 493 | + if( nV2>mx ) mx = nV2; | |
| 494 | + for(i=0; i<mx; i++){ | |
| 495 | + if( i<nPivot ){ | |
| 496 | + tclLineOfText(p->pOut, p->pPivot); | |
| 497 | + }else{ | |
| 498 | + blob_append_char(p->pOut, '.'); | |
| 499 | + } | |
| 500 | + blob_append_char(p->pOut, ' '); | |
| 501 | + if( i<nV1 ){ | |
| 502 | + tclLineOfText(p->pOut, p->pV1); | |
| 503 | + }else{ | |
| 504 | + blob_append_char(p->pOut, '.'); | |
| 505 | + } | |
| 506 | + blob_append_char(p->pOut, ' '); | |
| 507 | + if( i<nV2 ){ | |
| 508 | + tclLineOfText(p->pOut, p->pV2); | |
| 509 | + }else{ | |
| 510 | + blob_append_char(p->pOut, '.'); | |
| 511 | + } | |
| 512 | + blob_append(p->pOut, " X\n", 3); | |
| 513 | + } | |
| 514 | +} | |
| 515 | +static void mergebuilder_init_tcl(MergeBuilder *p){ | |
| 516 | + mergebuilder_init(p); | |
| 517 | + p->xSame = tclSame; | |
| 518 | + p->xChngV1 = tclChngV1; | |
| 519 | + p->xChngV2 = tclChngV2; | |
| 520 | + p->xChngBoth = tclChngBoth; | |
| 521 | + p->xConflict = tclConflict; | |
| 522 | +} | |
| 346 | 523 | /*****************************************************************************/ |
| 347 | 524 | |
| 348 | 525 | /* |
| 349 | 526 | ** aC[] is an "edit triple" for changes from A to B. Advance through |
| 350 | 527 | ** this triple to determine the number of lines to bypass on B in order |
| @@ -381,11 +558,11 @@ | ||
| 381 | 558 | ** The return is 0 upon complete success. If any input file is binary, |
| 382 | 559 | ** -1 is returned and pOut is unmodified. If there are merge |
| 383 | 560 | ** conflicts, the merge proceeds as best as it can and the number |
| 384 | 561 | ** of conflicts is returns |
| 385 | 562 | */ |
| 386 | -static int blob_merge(MergeBuilder *p){ | |
| 563 | +static int merge_three_blobs(MergeBuilder *p){ | |
| 387 | 564 | int *aC1; /* Changes from pPivot to pV1 */ |
| 388 | 565 | int *aC2; /* Changes from pPivot to pV2 */ |
| 389 | 566 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 390 | 567 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 391 | 568 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| @@ -573,10 +750,13 @@ | ||
| 573 | 750 | Blob pivot, v1, v2, out; |
| 574 | 751 | |
| 575 | 752 | mergebuilder_init_text(&s); |
| 576 | 753 | if( find_option("debug", 0, 0) ){ |
| 577 | 754 | mergebuilder_init(&s); |
| 755 | + } | |
| 756 | + if( find_option("tcl", 0, 0) ){ | |
| 757 | + mergebuilder_init_tcl(&s); | |
| 578 | 758 | } |
| 579 | 759 | blob_zero(&pivot); s.pPivot = &pivot; |
| 580 | 760 | blob_zero(&v1); s.pV1 = &v1; |
| 581 | 761 | blob_zero(&v2); s.pV2 = &v2; |
| 582 | 762 | blob_zero(&out); s.pOut = &out; |
| @@ -594,11 +774,11 @@ | ||
| 594 | 774 | fossil_fatal("cannot read %s", g.argv[3]); |
| 595 | 775 | } |
| 596 | 776 | if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){ |
| 597 | 777 | fossil_fatal("cannot read %s", g.argv[4]); |
| 598 | 778 | } |
| 599 | - nConflict = blob_merge(&s); | |
| 779 | + nConflict = merge_three_blobs(&s); | |
| 600 | 780 | if( g.argc==6 ){ |
| 601 | 781 | blob_write_to_file(s.pOut, g.argv[5]); |
| 602 | 782 | }else{ |
| 603 | 783 | blob_write_to_file(s.pOut, "-"); |
| 604 | 784 | } |
| @@ -658,21 +838,21 @@ | ||
| 658 | 838 | #define MERGE_KEEP_FILES 0x0002 |
| 659 | 839 | #endif |
| 660 | 840 | |
| 661 | 841 | |
| 662 | 842 | /* |
| 663 | -** This routine is a wrapper around blob_merge() with the following | |
| 843 | +** This routine is a wrapper around merge_three_blobs() with the following | |
| 664 | 844 | ** enhancements: |
| 665 | 845 | ** |
| 666 | 846 | ** (1) If the merge-command is defined, then use the external merging |
| 667 | 847 | ** program specified instead of the built-in blob-merge to do the |
| 668 | 848 | ** merging. Panic if the external merger fails. |
| 669 | 849 | ** ** Not currently implemented ** |
| 670 | 850 | ** |
| 671 | 851 | ** (2) If gmerge-command is defined and there are merge conflicts in |
| 672 | -** blob_merge() then invoke the external graphical merger to resolve | |
| 673 | -** the conflicts. | |
| 852 | +** merge_three_blobs() then invoke the external graphical merger | |
| 853 | +** to resolve the conflicts. | |
| 674 | 854 | ** |
| 675 | 855 | ** (3) If a merge conflict occurs and gmerge-command is not defined, |
| 676 | 856 | ** then write the pivot, original, and merge-in files to the |
| 677 | 857 | ** filesystem. |
| 678 | 858 | */ |
| @@ -693,11 +873,11 @@ | ||
| 693 | 873 | s.pV1 = &v1; |
| 694 | 874 | s.pV2 = pV2; |
| 695 | 875 | blob_zero(pOut); |
| 696 | 876 | s.pOut = pOut; |
| 697 | 877 | blob_read_from_file(s.pV1, zV1, ExtFILE); |
| 698 | - rc = blob_merge(&s); | |
| 878 | + rc = merge_three_blobs(&s); | |
| 699 | 879 | zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0); |
| 700 | 880 | if( (mergeFlags & MERGE_DRYRUN)==0 |
| 701 | 881 | && ((zGMerge!=0 && zGMerge[0]!=0) |
| 702 | 882 | || (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){ |
| 703 | 883 | char *zPivot; /* Name of the pivot file */ |
| 704 | 884 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -277,11 +277,10 @@ | |
| 277 | p->xConflict = dbgConflict; |
| 278 | p->xEnd = dbgStartEnd; |
| 279 | p->xDestroy = dbgDestroy; |
| 280 | } |
| 281 | |
| 282 | |
| 283 | /************************* MergeBuilderText **********************************/ |
| 284 | /* This version of MergeBuilder actually performs a merge on file and puts |
| 285 | ** the result in pOut |
| 286 | */ |
| 287 | static void txtStart(MergeBuilder *p){ |
| @@ -341,10 +340,188 @@ | |
| 341 | p->xChngV1 = txtChngV1; |
| 342 | p->xChngV2 = txtChngV2; |
| 343 | p->xChngBoth = txtChngBoth; |
| 344 | p->xConflict = txtConflict; |
| 345 | } |
| 346 | /*****************************************************************************/ |
| 347 | |
| 348 | /* |
| 349 | ** aC[] is an "edit triple" for changes from A to B. Advance through |
| 350 | ** this triple to determine the number of lines to bypass on B in order |
| @@ -381,11 +558,11 @@ | |
| 381 | ** The return is 0 upon complete success. If any input file is binary, |
| 382 | ** -1 is returned and pOut is unmodified. If there are merge |
| 383 | ** conflicts, the merge proceeds as best as it can and the number |
| 384 | ** of conflicts is returns |
| 385 | */ |
| 386 | static int blob_merge(MergeBuilder *p){ |
| 387 | int *aC1; /* Changes from pPivot to pV1 */ |
| 388 | int *aC2; /* Changes from pPivot to pV2 */ |
| 389 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 390 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 391 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| @@ -573,10 +750,13 @@ | |
| 573 | Blob pivot, v1, v2, out; |
| 574 | |
| 575 | mergebuilder_init_text(&s); |
| 576 | if( find_option("debug", 0, 0) ){ |
| 577 | mergebuilder_init(&s); |
| 578 | } |
| 579 | blob_zero(&pivot); s.pPivot = &pivot; |
| 580 | blob_zero(&v1); s.pV1 = &v1; |
| 581 | blob_zero(&v2); s.pV2 = &v2; |
| 582 | blob_zero(&out); s.pOut = &out; |
| @@ -594,11 +774,11 @@ | |
| 594 | fossil_fatal("cannot read %s", g.argv[3]); |
| 595 | } |
| 596 | if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){ |
| 597 | fossil_fatal("cannot read %s", g.argv[4]); |
| 598 | } |
| 599 | nConflict = blob_merge(&s); |
| 600 | if( g.argc==6 ){ |
| 601 | blob_write_to_file(s.pOut, g.argv[5]); |
| 602 | }else{ |
| 603 | blob_write_to_file(s.pOut, "-"); |
| 604 | } |
| @@ -658,21 +838,21 @@ | |
| 658 | #define MERGE_KEEP_FILES 0x0002 |
| 659 | #endif |
| 660 | |
| 661 | |
| 662 | /* |
| 663 | ** This routine is a wrapper around blob_merge() with the following |
| 664 | ** enhancements: |
| 665 | ** |
| 666 | ** (1) If the merge-command is defined, then use the external merging |
| 667 | ** program specified instead of the built-in blob-merge to do the |
| 668 | ** merging. Panic if the external merger fails. |
| 669 | ** ** Not currently implemented ** |
| 670 | ** |
| 671 | ** (2) If gmerge-command is defined and there are merge conflicts in |
| 672 | ** blob_merge() then invoke the external graphical merger to resolve |
| 673 | ** the conflicts. |
| 674 | ** |
| 675 | ** (3) If a merge conflict occurs and gmerge-command is not defined, |
| 676 | ** then write the pivot, original, and merge-in files to the |
| 677 | ** filesystem. |
| 678 | */ |
| @@ -693,11 +873,11 @@ | |
| 693 | s.pV1 = &v1; |
| 694 | s.pV2 = pV2; |
| 695 | blob_zero(pOut); |
| 696 | s.pOut = pOut; |
| 697 | blob_read_from_file(s.pV1, zV1, ExtFILE); |
| 698 | rc = blob_merge(&s); |
| 699 | zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0); |
| 700 | if( (mergeFlags & MERGE_DRYRUN)==0 |
| 701 | && ((zGMerge!=0 && zGMerge[0]!=0) |
| 702 | || (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){ |
| 703 | char *zPivot; /* Name of the pivot file */ |
| 704 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -277,11 +277,10 @@ | |
| 277 | p->xConflict = dbgConflict; |
| 278 | p->xEnd = dbgStartEnd; |
| 279 | p->xDestroy = dbgDestroy; |
| 280 | } |
| 281 | |
| 282 | /************************* MergeBuilderText **********************************/ |
| 283 | /* This version of MergeBuilder actually performs a merge on file and puts |
| 284 | ** the result in pOut |
| 285 | */ |
| 286 | static void txtStart(MergeBuilder *p){ |
| @@ -341,10 +340,188 @@ | |
| 340 | p->xChngV1 = txtChngV1; |
| 341 | p->xChngV2 = txtChngV2; |
| 342 | p->xChngBoth = txtChngBoth; |
| 343 | p->xConflict = txtConflict; |
| 344 | } |
| 345 | |
| 346 | /************************* MergeBuilderTcl **********************************/ |
| 347 | /* Generate merge output formatted for reading by a TCL script. |
| 348 | ** |
| 349 | ** The output consists of lines of text, each with 4 tokens. The tokens |
| 350 | ** represent the content for one line from baseline, v1, v2, and output |
| 351 | ** respectively. The first character of each token provides auxiliary |
| 352 | ** information: |
| 353 | ** |
| 354 | ** . This line is omitted. |
| 355 | ** T Literal text follows that should have a \n terminator. |
| 356 | ** R Literal text follows that needs a \r\n terminator. |
| 357 | ** Z Literal text without a line terminator. |
| 358 | ** 1 Text is a copy of token 1 |
| 359 | ** 2 Use data from data-token 2 |
| 360 | ** 3 Use data from data-token 3 |
| 361 | */ |
| 362 | |
| 363 | /* Copy one line of text from pIn and append to pOut, encoded as TCL */ |
| 364 | static void tclLineOfText(Blob *pOut, Blob *pIn){ |
| 365 | int i, j, k; |
| 366 | for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){} |
| 367 | if( i==pIn->nUsed ){ |
| 368 | blob_append(pOut, "\"Z", 2); |
| 369 | k = i; |
| 370 | }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){ |
| 371 | blob_append(pOut, "\"R", 2); |
| 372 | k = i-1; |
| 373 | i++; |
| 374 | }else{ |
| 375 | blob_append(pOut, "\"T", 2); |
| 376 | k = i; |
| 377 | i++; |
| 378 | } |
| 379 | for(j=pIn->iCursor; j<k; j++){ |
| 380 | char c = pIn->aData[j]; |
| 381 | if( c=='\\' ){ |
| 382 | blob_append(pOut, "\\\\", 2); |
| 383 | }else if( c=='"' ){ |
| 384 | blob_append(pOut, "\\\"", 2); |
| 385 | }else if( c<' ' || c>0x7e ){ |
| 386 | char z[5]; |
| 387 | z[0] = '\\'; |
| 388 | z[1] = "01234567"[(c>>6)&0x3]; |
| 389 | z[2] = "01234567"[(c>>3)&0x7]; |
| 390 | z[3] = "01234567"[c&0x7]; |
| 391 | z[4] = 0; |
| 392 | blob_append(pOut, z, 4); |
| 393 | }else{ |
| 394 | blob_append_char(pOut, c); |
| 395 | } |
| 396 | } |
| 397 | pIn->iCursor = i; |
| 398 | blob_append_char(pOut, '"'); |
| 399 | } |
| 400 | static void tclSame(MergeBuilder *p, unsigned int N){ |
| 401 | int i; |
| 402 | for(i=0; i<N; i++){ |
| 403 | tclLineOfText(p->pOut, p->pPivot); |
| 404 | blob_append(p->pOut, " 1 1 1\n", 7); |
| 405 | } |
| 406 | p->lnPivot += N; |
| 407 | p->lnV1 += N; |
| 408 | p->lnV2 += N; |
| 409 | blob_copy_lines(0, p->pV1, N); |
| 410 | blob_copy_lines(0, p->pV2, N); |
| 411 | } |
| 412 | static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){ |
| 413 | int i; |
| 414 | for(i=0; i<nPivot && i<nV1; i++){ |
| 415 | tclLineOfText(p->pOut, p->pPivot); |
| 416 | blob_append_char(p->pOut, ' '); |
| 417 | tclLineOfText(p->pOut, p->pV1); |
| 418 | blob_append(p->pOut, " 1 2\n", 5); |
| 419 | } |
| 420 | while( i<nPivot ){ |
| 421 | tclLineOfText(p->pOut, p->pPivot); |
| 422 | blob_append(p->pOut, " . 1 .\n", 7); |
| 423 | i++; |
| 424 | } |
| 425 | while( i<nV1 ){ |
| 426 | blob_append(p->pOut, ". ", 2); |
| 427 | tclLineOfText(p->pOut, p->pV1); |
| 428 | blob_append(p->pOut, " . 2\n", 5); |
| 429 | i++; |
| 430 | } |
| 431 | p->lnPivot += nPivot; |
| 432 | p->lnV1 += nV1; |
| 433 | p->lnV2 += nPivot; |
| 434 | blob_copy_lines(0, p->pV2, nPivot); |
| 435 | } |
| 436 | static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){ |
| 437 | int i; |
| 438 | for(i=0; i<nPivot && i<nV2; i++){ |
| 439 | tclLineOfText(p->pOut, p->pPivot); |
| 440 | blob_append(p->pOut, " 1 ", 3); |
| 441 | tclLineOfText(p->pOut, p->pV2); |
| 442 | blob_append(p->pOut, " 3\n", 3); |
| 443 | } |
| 444 | while( i<nPivot ){ |
| 445 | tclLineOfText(p->pOut, p->pPivot); |
| 446 | blob_append(p->pOut, " 1 . .\n", 7); |
| 447 | i++; |
| 448 | } |
| 449 | while( i<nV2 ){ |
| 450 | blob_append(p->pOut, ". . ", 4); |
| 451 | tclLineOfText(p->pOut, p->pV2); |
| 452 | blob_append(p->pOut, " 3\n", 3); |
| 453 | i++; |
| 454 | } |
| 455 | p->lnPivot += nPivot; |
| 456 | p->lnV1 += nPivot; |
| 457 | p->lnV2 += nV2; |
| 458 | blob_copy_lines(0, p->pV1, nPivot); |
| 459 | } |
| 460 | static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){ |
| 461 | int i; |
| 462 | for(i=0; i<nPivot && i<nV; i++){ |
| 463 | tclLineOfText(p->pOut, p->pPivot); |
| 464 | blob_append_char(p->pOut, ' '); |
| 465 | tclLineOfText(p->pOut, p->pV1); |
| 466 | blob_append(p->pOut, " 2 2\n", 5); |
| 467 | } |
| 468 | while( i<nPivot ){ |
| 469 | tclLineOfText(p->pOut, p->pPivot); |
| 470 | blob_append(p->pOut, " . . .\n", 7); |
| 471 | i++; |
| 472 | } |
| 473 | while( i<nV ){ |
| 474 | blob_append(p->pOut, ". ", 2); |
| 475 | tclLineOfText(p->pOut, p->pV1); |
| 476 | blob_append(p->pOut, " 2 2\n", 5); |
| 477 | i++; |
| 478 | } |
| 479 | p->lnPivot += nPivot; |
| 480 | p->lnV1 += nV; |
| 481 | p->lnV2 += nV; |
| 482 | blob_copy_lines(0, p->pV2, nV); |
| 483 | } |
| 484 | static void tclConflict( |
| 485 | MergeBuilder *p, |
| 486 | unsigned int nPivot, |
| 487 | unsigned int nV1, |
| 488 | unsigned int nV2 |
| 489 | ){ |
| 490 | int mx = nPivot; |
| 491 | int i; |
| 492 | if( nV1>mx ) mx = nV1; |
| 493 | if( nV2>mx ) mx = nV2; |
| 494 | for(i=0; i<mx; i++){ |
| 495 | if( i<nPivot ){ |
| 496 | tclLineOfText(p->pOut, p->pPivot); |
| 497 | }else{ |
| 498 | blob_append_char(p->pOut, '.'); |
| 499 | } |
| 500 | blob_append_char(p->pOut, ' '); |
| 501 | if( i<nV1 ){ |
| 502 | tclLineOfText(p->pOut, p->pV1); |
| 503 | }else{ |
| 504 | blob_append_char(p->pOut, '.'); |
| 505 | } |
| 506 | blob_append_char(p->pOut, ' '); |
| 507 | if( i<nV2 ){ |
| 508 | tclLineOfText(p->pOut, p->pV2); |
| 509 | }else{ |
| 510 | blob_append_char(p->pOut, '.'); |
| 511 | } |
| 512 | blob_append(p->pOut, " X\n", 3); |
| 513 | } |
| 514 | } |
| 515 | static void mergebuilder_init_tcl(MergeBuilder *p){ |
| 516 | mergebuilder_init(p); |
| 517 | p->xSame = tclSame; |
| 518 | p->xChngV1 = tclChngV1; |
| 519 | p->xChngV2 = tclChngV2; |
| 520 | p->xChngBoth = tclChngBoth; |
| 521 | p->xConflict = tclConflict; |
| 522 | } |
| 523 | /*****************************************************************************/ |
| 524 | |
| 525 | /* |
| 526 | ** aC[] is an "edit triple" for changes from A to B. Advance through |
| 527 | ** this triple to determine the number of lines to bypass on B in order |
| @@ -381,11 +558,11 @@ | |
| 558 | ** The return is 0 upon complete success. If any input file is binary, |
| 559 | ** -1 is returned and pOut is unmodified. If there are merge |
| 560 | ** conflicts, the merge proceeds as best as it can and the number |
| 561 | ** of conflicts is returns |
| 562 | */ |
| 563 | static int merge_three_blobs(MergeBuilder *p){ |
| 564 | int *aC1; /* Changes from pPivot to pV1 */ |
| 565 | int *aC2; /* Changes from pPivot to pV2 */ |
| 566 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 567 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 568 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| @@ -573,10 +750,13 @@ | |
| 750 | Blob pivot, v1, v2, out; |
| 751 | |
| 752 | mergebuilder_init_text(&s); |
| 753 | if( find_option("debug", 0, 0) ){ |
| 754 | mergebuilder_init(&s); |
| 755 | } |
| 756 | if( find_option("tcl", 0, 0) ){ |
| 757 | mergebuilder_init_tcl(&s); |
| 758 | } |
| 759 | blob_zero(&pivot); s.pPivot = &pivot; |
| 760 | blob_zero(&v1); s.pV1 = &v1; |
| 761 | blob_zero(&v2); s.pV2 = &v2; |
| 762 | blob_zero(&out); s.pOut = &out; |
| @@ -594,11 +774,11 @@ | |
| 774 | fossil_fatal("cannot read %s", g.argv[3]); |
| 775 | } |
| 776 | if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){ |
| 777 | fossil_fatal("cannot read %s", g.argv[4]); |
| 778 | } |
| 779 | nConflict = merge_three_blobs(&s); |
| 780 | if( g.argc==6 ){ |
| 781 | blob_write_to_file(s.pOut, g.argv[5]); |
| 782 | }else{ |
| 783 | blob_write_to_file(s.pOut, "-"); |
| 784 | } |
| @@ -658,21 +838,21 @@ | |
| 838 | #define MERGE_KEEP_FILES 0x0002 |
| 839 | #endif |
| 840 | |
| 841 | |
| 842 | /* |
| 843 | ** This routine is a wrapper around merge_three_blobs() with the following |
| 844 | ** enhancements: |
| 845 | ** |
| 846 | ** (1) If the merge-command is defined, then use the external merging |
| 847 | ** program specified instead of the built-in blob-merge to do the |
| 848 | ** merging. Panic if the external merger fails. |
| 849 | ** ** Not currently implemented ** |
| 850 | ** |
| 851 | ** (2) If gmerge-command is defined and there are merge conflicts in |
| 852 | ** merge_three_blobs() then invoke the external graphical merger |
| 853 | ** to resolve the conflicts. |
| 854 | ** |
| 855 | ** (3) If a merge conflict occurs and gmerge-command is not defined, |
| 856 | ** then write the pivot, original, and merge-in files to the |
| 857 | ** filesystem. |
| 858 | */ |
| @@ -693,11 +873,11 @@ | |
| 873 | s.pV1 = &v1; |
| 874 | s.pV2 = pV2; |
| 875 | blob_zero(pOut); |
| 876 | s.pOut = pOut; |
| 877 | blob_read_from_file(s.pV1, zV1, ExtFILE); |
| 878 | rc = merge_three_blobs(&s); |
| 879 | zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0); |
| 880 | if( (mergeFlags & MERGE_DRYRUN)==0 |
| 881 | && ((zGMerge!=0 && zGMerge[0]!=0) |
| 882 | || (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){ |
| 883 | char *zPivot; /* Name of the pivot file */ |
| 884 |