Fossil SCM
Add the "fossil patch diff" command.
Commit
72d07a52c36c196f45335c633a26f5c2551f87ec329665c66037b48b6f7253fc
Parent
b9d5fa4d7b73e31…
2 files changed
+135
-15
+1
-2
+135
-15
| --- src/patch.c | ||
| +++ src/patch.c | ||
| @@ -82,12 +82,12 @@ | ||
| 82 | 82 | /* |
| 83 | 83 | ** mkdelta(X,Y) |
| 84 | 84 | ** |
| 85 | 85 | ** X is an numeric artifact id. Y is a filename. |
| 86 | 86 | ** |
| 87 | -** Compute a compressed delta that carries X into Y. Or return NULL | |
| 88 | -** if X is equal to Y. | |
| 87 | +** Compute a compressed delta that carries X into Y. Or return | |
| 88 | +** and zero-length blob if X is equal to Y. | |
| 89 | 89 | */ |
| 90 | 90 | static void mkdeltaFunc( |
| 91 | 91 | sqlite3_context *context, |
| 92 | 92 | int argc, |
| 93 | 93 | sqlite3_value **argv |
| @@ -126,10 +126,11 @@ | ||
| 126 | 126 | if( blob_size(&x)==blob_size(&y) |
| 127 | 127 | && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 |
| 128 | 128 | ){ |
| 129 | 129 | blob_reset(&y); |
| 130 | 130 | blob_reset(&x); |
| 131 | + sqlite3_result_blob64(context, "", 0, SQLITE_STATIC); | |
| 131 | 132 | return; |
| 132 | 133 | } |
| 133 | 134 | nOut = delta_create(blob_buffer(&x),blob_size(&x), |
| 134 | 135 | blob_buffer(&y),blob_size(&y), aOut); |
| 135 | 136 | blob_reset(&x); |
| @@ -167,11 +168,12 @@ | ||
| 167 | 168 | " pathname TEXT,\n" /* Filename */ |
| 168 | 169 | " origname TEXT,\n" /* Name before rename. NULL if not renamed */ |
| 169 | 170 | " hash TEXT,\n" /* Baseline hash. NULL for new files. */ |
| 170 | 171 | " isexe BOOL,\n" /* True if executable */ |
| 171 | 172 | " islink BOOL,\n" /* True if is a symbolic link */ |
| 172 | - " delta BLOB\n" /* Delta. NULL if file deleted or unchanged */ | |
| 173 | + " delta BLOB\n" /* compressed delta. NULL if deleted. | |
| 174 | + ** length 0 if unchanged */ | |
| 173 | 175 | ");" |
| 174 | 176 | "CREATE TABLE patch.cfg(\n" |
| 175 | 177 | " key TEXT,\n" |
| 176 | 178 | " value ANY\n" |
| 177 | 179 | ");" |
| @@ -322,14 +324,14 @@ | ||
| 322 | 324 | db_column_text(&q,1)); |
| 323 | 325 | } |
| 324 | 326 | db_finalize(&q); |
| 325 | 327 | } |
| 326 | 328 | db_prepare(&q, |
| 327 | - "SELECT pathname," | |
| 328 | - " hash IS NULL AND delta IS NOT NULL," /* isNew */ | |
| 329 | - " delta IS NULL," /* delete if origname NULL */ | |
| 330 | - " origname" | |
| 329 | + "SELECT pathname," /* 0: new name */ | |
| 330 | + " hash IS NULL AND delta IS NOT NULL," /* 1: isNew */ | |
| 331 | + " delta IS NULL," /* 2: isDeleted */ | |
| 332 | + " origname" /* 3: old name or NULL */ | |
| 331 | 333 | " FROM patch.chng ORDER BY 1"); |
| 332 | 334 | while( db_step(&q)==SQLITE_ROW ){ |
| 333 | 335 | const char *zClass = "EDIT"; |
| 334 | 336 | const char *zName = db_column_text(&q,0); |
| 335 | 337 | const char *zOrigName = db_column_text(&q, 3); |
| @@ -486,13 +488,17 @@ | ||
| 486 | 488 | int isExe = db_column_int(&q,2); |
| 487 | 489 | int isLink = db_column_int(&q,3); |
| 488 | 490 | Blob data; |
| 489 | 491 | |
| 490 | 492 | blob_init(&data, 0, 0); |
| 491 | - db_column_blob(&q, 4, &data); | |
| 492 | - blob_uncompress(&data, &data); | |
| 493 | - if( zHash ){ | |
| 493 | + db_ephemeral_blob(&q, 4, &data); | |
| 494 | + if( blob_size(&data) ){ | |
| 495 | + blob_uncompress(&data, &data); | |
| 496 | + } | |
| 497 | + if( blob_size(&data)==0 ){ | |
| 498 | + /* No changes to the file */ | |
| 499 | + }else if( zHash ){ | |
| 494 | 500 | Blob basis; |
| 495 | 501 | int rid = fast_uuid_to_rid(zHash); |
| 496 | 502 | int outSize, sz; |
| 497 | 503 | char *aOut; |
| 498 | 504 | if( rid==0 ){ |
| @@ -553,19 +559,35 @@ | ||
| 553 | 559 | blob_reset(&cmd); |
| 554 | 560 | } |
| 555 | 561 | } |
| 556 | 562 | |
| 557 | 563 | /* |
| 558 | -** Find the filename of the patch file to be used by | |
| 559 | -** "fossil patch apply" or "fossil patch create". | |
| 564 | +** This routine processes the | |
| 565 | +** | |
| 566 | +** ... [--dir64 DIR64] [DIRECTORY] FILENAME | |
| 567 | +** | |
| 568 | +** part of various "fossil patch" subcommands. | |
| 569 | +** | |
| 570 | +** Find and return the filename of the patch file to be used by | |
| 571 | +** "fossil patch apply" or "fossil patch create". Space to hold | |
| 572 | +** the returned name is obtained from fossil_malloc() and should | |
| 573 | +** be freed by the caller. | |
| 560 | 574 | ** |
| 561 | -** If the name is "-" return NULL. | |
| 575 | +** If the name is "-" return NULL. The caller will interpret this | |
| 576 | +** to mean the patch is coming in over stdin or going out over | |
| 577 | +** stdout. | |
| 562 | 578 | ** |
| 563 | -** Otherwise, if there is a prior DIRECTORY argument, or if | |
| 579 | +** If there is a prior DIRECTORY argument, or if | |
| 564 | 580 | ** the --dir64 option is present, first chdir to the specified |
| 565 | -** directory, and translate the name in the argument accordingly. | |
| 581 | +** directory, and adjust the path of FILENAME as appropriate so | |
| 582 | +** that it still points to the same file. | |
| 566 | 583 | ** |
| 584 | +** The --dir64 option is undocumented. The argument to --dir64 | |
| 585 | +** is a base64-encoded directory name. The --dir64 option is used | |
| 586 | +** to transmit the directory as part of the command argument to | |
| 587 | +** a "ssh" command without having to worry about quoting | |
| 588 | +** any special characters in the filename. | |
| 567 | 589 | ** |
| 568 | 590 | ** The returned name is obtained from fossil_malloc() and should |
| 569 | 591 | ** be freed by the caller. |
| 570 | 592 | */ |
| 571 | 593 | static char *patch_find_patch_filename(const char *zCmdName){ |
| @@ -604,10 +626,12 @@ | ||
| 604 | 626 | } |
| 605 | 627 | |
| 606 | 628 | /* |
| 607 | 629 | ** Create a FILE* that will execute the remote side of a push or pull |
| 608 | 630 | ** using ssh (probably) or fossil for local pushes and pulls. Return |
| 631 | +** a FILE* obtained from popen() into which we write the patch, or from | |
| 632 | +** which we read the patch, depending on whether this is a push or pull. | |
| 609 | 633 | */ |
| 610 | 634 | static FILE *patch_remote_command( |
| 611 | 635 | unsigned mFlags, /* flags */ |
| 612 | 636 | const char *zThisCmd, /* "push" or "pull" */ |
| 613 | 637 | const char *zRemoteCmd, /* "apply" or "create" */ |
| @@ -650,10 +674,73 @@ | ||
| 650 | 674 | } |
| 651 | 675 | blob_reset(&cmd); |
| 652 | 676 | return f; |
| 653 | 677 | } |
| 654 | 678 | |
| 679 | +/* | |
| 680 | +** Show a diff for the patch currently loaded into database "patch". | |
| 681 | +*/ | |
| 682 | +static void patch_diff( | |
| 683 | + const char *zDiffCmd, /* Command used for diffing */ | |
| 684 | + const char *zBinGlob, /* GLOB pattern to determine binary files */ | |
| 685 | + int fIncludeBinary, /* Do diffs against binary files */ | |
| 686 | + u64 diffFlags /* Other diff flags */ | |
| 687 | +){ | |
| 688 | + Stmt q; | |
| 689 | + Blob empty; | |
| 690 | + blob_zero(&empty); | |
| 691 | + db_prepare(&q, | |
| 692 | + "SELECT" | |
| 693 | + " blob.rid," /* 0: rid of the baseline */ | |
| 694 | + " pathname," /* 1: new pathname */ | |
| 695 | + " origname," /* 2: original pathname. Null if not renamed */ | |
| 696 | + " delta" /* 3: delta. NULL if deleted. empty is no change */ | |
| 697 | + " FROM patch.chng, blob WHERE blob.uuid=patch.chng.hash" | |
| 698 | + " ORDER BY pathname" | |
| 699 | + ); | |
| 700 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 701 | + int rid = db_column_int(&q, 0); | |
| 702 | +// const char *zOrig = db_column_text(&q, 2); | |
| 703 | + const char *zName = db_column_text(&q, 1); | |
| 704 | + int isBin1, isBin2; | |
| 705 | + Blob a, b; | |
| 706 | + if( db_column_type(&q,3)==SQLITE_NULL ){ | |
| 707 | + fossil_print("DELETE %s\n", zName); | |
| 708 | + diff_print_index(zName, diffFlags, 0); | |
| 709 | + isBin2 = 0; | |
| 710 | + content_get(rid, &a); | |
| 711 | + isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); | |
| 712 | + diff_file_mem(&a, &empty, isBin1, isBin2, zName, zDiffCmd, | |
| 713 | + zBinGlob, fIncludeBinary, diffFlags); | |
| 714 | + }else if( rid==0 ){ | |
| 715 | + db_ephemeral_blob(&q, 3, &a); | |
| 716 | + blob_uncompress(&a, &a); | |
| 717 | + fossil_print("ADDED %s\n", zName); | |
| 718 | + diff_print_index(zName, diffFlags, 0); | |
| 719 | + isBin1 = 0; | |
| 720 | + isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a); | |
| 721 | + diff_file_mem(&empty, &a, isBin1, isBin2, zName, zDiffCmd, | |
| 722 | + zBinGlob, fIncludeBinary, diffFlags); | |
| 723 | + blob_reset(&a); | |
| 724 | + }else if( db_column_bytes(&q, 3)>0 ){ | |
| 725 | + Blob delta; | |
| 726 | + db_ephemeral_blob(&q, 3, &delta); | |
| 727 | + blob_uncompress(&delta, &delta); | |
| 728 | + content_get(rid, &a); | |
| 729 | + blob_delta_apply(&a, &delta, &b); | |
| 730 | + isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); | |
| 731 | + isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b); | |
| 732 | + diff_file_mem(&a, &b, isBin1, isBin2, zName, | |
| 733 | + zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); | |
| 734 | + blob_reset(&a); | |
| 735 | + blob_reset(&b); | |
| 736 | + blob_reset(&delta); | |
| 737 | + } | |
| 738 | + } | |
| 739 | + db_finalize(&q); | |
| 740 | +} | |
| 741 | + | |
| 655 | 742 | |
| 656 | 743 | /* |
| 657 | 744 | ** COMMAND: patch |
| 658 | 745 | ** |
| 659 | 746 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| @@ -678,10 +765,15 @@ | ||
| 678 | 765 | ** -f|--force Apply the patch even though there are unsaved |
| 679 | 766 | ** changes in the current check-out. |
| 680 | 767 | ** -n|--dryrun Do nothing, but print what would have happened. |
| 681 | 768 | ** -v|--verbose Extra output explaining what happens. |
| 682 | 769 | ** |
| 770 | +** > fossil patch diff [DIRECTORY] FILENAME | |
| 771 | +** | |
| 772 | +** Show a human-readable diff for the patch. All the usual | |
| 773 | +** diff flags apply. (See help for "fossil diff"). | |
| 774 | +** | |
| 683 | 775 | ** > fossil patch push REMOTE-CHECKOUT |
| 684 | 776 | ** |
| 685 | 777 | ** Create a patch for the current check-out, transfer that patch to |
| 686 | 778 | ** a remote machine (using ssh) and apply the patch there. The |
| 687 | 779 | ** REMOTE-CHECKOUT is in one of the following formats: |
| @@ -733,10 +825,38 @@ | ||
| 733 | 825 | char *zOut; |
| 734 | 826 | zOut = patch_find_patch_filename("create"); |
| 735 | 827 | db_must_be_within_tree(); |
| 736 | 828 | patch_create(zOut, stdout); |
| 737 | 829 | fossil_free(zOut); |
| 830 | + }else | |
| 831 | + if( strncmp(zCmd, "diff", n)==0 ){ | |
| 832 | + const char *zDiffCmd = 0; | |
| 833 | + const char *zBinGlob = 0; | |
| 834 | + int fIncludeBinary = 0; | |
| 835 | + u64 diffFlags; | |
| 836 | + char *zIn; | |
| 837 | + | |
| 838 | + if( find_option("tk",0,0)!=0 ){ | |
| 839 | + db_close(0); | |
| 840 | + diff_tk("patch diff", 3); | |
| 841 | + return; | |
| 842 | + } | |
| 843 | + if( find_option("internal","i",0)==0 ){ | |
| 844 | + zDiffCmd = diff_command_external(zCmd[0]=='g'); | |
| 845 | + } | |
| 846 | + diffFlags = diff_options(); | |
| 847 | + if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; | |
| 848 | + if( zDiffCmd ){ | |
| 849 | + zBinGlob = diff_get_binary_glob(); | |
| 850 | + fIncludeBinary = diff_include_binary_files(); | |
| 851 | + } | |
| 852 | + zIn = patch_find_patch_filename("apply"); | |
| 853 | + db_must_be_within_tree(); | |
| 854 | + verify_all_options(); | |
| 855 | + patch_attach(zIn, stdin); | |
| 856 | + patch_diff( zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); | |
| 857 | + fossil_free(zIn); | |
| 738 | 858 | }else |
| 739 | 859 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 740 | 860 | FILE *pIn = 0; |
| 741 | 861 | unsigned flags = 0; |
| 742 | 862 | if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN; |
| 743 | 863 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -82,12 +82,12 @@ | |
| 82 | /* |
| 83 | ** mkdelta(X,Y) |
| 84 | ** |
| 85 | ** X is an numeric artifact id. Y is a filename. |
| 86 | ** |
| 87 | ** Compute a compressed delta that carries X into Y. Or return NULL |
| 88 | ** if X is equal to Y. |
| 89 | */ |
| 90 | static void mkdeltaFunc( |
| 91 | sqlite3_context *context, |
| 92 | int argc, |
| 93 | sqlite3_value **argv |
| @@ -126,10 +126,11 @@ | |
| 126 | if( blob_size(&x)==blob_size(&y) |
| 127 | && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 |
| 128 | ){ |
| 129 | blob_reset(&y); |
| 130 | blob_reset(&x); |
| 131 | return; |
| 132 | } |
| 133 | nOut = delta_create(blob_buffer(&x),blob_size(&x), |
| 134 | blob_buffer(&y),blob_size(&y), aOut); |
| 135 | blob_reset(&x); |
| @@ -167,11 +168,12 @@ | |
| 167 | " pathname TEXT,\n" /* Filename */ |
| 168 | " origname TEXT,\n" /* Name before rename. NULL if not renamed */ |
| 169 | " hash TEXT,\n" /* Baseline hash. NULL for new files. */ |
| 170 | " isexe BOOL,\n" /* True if executable */ |
| 171 | " islink BOOL,\n" /* True if is a symbolic link */ |
| 172 | " delta BLOB\n" /* Delta. NULL if file deleted or unchanged */ |
| 173 | ");" |
| 174 | "CREATE TABLE patch.cfg(\n" |
| 175 | " key TEXT,\n" |
| 176 | " value ANY\n" |
| 177 | ");" |
| @@ -322,14 +324,14 @@ | |
| 322 | db_column_text(&q,1)); |
| 323 | } |
| 324 | db_finalize(&q); |
| 325 | } |
| 326 | db_prepare(&q, |
| 327 | "SELECT pathname," |
| 328 | " hash IS NULL AND delta IS NOT NULL," /* isNew */ |
| 329 | " delta IS NULL," /* delete if origname NULL */ |
| 330 | " origname" |
| 331 | " FROM patch.chng ORDER BY 1"); |
| 332 | while( db_step(&q)==SQLITE_ROW ){ |
| 333 | const char *zClass = "EDIT"; |
| 334 | const char *zName = db_column_text(&q,0); |
| 335 | const char *zOrigName = db_column_text(&q, 3); |
| @@ -486,13 +488,17 @@ | |
| 486 | int isExe = db_column_int(&q,2); |
| 487 | int isLink = db_column_int(&q,3); |
| 488 | Blob data; |
| 489 | |
| 490 | blob_init(&data, 0, 0); |
| 491 | db_column_blob(&q, 4, &data); |
| 492 | blob_uncompress(&data, &data); |
| 493 | if( zHash ){ |
| 494 | Blob basis; |
| 495 | int rid = fast_uuid_to_rid(zHash); |
| 496 | int outSize, sz; |
| 497 | char *aOut; |
| 498 | if( rid==0 ){ |
| @@ -553,19 +559,35 @@ | |
| 553 | blob_reset(&cmd); |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | /* |
| 558 | ** Find the filename of the patch file to be used by |
| 559 | ** "fossil patch apply" or "fossil patch create". |
| 560 | ** |
| 561 | ** If the name is "-" return NULL. |
| 562 | ** |
| 563 | ** Otherwise, if there is a prior DIRECTORY argument, or if |
| 564 | ** the --dir64 option is present, first chdir to the specified |
| 565 | ** directory, and translate the name in the argument accordingly. |
| 566 | ** |
| 567 | ** |
| 568 | ** The returned name is obtained from fossil_malloc() and should |
| 569 | ** be freed by the caller. |
| 570 | */ |
| 571 | static char *patch_find_patch_filename(const char *zCmdName){ |
| @@ -604,10 +626,12 @@ | |
| 604 | } |
| 605 | |
| 606 | /* |
| 607 | ** Create a FILE* that will execute the remote side of a push or pull |
| 608 | ** using ssh (probably) or fossil for local pushes and pulls. Return |
| 609 | */ |
| 610 | static FILE *patch_remote_command( |
| 611 | unsigned mFlags, /* flags */ |
| 612 | const char *zThisCmd, /* "push" or "pull" */ |
| 613 | const char *zRemoteCmd, /* "apply" or "create" */ |
| @@ -650,10 +674,73 @@ | |
| 650 | } |
| 651 | blob_reset(&cmd); |
| 652 | return f; |
| 653 | } |
| 654 | |
| 655 | |
| 656 | /* |
| 657 | ** COMMAND: patch |
| 658 | ** |
| 659 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| @@ -678,10 +765,15 @@ | |
| 678 | ** -f|--force Apply the patch even though there are unsaved |
| 679 | ** changes in the current check-out. |
| 680 | ** -n|--dryrun Do nothing, but print what would have happened. |
| 681 | ** -v|--verbose Extra output explaining what happens. |
| 682 | ** |
| 683 | ** > fossil patch push REMOTE-CHECKOUT |
| 684 | ** |
| 685 | ** Create a patch for the current check-out, transfer that patch to |
| 686 | ** a remote machine (using ssh) and apply the patch there. The |
| 687 | ** REMOTE-CHECKOUT is in one of the following formats: |
| @@ -733,10 +825,38 @@ | |
| 733 | char *zOut; |
| 734 | zOut = patch_find_patch_filename("create"); |
| 735 | db_must_be_within_tree(); |
| 736 | patch_create(zOut, stdout); |
| 737 | fossil_free(zOut); |
| 738 | }else |
| 739 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 740 | FILE *pIn = 0; |
| 741 | unsigned flags = 0; |
| 742 | if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN; |
| 743 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -82,12 +82,12 @@ | |
| 82 | /* |
| 83 | ** mkdelta(X,Y) |
| 84 | ** |
| 85 | ** X is an numeric artifact id. Y is a filename. |
| 86 | ** |
| 87 | ** Compute a compressed delta that carries X into Y. Or return |
| 88 | ** and zero-length blob if X is equal to Y. |
| 89 | */ |
| 90 | static void mkdeltaFunc( |
| 91 | sqlite3_context *context, |
| 92 | int argc, |
| 93 | sqlite3_value **argv |
| @@ -126,10 +126,11 @@ | |
| 126 | if( blob_size(&x)==blob_size(&y) |
| 127 | && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 |
| 128 | ){ |
| 129 | blob_reset(&y); |
| 130 | blob_reset(&x); |
| 131 | sqlite3_result_blob64(context, "", 0, SQLITE_STATIC); |
| 132 | return; |
| 133 | } |
| 134 | nOut = delta_create(blob_buffer(&x),blob_size(&x), |
| 135 | blob_buffer(&y),blob_size(&y), aOut); |
| 136 | blob_reset(&x); |
| @@ -167,11 +168,12 @@ | |
| 168 | " pathname TEXT,\n" /* Filename */ |
| 169 | " origname TEXT,\n" /* Name before rename. NULL if not renamed */ |
| 170 | " hash TEXT,\n" /* Baseline hash. NULL for new files. */ |
| 171 | " isexe BOOL,\n" /* True if executable */ |
| 172 | " islink BOOL,\n" /* True if is a symbolic link */ |
| 173 | " delta BLOB\n" /* compressed delta. NULL if deleted. |
| 174 | ** length 0 if unchanged */ |
| 175 | ");" |
| 176 | "CREATE TABLE patch.cfg(\n" |
| 177 | " key TEXT,\n" |
| 178 | " value ANY\n" |
| 179 | ");" |
| @@ -322,14 +324,14 @@ | |
| 324 | db_column_text(&q,1)); |
| 325 | } |
| 326 | db_finalize(&q); |
| 327 | } |
| 328 | db_prepare(&q, |
| 329 | "SELECT pathname," /* 0: new name */ |
| 330 | " hash IS NULL AND delta IS NOT NULL," /* 1: isNew */ |
| 331 | " delta IS NULL," /* 2: isDeleted */ |
| 332 | " origname" /* 3: old name or NULL */ |
| 333 | " FROM patch.chng ORDER BY 1"); |
| 334 | while( db_step(&q)==SQLITE_ROW ){ |
| 335 | const char *zClass = "EDIT"; |
| 336 | const char *zName = db_column_text(&q,0); |
| 337 | const char *zOrigName = db_column_text(&q, 3); |
| @@ -486,13 +488,17 @@ | |
| 488 | int isExe = db_column_int(&q,2); |
| 489 | int isLink = db_column_int(&q,3); |
| 490 | Blob data; |
| 491 | |
| 492 | blob_init(&data, 0, 0); |
| 493 | db_ephemeral_blob(&q, 4, &data); |
| 494 | if( blob_size(&data) ){ |
| 495 | blob_uncompress(&data, &data); |
| 496 | } |
| 497 | if( blob_size(&data)==0 ){ |
| 498 | /* No changes to the file */ |
| 499 | }else if( zHash ){ |
| 500 | Blob basis; |
| 501 | int rid = fast_uuid_to_rid(zHash); |
| 502 | int outSize, sz; |
| 503 | char *aOut; |
| 504 | if( rid==0 ){ |
| @@ -553,19 +559,35 @@ | |
| 559 | blob_reset(&cmd); |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | /* |
| 564 | ** This routine processes the |
| 565 | ** |
| 566 | ** ... [--dir64 DIR64] [DIRECTORY] FILENAME |
| 567 | ** |
| 568 | ** part of various "fossil patch" subcommands. |
| 569 | ** |
| 570 | ** Find and return the filename of the patch file to be used by |
| 571 | ** "fossil patch apply" or "fossil patch create". Space to hold |
| 572 | ** the returned name is obtained from fossil_malloc() and should |
| 573 | ** be freed by the caller. |
| 574 | ** |
| 575 | ** If the name is "-" return NULL. The caller will interpret this |
| 576 | ** to mean the patch is coming in over stdin or going out over |
| 577 | ** stdout. |
| 578 | ** |
| 579 | ** If there is a prior DIRECTORY argument, or if |
| 580 | ** the --dir64 option is present, first chdir to the specified |
| 581 | ** directory, and adjust the path of FILENAME as appropriate so |
| 582 | ** that it still points to the same file. |
| 583 | ** |
| 584 | ** The --dir64 option is undocumented. The argument to --dir64 |
| 585 | ** is a base64-encoded directory name. The --dir64 option is used |
| 586 | ** to transmit the directory as part of the command argument to |
| 587 | ** a "ssh" command without having to worry about quoting |
| 588 | ** any special characters in the filename. |
| 589 | ** |
| 590 | ** The returned name is obtained from fossil_malloc() and should |
| 591 | ** be freed by the caller. |
| 592 | */ |
| 593 | static char *patch_find_patch_filename(const char *zCmdName){ |
| @@ -604,10 +626,12 @@ | |
| 626 | } |
| 627 | |
| 628 | /* |
| 629 | ** Create a FILE* that will execute the remote side of a push or pull |
| 630 | ** using ssh (probably) or fossil for local pushes and pulls. Return |
| 631 | ** a FILE* obtained from popen() into which we write the patch, or from |
| 632 | ** which we read the patch, depending on whether this is a push or pull. |
| 633 | */ |
| 634 | static FILE *patch_remote_command( |
| 635 | unsigned mFlags, /* flags */ |
| 636 | const char *zThisCmd, /* "push" or "pull" */ |
| 637 | const char *zRemoteCmd, /* "apply" or "create" */ |
| @@ -650,10 +674,73 @@ | |
| 674 | } |
| 675 | blob_reset(&cmd); |
| 676 | return f; |
| 677 | } |
| 678 | |
| 679 | /* |
| 680 | ** Show a diff for the patch currently loaded into database "patch". |
| 681 | */ |
| 682 | static void patch_diff( |
| 683 | const char *zDiffCmd, /* Command used for diffing */ |
| 684 | const char *zBinGlob, /* GLOB pattern to determine binary files */ |
| 685 | int fIncludeBinary, /* Do diffs against binary files */ |
| 686 | u64 diffFlags /* Other diff flags */ |
| 687 | ){ |
| 688 | Stmt q; |
| 689 | Blob empty; |
| 690 | blob_zero(&empty); |
| 691 | db_prepare(&q, |
| 692 | "SELECT" |
| 693 | " blob.rid," /* 0: rid of the baseline */ |
| 694 | " pathname," /* 1: new pathname */ |
| 695 | " origname," /* 2: original pathname. Null if not renamed */ |
| 696 | " delta" /* 3: delta. NULL if deleted. empty is no change */ |
| 697 | " FROM patch.chng, blob WHERE blob.uuid=patch.chng.hash" |
| 698 | " ORDER BY pathname" |
| 699 | ); |
| 700 | while( db_step(&q)==SQLITE_ROW ){ |
| 701 | int rid = db_column_int(&q, 0); |
| 702 | // const char *zOrig = db_column_text(&q, 2); |
| 703 | const char *zName = db_column_text(&q, 1); |
| 704 | int isBin1, isBin2; |
| 705 | Blob a, b; |
| 706 | if( db_column_type(&q,3)==SQLITE_NULL ){ |
| 707 | fossil_print("DELETE %s\n", zName); |
| 708 | diff_print_index(zName, diffFlags, 0); |
| 709 | isBin2 = 0; |
| 710 | content_get(rid, &a); |
| 711 | isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); |
| 712 | diff_file_mem(&a, &empty, isBin1, isBin2, zName, zDiffCmd, |
| 713 | zBinGlob, fIncludeBinary, diffFlags); |
| 714 | }else if( rid==0 ){ |
| 715 | db_ephemeral_blob(&q, 3, &a); |
| 716 | blob_uncompress(&a, &a); |
| 717 | fossil_print("ADDED %s\n", zName); |
| 718 | diff_print_index(zName, diffFlags, 0); |
| 719 | isBin1 = 0; |
| 720 | isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a); |
| 721 | diff_file_mem(&empty, &a, isBin1, isBin2, zName, zDiffCmd, |
| 722 | zBinGlob, fIncludeBinary, diffFlags); |
| 723 | blob_reset(&a); |
| 724 | }else if( db_column_bytes(&q, 3)>0 ){ |
| 725 | Blob delta; |
| 726 | db_ephemeral_blob(&q, 3, &delta); |
| 727 | blob_uncompress(&delta, &delta); |
| 728 | content_get(rid, &a); |
| 729 | blob_delta_apply(&a, &delta, &b); |
| 730 | isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); |
| 731 | isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b); |
| 732 | diff_file_mem(&a, &b, isBin1, isBin2, zName, |
| 733 | zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); |
| 734 | blob_reset(&a); |
| 735 | blob_reset(&b); |
| 736 | blob_reset(&delta); |
| 737 | } |
| 738 | } |
| 739 | db_finalize(&q); |
| 740 | } |
| 741 | |
| 742 | |
| 743 | /* |
| 744 | ** COMMAND: patch |
| 745 | ** |
| 746 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| @@ -678,10 +765,15 @@ | |
| 765 | ** -f|--force Apply the patch even though there are unsaved |
| 766 | ** changes in the current check-out. |
| 767 | ** -n|--dryrun Do nothing, but print what would have happened. |
| 768 | ** -v|--verbose Extra output explaining what happens. |
| 769 | ** |
| 770 | ** > fossil patch diff [DIRECTORY] FILENAME |
| 771 | ** |
| 772 | ** Show a human-readable diff for the patch. All the usual |
| 773 | ** diff flags apply. (See help for "fossil diff"). |
| 774 | ** |
| 775 | ** > fossil patch push REMOTE-CHECKOUT |
| 776 | ** |
| 777 | ** Create a patch for the current check-out, transfer that patch to |
| 778 | ** a remote machine (using ssh) and apply the patch there. The |
| 779 | ** REMOTE-CHECKOUT is in one of the following formats: |
| @@ -733,10 +825,38 @@ | |
| 825 | char *zOut; |
| 826 | zOut = patch_find_patch_filename("create"); |
| 827 | db_must_be_within_tree(); |
| 828 | patch_create(zOut, stdout); |
| 829 | fossil_free(zOut); |
| 830 | }else |
| 831 | if( strncmp(zCmd, "diff", n)==0 ){ |
| 832 | const char *zDiffCmd = 0; |
| 833 | const char *zBinGlob = 0; |
| 834 | int fIncludeBinary = 0; |
| 835 | u64 diffFlags; |
| 836 | char *zIn; |
| 837 | |
| 838 | if( find_option("tk",0,0)!=0 ){ |
| 839 | db_close(0); |
| 840 | diff_tk("patch diff", 3); |
| 841 | return; |
| 842 | } |
| 843 | if( find_option("internal","i",0)==0 ){ |
| 844 | zDiffCmd = diff_command_external(zCmd[0]=='g'); |
| 845 | } |
| 846 | diffFlags = diff_options(); |
| 847 | if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 848 | if( zDiffCmd ){ |
| 849 | zBinGlob = diff_get_binary_glob(); |
| 850 | fIncludeBinary = diff_include_binary_files(); |
| 851 | } |
| 852 | zIn = patch_find_patch_filename("apply"); |
| 853 | db_must_be_within_tree(); |
| 854 | verify_all_options(); |
| 855 | patch_attach(zIn, stdin); |
| 856 | patch_diff( zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); |
| 857 | fossil_free(zIn); |
| 858 | }else |
| 859 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 860 | FILE *pIn = 0; |
| 861 | unsigned flags = 0; |
| 862 | if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN; |
| 863 |
+1
-2
| --- src/stash.c | ||
| +++ src/stash.c | ||
| @@ -442,11 +442,10 @@ | ||
| 442 | 442 | if( fBaseline ){ |
| 443 | 443 | content_get(rid, &a); |
| 444 | 444 | isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); |
| 445 | 445 | diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd, |
| 446 | 446 | zBinGlob, fIncludeBinary, diffFlags); |
| 447 | - }else{ | |
| 448 | 447 | } |
| 449 | 448 | }else{ |
| 450 | 449 | Blob delta; |
| 451 | 450 | int isOrigLink = file_islink(zOPath); |
| 452 | 451 | db_ephemeral_blob(&q, 6, &delta); |
| @@ -472,11 +471,11 @@ | ||
| 472 | 471 | blob_reset(&a); |
| 473 | 472 | blob_reset(&b); |
| 474 | 473 | } |
| 475 | 474 | blob_reset(&delta); |
| 476 | 475 | } |
| 477 | - } | |
| 476 | + } | |
| 478 | 477 | db_finalize(&q); |
| 479 | 478 | } |
| 480 | 479 | |
| 481 | 480 | /* |
| 482 | 481 | ** Drop the indicated stash |
| 483 | 482 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -442,11 +442,10 @@ | |
| 442 | if( fBaseline ){ |
| 443 | content_get(rid, &a); |
| 444 | isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); |
| 445 | diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd, |
| 446 | zBinGlob, fIncludeBinary, diffFlags); |
| 447 | }else{ |
| 448 | } |
| 449 | }else{ |
| 450 | Blob delta; |
| 451 | int isOrigLink = file_islink(zOPath); |
| 452 | db_ephemeral_blob(&q, 6, &delta); |
| @@ -472,11 +471,11 @@ | |
| 472 | blob_reset(&a); |
| 473 | blob_reset(&b); |
| 474 | } |
| 475 | blob_reset(&delta); |
| 476 | } |
| 477 | } |
| 478 | db_finalize(&q); |
| 479 | } |
| 480 | |
| 481 | /* |
| 482 | ** Drop the indicated stash |
| 483 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -442,11 +442,10 @@ | |
| 442 | if( fBaseline ){ |
| 443 | content_get(rid, &a); |
| 444 | isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); |
| 445 | diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd, |
| 446 | zBinGlob, fIncludeBinary, diffFlags); |
| 447 | } |
| 448 | }else{ |
| 449 | Blob delta; |
| 450 | int isOrigLink = file_islink(zOPath); |
| 451 | db_ephemeral_blob(&q, 6, &delta); |
| @@ -472,11 +471,11 @@ | |
| 471 | blob_reset(&a); |
| 472 | blob_reset(&b); |
| 473 | } |
| 474 | blob_reset(&delta); |
| 475 | } |
| 476 | } |
| 477 | db_finalize(&q); |
| 478 | } |
| 479 | |
| 480 | /* |
| 481 | ** Drop the indicated stash |
| 482 |