Fossil SCM
Fix the "fossil patch push" and "fossil patch pull" commands so that, like "fossil sync", they initial try to run ssh without the PATH= argument, but add in the PATH= argument if the initial attempt does not work.
Commit
eb135ef204f467963fe293ffa14f88aae7556c38e7a6931849aee0eac39db108
Parent
efd3a5ec0760f59…
2 files changed
+43
-4
+70
-16
+43
-4
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -275,25 +275,64 @@ | ||
| 275 | 275 | /* If iTruth<0 then guess as to whether or not a PATH= argument is required |
| 276 | 276 | ** when using ssh to run fossil on a remote machine name zHostname. |
| 277 | 277 | ** |
| 278 | 278 | ** If iTruth is 1 or 0 then that means that the PATH= is or is not required, |
| 279 | 279 | ** respectively. Record this fact for future reference. |
| 280 | +** | |
| 281 | +** If iTruth is 99 or more, then toggle the truth value. | |
| 280 | 282 | */ |
| 281 | 283 | int ssh_needs_path_argument(const char *zHostname, int iTruth){ |
| 282 | 284 | int ans = 0; /* Default to "no" */ |
| 283 | 285 | char *z = mprintf("use-path-for-ssh:%s", zHostname); |
| 284 | 286 | if( iTruth<0 ){ |
| 285 | 287 | if( db_get_boolean(z/*works-like:"x"*/, 0) ) ans = 1; |
| 286 | - }else if( iTruth ){ | |
| 287 | - ans = 1; | |
| 288 | - db_set(z/*works-like:"x"*/, "1", 0); | |
| 289 | 288 | }else{ |
| 290 | - db_unset(z/*works-like:"x"*/, 0); | |
| 289 | + if( iTruth>=99 ){ | |
| 290 | + iTruth = !db_get_boolean(z/*works-like:"x"*/, 0); | |
| 291 | + } | |
| 292 | + if( iTruth ){ | |
| 293 | + ans = 1; | |
| 294 | + db_set(z/*works-like:"x"*/, "1", 0); | |
| 295 | + }else{ | |
| 296 | + db_unset(z/*works-like:"x"*/, 0); | |
| 297 | + } | |
| 291 | 298 | } |
| 292 | 299 | fossil_free(z); |
| 293 | 300 | return ans; |
| 294 | 301 | } |
| 302 | + | |
| 303 | +/* | |
| 304 | +** COMMAND: test-ssh-needs-path | |
| 305 | +** | |
| 306 | +** Usage: fossil test-ssh-needs-path HOSTNAME ?BOOLEAN? | |
| 307 | +** | |
| 308 | +** With one argument, show whether or not the PATH= argument is included | |
| 309 | +** by default for HOSTNAME. If the second argument is a boolean, then | |
| 310 | +** change the value. | |
| 311 | +** | |
| 312 | +** With no arguments, show all hosts for which ssh-needs-path is true. | |
| 313 | +*/ | |
| 314 | +void test_ssh_needs_path(void){ | |
| 315 | + db_find_and_open_repository(0,0); | |
| 316 | + if( g.argc>=3 ){ | |
| 317 | + const char *zHost = g.argv[2]; | |
| 318 | + int a = -1; | |
| 319 | + int rc; | |
| 320 | + if( g.argc>=4 ) a = is_truth(g.argv[3]); | |
| 321 | + rc = ssh_needs_path_argument(zHost, a); | |
| 322 | + fossil_print("%-20s %s\n", zHost, rc ? "yes" : "no"); | |
| 323 | + }else{ | |
| 324 | + Stmt s; | |
| 325 | + db_prepare(&s, "SELECT substr(name,18) FROM config" | |
| 326 | + " WHERE name GLOB 'use-path-for-ssh:*'"); | |
| 327 | + while( db_step(&s)==SQLITE_ROW ){ | |
| 328 | + const char *zHost = db_column_text(&s,0); | |
| 329 | + fossil_print("%-20s yes\n", zHost); | |
| 330 | + } | |
| 331 | + db_finalize(&s); | |
| 332 | + } | |
| 333 | +} | |
| 295 | 334 | |
| 296 | 335 | /* Add an approprate PATH= argument to the SSH command under construction |
| 297 | 336 | ** in pCmd. |
| 298 | 337 | */ |
| 299 | 338 | void ssh_add_path_argument(Blob *pCmd){ |
| 300 | 339 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -275,25 +275,64 @@ | |
| 275 | /* If iTruth<0 then guess as to whether or not a PATH= argument is required |
| 276 | ** when using ssh to run fossil on a remote machine name zHostname. |
| 277 | ** |
| 278 | ** If iTruth is 1 or 0 then that means that the PATH= is or is not required, |
| 279 | ** respectively. Record this fact for future reference. |
| 280 | */ |
| 281 | int ssh_needs_path_argument(const char *zHostname, int iTruth){ |
| 282 | int ans = 0; /* Default to "no" */ |
| 283 | char *z = mprintf("use-path-for-ssh:%s", zHostname); |
| 284 | if( iTruth<0 ){ |
| 285 | if( db_get_boolean(z/*works-like:"x"*/, 0) ) ans = 1; |
| 286 | }else if( iTruth ){ |
| 287 | ans = 1; |
| 288 | db_set(z/*works-like:"x"*/, "1", 0); |
| 289 | }else{ |
| 290 | db_unset(z/*works-like:"x"*/, 0); |
| 291 | } |
| 292 | fossil_free(z); |
| 293 | return ans; |
| 294 | } |
| 295 | |
| 296 | /* Add an approprate PATH= argument to the SSH command under construction |
| 297 | ** in pCmd. |
| 298 | */ |
| 299 | void ssh_add_path_argument(Blob *pCmd){ |
| 300 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -275,25 +275,64 @@ | |
| 275 | /* If iTruth<0 then guess as to whether or not a PATH= argument is required |
| 276 | ** when using ssh to run fossil on a remote machine name zHostname. |
| 277 | ** |
| 278 | ** If iTruth is 1 or 0 then that means that the PATH= is or is not required, |
| 279 | ** respectively. Record this fact for future reference. |
| 280 | ** |
| 281 | ** If iTruth is 99 or more, then toggle the truth value. |
| 282 | */ |
| 283 | int ssh_needs_path_argument(const char *zHostname, int iTruth){ |
| 284 | int ans = 0; /* Default to "no" */ |
| 285 | char *z = mprintf("use-path-for-ssh:%s", zHostname); |
| 286 | if( iTruth<0 ){ |
| 287 | if( db_get_boolean(z/*works-like:"x"*/, 0) ) ans = 1; |
| 288 | }else{ |
| 289 | if( iTruth>=99 ){ |
| 290 | iTruth = !db_get_boolean(z/*works-like:"x"*/, 0); |
| 291 | } |
| 292 | if( iTruth ){ |
| 293 | ans = 1; |
| 294 | db_set(z/*works-like:"x"*/, "1", 0); |
| 295 | }else{ |
| 296 | db_unset(z/*works-like:"x"*/, 0); |
| 297 | } |
| 298 | } |
| 299 | fossil_free(z); |
| 300 | return ans; |
| 301 | } |
| 302 | |
| 303 | /* |
| 304 | ** COMMAND: test-ssh-needs-path |
| 305 | ** |
| 306 | ** Usage: fossil test-ssh-needs-path HOSTNAME ?BOOLEAN? |
| 307 | ** |
| 308 | ** With one argument, show whether or not the PATH= argument is included |
| 309 | ** by default for HOSTNAME. If the second argument is a boolean, then |
| 310 | ** change the value. |
| 311 | ** |
| 312 | ** With no arguments, show all hosts for which ssh-needs-path is true. |
| 313 | */ |
| 314 | void test_ssh_needs_path(void){ |
| 315 | db_find_and_open_repository(0,0); |
| 316 | if( g.argc>=3 ){ |
| 317 | const char *zHost = g.argv[2]; |
| 318 | int a = -1; |
| 319 | int rc; |
| 320 | if( g.argc>=4 ) a = is_truth(g.argv[3]); |
| 321 | rc = ssh_needs_path_argument(zHost, a); |
| 322 | fossil_print("%-20s %s\n", zHost, rc ? "yes" : "no"); |
| 323 | }else{ |
| 324 | Stmt s; |
| 325 | db_prepare(&s, "SELECT substr(name,18) FROM config" |
| 326 | " WHERE name GLOB 'use-path-for-ssh:*'"); |
| 327 | while( db_step(&s)==SQLITE_ROW ){ |
| 328 | const char *zHost = db_column_text(&s,0); |
| 329 | fossil_print("%-20s yes\n", zHost); |
| 330 | } |
| 331 | db_finalize(&s); |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | /* Add an approprate PATH= argument to the SSH command under construction |
| 336 | ** in pCmd. |
| 337 | */ |
| 338 | void ssh_add_path_argument(Blob *pCmd){ |
| 339 |
+70
-16
| --- src/patch.c | ||
| +++ src/patch.c | ||
| @@ -45,10 +45,11 @@ | ||
| 45 | 45 | ** to implement the various subcommands. |
| 46 | 46 | */ |
| 47 | 47 | #define PATCH_DRYRUN 0x0001 |
| 48 | 48 | #define PATCH_VERBOSE 0x0002 |
| 49 | 49 | #define PATCH_FORCE 0x0004 |
| 50 | +#define PATCH_RETRY 0x0008 /* Second attempt */ | |
| 50 | 51 | |
| 51 | 52 | /* |
| 52 | 53 | ** Implementation of the "readfile(X)" SQL function. The entire content |
| 53 | 54 | ** of the check-out file named X is read and returned as a BLOB. |
| 54 | 55 | */ |
| @@ -132,11 +133,13 @@ | ||
| 132 | 133 | } |
| 133 | 134 | |
| 134 | 135 | |
| 135 | 136 | /* |
| 136 | 137 | ** Generate a binary patch file and store it into the file |
| 137 | -** named zOut. | |
| 138 | +** named zOut. Or if zOut is NULL, write it into out. | |
| 139 | +** | |
| 140 | +** Return the number of errors. | |
| 138 | 141 | */ |
| 139 | 142 | void patch_create(unsigned mFlags, const char *zOut, FILE *out){ |
| 140 | 143 | int vid; |
| 141 | 144 | char *z; |
| 142 | 145 | |
| @@ -248,21 +251,22 @@ | ||
| 248 | 251 | } |
| 249 | 252 | #ifdef _WIN32 |
| 250 | 253 | fflush(out); |
| 251 | 254 | _setmode(_fileno(out), _O_BINARY); |
| 252 | 255 | #endif |
| 253 | - fwrite(pData, sz, 1, out); | |
| 254 | - sqlite3_free(pData); | |
| 256 | + fwrite(pData, 1, sz, out); | |
| 255 | 257 | fflush(out); |
| 258 | + sqlite3_free(pData); | |
| 256 | 259 | } |
| 260 | + db_multi_exec("DETACH patch;"); | |
| 257 | 261 | } |
| 258 | 262 | |
| 259 | 263 | /* |
| 260 | 264 | ** Attempt to load and validate a patchfile identified by the first |
| 261 | 265 | ** argument. |
| 262 | 266 | */ |
| 263 | -void patch_attach(const char *zIn, FILE *in){ | |
| 267 | +void patch_attach(const char *zIn, FILE *in, int bIgnoreEmptyPatch){ | |
| 264 | 268 | Stmt q; |
| 265 | 269 | if( g.db==0 ){ |
| 266 | 270 | sqlite3_open(":memory:", &g.db); |
| 267 | 271 | } |
| 268 | 272 | if( zIn==0 ){ |
| @@ -274,10 +278,15 @@ | ||
| 274 | 278 | #ifdef _WIN32 |
| 275 | 279 | _setmode(_fileno(in), _O_BINARY); |
| 276 | 280 | #endif |
| 277 | 281 | sz = blob_read_from_channel(&buf, in, -1); |
| 278 | 282 | pData = (unsigned char*)blob_buffer(&buf); |
| 283 | + if( sz<512 ){ | |
| 284 | + blob_reset(&buf); | |
| 285 | + if( bIgnoreEmptyPatch ) return; | |
| 286 | + fossil_fatal("input is too small to be a patch file"); | |
| 287 | + } | |
| 279 | 288 | db_multi_exec("ATTACH ':memory:' AS patch"); |
| 280 | 289 | if( g.fSqlTrace ){ |
| 281 | 290 | fossil_trace("-- deserialize(\"patch\", pData, %lld);\n", sz); |
| 282 | 291 | } |
| 283 | 292 | rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0); |
| @@ -664,18 +673,20 @@ | ||
| 664 | 673 | const char *zThisCmd, /* "push" or "pull" */ |
| 665 | 674 | const char *zRemoteCmd, /* "apply" or "create" */ |
| 666 | 675 | const char *zFossilCmd, /* Name of "fossil" on remote system */ |
| 667 | 676 | const char *zRW /* "w" or "r" */ |
| 668 | 677 | ){ |
| 669 | - char *zRemote; | |
| 670 | - char *zDir; | |
| 678 | + char *zRemote = 0; | |
| 679 | + char *zDir = 0; | |
| 671 | 680 | Blob cmd; |
| 672 | - FILE *f; | |
| 681 | + FILE *f = 0; | |
| 673 | 682 | Blob flgs; |
| 674 | - char *zForce; | |
| 683 | + char *zForce = 0; | |
| 684 | + int isRetry = (mFlags & PATCH_RETRY)!=0; | |
| 675 | 685 | |
| 676 | 686 | blob_init(&flgs, 0, 0); |
| 687 | + blob_init(&cmd, 0, 0); | |
| 677 | 688 | if( mFlags & PATCH_FORCE ) blob_appendf(&flgs, " -f"); |
| 678 | 689 | if( mFlags & PATCH_VERBOSE ) blob_appendf(&flgs, " -v"); |
| 679 | 690 | if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n"); |
| 680 | 691 | zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : ""; |
| 681 | 692 | if( g.argc!=4 ){ |
| @@ -682,12 +693,12 @@ | ||
| 682 | 693 | usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd)); |
| 683 | 694 | } |
| 684 | 695 | zRemote = fossil_strdup(g.argv[3]); |
| 685 | 696 | zDir = (char*)file_skip_userhost(zRemote); |
| 686 | 697 | if( zDir==0 ){ |
| 698 | + if( isRetry ) goto remote_command_error; | |
| 687 | 699 | zDir = zRemote; |
| 688 | - blob_init(&cmd, 0, 0); | |
| 689 | 700 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 690 | 701 | blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir); |
| 691 | 702 | }else{ |
| 692 | 703 | Blob remote; |
| 693 | 704 | *(char*)(zDir-1) = 0; |
| @@ -694,28 +705,52 @@ | ||
| 694 | 705 | transport_ssh_command(&cmd); |
| 695 | 706 | blob_appendf(&cmd, " -T"); |
| 696 | 707 | blob_append_escaped_arg(&cmd, zRemote, 0); |
| 697 | 708 | blob_init(&remote, 0, 0); |
| 698 | 709 | if( zFossilCmd==0 ){ |
| 699 | - ssh_add_path_argument(&cmd); | |
| 710 | + if( ssh_needs_path_argument(zRemote,-1) ^ isRetry ){ | |
| 711 | + ssh_add_path_argument(&cmd); | |
| 712 | + } | |
| 700 | 713 | zFossilCmd = "fossil"; |
| 714 | + }else if( mFlags & PATCH_RETRY ){ | |
| 715 | + goto remote_command_error; | |
| 701 | 716 | } |
| 702 | 717 | blob_appendf(&remote, "%$ patch %s%s --dir64 %z -", |
| 703 | 718 | zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1)); |
| 704 | 719 | blob_append_escaped_arg(&cmd, blob_str(&remote), 0); |
| 705 | 720 | blob_reset(&remote); |
| 706 | 721 | } |
| 722 | + if( isRetry ){ | |
| 723 | + fossil_print("First attempt to run \"fossil\" on %s failed\n" | |
| 724 | + "Retry: ", zRemote); | |
| 725 | + } | |
| 707 | 726 | fossil_print("%s\n", blob_str(&cmd)); |
| 708 | 727 | fflush(stdout); |
| 709 | 728 | f = popen(blob_str(&cmd), zRW); |
| 710 | 729 | if( f==0 ){ |
| 711 | 730 | fossil_fatal("cannot run command: %s", blob_str(&cmd)); |
| 712 | 731 | } |
| 732 | +remote_command_error: | |
| 733 | + fossil_free(zRemote); | |
| 713 | 734 | blob_reset(&cmd); |
| 714 | 735 | blob_reset(&flgs); |
| 715 | 736 | return f; |
| 716 | 737 | } |
| 738 | + | |
| 739 | +/* | |
| 740 | +** Toggle the use-path-for-ssh setting for the remote host defined | |
| 741 | +** by g.argv[3]. | |
| 742 | +*/ | |
| 743 | +static void patch_toggle_ssh_needs_path(void){ | |
| 744 | + char *zRemote = fossil_strdup(g.argv[3]); | |
| 745 | + char *zDir = (char*)file_skip_userhost(zRemote); | |
| 746 | + if( zDir ){ | |
| 747 | + *(char*)(zDir - 1) = 0; | |
| 748 | + ssh_needs_path_argument(zRemote, 99); | |
| 749 | + } | |
| 750 | + fossil_free(zRemote); | |
| 751 | +} | |
| 717 | 752 | |
| 718 | 753 | /* |
| 719 | 754 | ** Show a diff for the patch currently loaded into database "patch". |
| 720 | 755 | */ |
| 721 | 756 | static void patch_diff( |
| @@ -934,11 +969,11 @@ | ||
| 934 | 969 | if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; |
| 935 | 970 | if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; |
| 936 | 971 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 937 | 972 | zIn = patch_find_patch_filename("apply"); |
| 938 | 973 | db_must_be_within_tree(); |
| 939 | - patch_attach(zIn, stdin); | |
| 974 | + patch_attach(zIn, stdin, 0); | |
| 940 | 975 | patch_apply(flags); |
| 941 | 976 | fossil_free(zIn); |
| 942 | 977 | }else |
| 943 | 978 | if( strncmp(zCmd, "create", n)==0 ){ |
| 944 | 979 | char *zOut; |
| @@ -963,11 +998,11 @@ | ||
| 963 | 998 | db_find_and_open_repository(0, 0); |
| 964 | 999 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 965 | 1000 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 966 | 1001 | verify_all_options(); |
| 967 | 1002 | zIn = patch_find_patch_filename("apply"); |
| 968 | - patch_attach(zIn, stdin); | |
| 1003 | + patch_attach(zIn, stdin, 0); | |
| 969 | 1004 | patch_diff(flags, &DCfg); |
| 970 | 1005 | fossil_free(zIn); |
| 971 | 1006 | }else |
| 972 | 1007 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 973 | 1008 | FILE *pIn = 0; |
| @@ -979,12 +1014,22 @@ | ||
| 979 | 1014 | db_must_be_within_tree(); |
| 980 | 1015 | verify_all_options(); |
| 981 | 1016 | pIn = patch_remote_command(flags & (~PATCH_FORCE), |
| 982 | 1017 | "pull", "create", zFossilCmd, "r"); |
| 983 | 1018 | if( pIn ){ |
| 984 | - patch_attach(0, pIn); | |
| 985 | - pclose(pIn); | |
| 1019 | + patch_attach(0, pIn, 1); | |
| 1020 | + if( pclose(pIn) ){ | |
| 1021 | + flags |= PATCH_RETRY; | |
| 1022 | + pIn = patch_remote_command(flags & (~PATCH_FORCE), | |
| 1023 | + "pull", "create", zFossilCmd, "r"); | |
| 1024 | + if( pIn ){ | |
| 1025 | + patch_attach(0, pIn, 0); | |
| 1026 | + if( pclose(pIn)==0 ){ | |
| 1027 | + patch_toggle_ssh_needs_path(); | |
| 1028 | + } | |
| 1029 | + } | |
| 1030 | + } | |
| 986 | 1031 | patch_apply(flags); |
| 987 | 1032 | } |
| 988 | 1033 | }else |
| 989 | 1034 | if( strncmp(zCmd, "push", n)==0 ){ |
| 990 | 1035 | FILE *pOut = 0; |
| @@ -996,11 +1041,20 @@ | ||
| 996 | 1041 | db_must_be_within_tree(); |
| 997 | 1042 | verify_all_options(); |
| 998 | 1043 | pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w"); |
| 999 | 1044 | if( pOut ){ |
| 1000 | 1045 | patch_create(0, 0, pOut); |
| 1001 | - pclose(pOut); | |
| 1046 | + if( pclose(pOut)!=0 ){ | |
| 1047 | + flags |= PATCH_RETRY; | |
| 1048 | + pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w"); | |
| 1049 | + if( pOut ){ | |
| 1050 | + patch_create(0, 0, pOut); | |
| 1051 | + if( pclose(pOut)==0 ){ | |
| 1052 | + patch_toggle_ssh_needs_path(); | |
| 1053 | + } | |
| 1054 | + } | |
| 1055 | + } | |
| 1002 | 1056 | } |
| 1003 | 1057 | }else |
| 1004 | 1058 | if( strncmp(zCmd, "view", n)==0 ){ |
| 1005 | 1059 | const char *zIn; |
| 1006 | 1060 | unsigned int flags = 0; |
| @@ -1009,12 +1063,12 @@ | ||
| 1009 | 1063 | if( g.argc!=4 ){ |
| 1010 | 1064 | usage("view FILENAME"); |
| 1011 | 1065 | } |
| 1012 | 1066 | zIn = g.argv[3]; |
| 1013 | 1067 | if( fossil_strcmp(zIn, "-")==0 ) zIn = 0; |
| 1014 | - patch_attach(zIn, stdin); | |
| 1068 | + patch_attach(zIn, stdin, 0); | |
| 1015 | 1069 | patch_view(flags); |
| 1016 | 1070 | }else |
| 1017 | 1071 | { |
| 1018 | 1072 | goto patch_usage; |
| 1019 | 1073 | } |
| 1020 | 1074 | } |
| 1021 | 1075 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -45,10 +45,11 @@ | |
| 45 | ** to implement the various subcommands. |
| 46 | */ |
| 47 | #define PATCH_DRYRUN 0x0001 |
| 48 | #define PATCH_VERBOSE 0x0002 |
| 49 | #define PATCH_FORCE 0x0004 |
| 50 | |
| 51 | /* |
| 52 | ** Implementation of the "readfile(X)" SQL function. The entire content |
| 53 | ** of the check-out file named X is read and returned as a BLOB. |
| 54 | */ |
| @@ -132,11 +133,13 @@ | |
| 132 | } |
| 133 | |
| 134 | |
| 135 | /* |
| 136 | ** Generate a binary patch file and store it into the file |
| 137 | ** named zOut. |
| 138 | */ |
| 139 | void patch_create(unsigned mFlags, const char *zOut, FILE *out){ |
| 140 | int vid; |
| 141 | char *z; |
| 142 | |
| @@ -248,21 +251,22 @@ | |
| 248 | } |
| 249 | #ifdef _WIN32 |
| 250 | fflush(out); |
| 251 | _setmode(_fileno(out), _O_BINARY); |
| 252 | #endif |
| 253 | fwrite(pData, sz, 1, out); |
| 254 | sqlite3_free(pData); |
| 255 | fflush(out); |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** Attempt to load and validate a patchfile identified by the first |
| 261 | ** argument. |
| 262 | */ |
| 263 | void patch_attach(const char *zIn, FILE *in){ |
| 264 | Stmt q; |
| 265 | if( g.db==0 ){ |
| 266 | sqlite3_open(":memory:", &g.db); |
| 267 | } |
| 268 | if( zIn==0 ){ |
| @@ -274,10 +278,15 @@ | |
| 274 | #ifdef _WIN32 |
| 275 | _setmode(_fileno(in), _O_BINARY); |
| 276 | #endif |
| 277 | sz = blob_read_from_channel(&buf, in, -1); |
| 278 | pData = (unsigned char*)blob_buffer(&buf); |
| 279 | db_multi_exec("ATTACH ':memory:' AS patch"); |
| 280 | if( g.fSqlTrace ){ |
| 281 | fossil_trace("-- deserialize(\"patch\", pData, %lld);\n", sz); |
| 282 | } |
| 283 | rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0); |
| @@ -664,18 +673,20 @@ | |
| 664 | const char *zThisCmd, /* "push" or "pull" */ |
| 665 | const char *zRemoteCmd, /* "apply" or "create" */ |
| 666 | const char *zFossilCmd, /* Name of "fossil" on remote system */ |
| 667 | const char *zRW /* "w" or "r" */ |
| 668 | ){ |
| 669 | char *zRemote; |
| 670 | char *zDir; |
| 671 | Blob cmd; |
| 672 | FILE *f; |
| 673 | Blob flgs; |
| 674 | char *zForce; |
| 675 | |
| 676 | blob_init(&flgs, 0, 0); |
| 677 | if( mFlags & PATCH_FORCE ) blob_appendf(&flgs, " -f"); |
| 678 | if( mFlags & PATCH_VERBOSE ) blob_appendf(&flgs, " -v"); |
| 679 | if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n"); |
| 680 | zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : ""; |
| 681 | if( g.argc!=4 ){ |
| @@ -682,12 +693,12 @@ | |
| 682 | usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd)); |
| 683 | } |
| 684 | zRemote = fossil_strdup(g.argv[3]); |
| 685 | zDir = (char*)file_skip_userhost(zRemote); |
| 686 | if( zDir==0 ){ |
| 687 | zDir = zRemote; |
| 688 | blob_init(&cmd, 0, 0); |
| 689 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 690 | blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir); |
| 691 | }else{ |
| 692 | Blob remote; |
| 693 | *(char*)(zDir-1) = 0; |
| @@ -694,28 +705,52 @@ | |
| 694 | transport_ssh_command(&cmd); |
| 695 | blob_appendf(&cmd, " -T"); |
| 696 | blob_append_escaped_arg(&cmd, zRemote, 0); |
| 697 | blob_init(&remote, 0, 0); |
| 698 | if( zFossilCmd==0 ){ |
| 699 | ssh_add_path_argument(&cmd); |
| 700 | zFossilCmd = "fossil"; |
| 701 | } |
| 702 | blob_appendf(&remote, "%$ patch %s%s --dir64 %z -", |
| 703 | zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1)); |
| 704 | blob_append_escaped_arg(&cmd, blob_str(&remote), 0); |
| 705 | blob_reset(&remote); |
| 706 | } |
| 707 | fossil_print("%s\n", blob_str(&cmd)); |
| 708 | fflush(stdout); |
| 709 | f = popen(blob_str(&cmd), zRW); |
| 710 | if( f==0 ){ |
| 711 | fossil_fatal("cannot run command: %s", blob_str(&cmd)); |
| 712 | } |
| 713 | blob_reset(&cmd); |
| 714 | blob_reset(&flgs); |
| 715 | return f; |
| 716 | } |
| 717 | |
| 718 | /* |
| 719 | ** Show a diff for the patch currently loaded into database "patch". |
| 720 | */ |
| 721 | static void patch_diff( |
| @@ -934,11 +969,11 @@ | |
| 934 | if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; |
| 935 | if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; |
| 936 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 937 | zIn = patch_find_patch_filename("apply"); |
| 938 | db_must_be_within_tree(); |
| 939 | patch_attach(zIn, stdin); |
| 940 | patch_apply(flags); |
| 941 | fossil_free(zIn); |
| 942 | }else |
| 943 | if( strncmp(zCmd, "create", n)==0 ){ |
| 944 | char *zOut; |
| @@ -963,11 +998,11 @@ | |
| 963 | db_find_and_open_repository(0, 0); |
| 964 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 965 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 966 | verify_all_options(); |
| 967 | zIn = patch_find_patch_filename("apply"); |
| 968 | patch_attach(zIn, stdin); |
| 969 | patch_diff(flags, &DCfg); |
| 970 | fossil_free(zIn); |
| 971 | }else |
| 972 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 973 | FILE *pIn = 0; |
| @@ -979,12 +1014,22 @@ | |
| 979 | db_must_be_within_tree(); |
| 980 | verify_all_options(); |
| 981 | pIn = patch_remote_command(flags & (~PATCH_FORCE), |
| 982 | "pull", "create", zFossilCmd, "r"); |
| 983 | if( pIn ){ |
| 984 | patch_attach(0, pIn); |
| 985 | pclose(pIn); |
| 986 | patch_apply(flags); |
| 987 | } |
| 988 | }else |
| 989 | if( strncmp(zCmd, "push", n)==0 ){ |
| 990 | FILE *pOut = 0; |
| @@ -996,11 +1041,20 @@ | |
| 996 | db_must_be_within_tree(); |
| 997 | verify_all_options(); |
| 998 | pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w"); |
| 999 | if( pOut ){ |
| 1000 | patch_create(0, 0, pOut); |
| 1001 | pclose(pOut); |
| 1002 | } |
| 1003 | }else |
| 1004 | if( strncmp(zCmd, "view", n)==0 ){ |
| 1005 | const char *zIn; |
| 1006 | unsigned int flags = 0; |
| @@ -1009,12 +1063,12 @@ | |
| 1009 | if( g.argc!=4 ){ |
| 1010 | usage("view FILENAME"); |
| 1011 | } |
| 1012 | zIn = g.argv[3]; |
| 1013 | if( fossil_strcmp(zIn, "-")==0 ) zIn = 0; |
| 1014 | patch_attach(zIn, stdin); |
| 1015 | patch_view(flags); |
| 1016 | }else |
| 1017 | { |
| 1018 | goto patch_usage; |
| 1019 | } |
| 1020 | } |
| 1021 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -45,10 +45,11 @@ | |
| 45 | ** to implement the various subcommands. |
| 46 | */ |
| 47 | #define PATCH_DRYRUN 0x0001 |
| 48 | #define PATCH_VERBOSE 0x0002 |
| 49 | #define PATCH_FORCE 0x0004 |
| 50 | #define PATCH_RETRY 0x0008 /* Second attempt */ |
| 51 | |
| 52 | /* |
| 53 | ** Implementation of the "readfile(X)" SQL function. The entire content |
| 54 | ** of the check-out file named X is read and returned as a BLOB. |
| 55 | */ |
| @@ -132,11 +133,13 @@ | |
| 133 | } |
| 134 | |
| 135 | |
| 136 | /* |
| 137 | ** Generate a binary patch file and store it into the file |
| 138 | ** named zOut. Or if zOut is NULL, write it into out. |
| 139 | ** |
| 140 | ** Return the number of errors. |
| 141 | */ |
| 142 | void patch_create(unsigned mFlags, const char *zOut, FILE *out){ |
| 143 | int vid; |
| 144 | char *z; |
| 145 | |
| @@ -248,21 +251,22 @@ | |
| 251 | } |
| 252 | #ifdef _WIN32 |
| 253 | fflush(out); |
| 254 | _setmode(_fileno(out), _O_BINARY); |
| 255 | #endif |
| 256 | fwrite(pData, 1, sz, out); |
| 257 | fflush(out); |
| 258 | sqlite3_free(pData); |
| 259 | } |
| 260 | db_multi_exec("DETACH patch;"); |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | ** Attempt to load and validate a patchfile identified by the first |
| 265 | ** argument. |
| 266 | */ |
| 267 | void patch_attach(const char *zIn, FILE *in, int bIgnoreEmptyPatch){ |
| 268 | Stmt q; |
| 269 | if( g.db==0 ){ |
| 270 | sqlite3_open(":memory:", &g.db); |
| 271 | } |
| 272 | if( zIn==0 ){ |
| @@ -274,10 +278,15 @@ | |
| 278 | #ifdef _WIN32 |
| 279 | _setmode(_fileno(in), _O_BINARY); |
| 280 | #endif |
| 281 | sz = blob_read_from_channel(&buf, in, -1); |
| 282 | pData = (unsigned char*)blob_buffer(&buf); |
| 283 | if( sz<512 ){ |
| 284 | blob_reset(&buf); |
| 285 | if( bIgnoreEmptyPatch ) return; |
| 286 | fossil_fatal("input is too small to be a patch file"); |
| 287 | } |
| 288 | db_multi_exec("ATTACH ':memory:' AS patch"); |
| 289 | if( g.fSqlTrace ){ |
| 290 | fossil_trace("-- deserialize(\"patch\", pData, %lld);\n", sz); |
| 291 | } |
| 292 | rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0); |
| @@ -664,18 +673,20 @@ | |
| 673 | const char *zThisCmd, /* "push" or "pull" */ |
| 674 | const char *zRemoteCmd, /* "apply" or "create" */ |
| 675 | const char *zFossilCmd, /* Name of "fossil" on remote system */ |
| 676 | const char *zRW /* "w" or "r" */ |
| 677 | ){ |
| 678 | char *zRemote = 0; |
| 679 | char *zDir = 0; |
| 680 | Blob cmd; |
| 681 | FILE *f = 0; |
| 682 | Blob flgs; |
| 683 | char *zForce = 0; |
| 684 | int isRetry = (mFlags & PATCH_RETRY)!=0; |
| 685 | |
| 686 | blob_init(&flgs, 0, 0); |
| 687 | blob_init(&cmd, 0, 0); |
| 688 | if( mFlags & PATCH_FORCE ) blob_appendf(&flgs, " -f"); |
| 689 | if( mFlags & PATCH_VERBOSE ) blob_appendf(&flgs, " -v"); |
| 690 | if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n"); |
| 691 | zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : ""; |
| 692 | if( g.argc!=4 ){ |
| @@ -682,12 +693,12 @@ | |
| 693 | usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd)); |
| 694 | } |
| 695 | zRemote = fossil_strdup(g.argv[3]); |
| 696 | zDir = (char*)file_skip_userhost(zRemote); |
| 697 | if( zDir==0 ){ |
| 698 | if( isRetry ) goto remote_command_error; |
| 699 | zDir = zRemote; |
| 700 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 701 | blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir); |
| 702 | }else{ |
| 703 | Blob remote; |
| 704 | *(char*)(zDir-1) = 0; |
| @@ -694,28 +705,52 @@ | |
| 705 | transport_ssh_command(&cmd); |
| 706 | blob_appendf(&cmd, " -T"); |
| 707 | blob_append_escaped_arg(&cmd, zRemote, 0); |
| 708 | blob_init(&remote, 0, 0); |
| 709 | if( zFossilCmd==0 ){ |
| 710 | if( ssh_needs_path_argument(zRemote,-1) ^ isRetry ){ |
| 711 | ssh_add_path_argument(&cmd); |
| 712 | } |
| 713 | zFossilCmd = "fossil"; |
| 714 | }else if( mFlags & PATCH_RETRY ){ |
| 715 | goto remote_command_error; |
| 716 | } |
| 717 | blob_appendf(&remote, "%$ patch %s%s --dir64 %z -", |
| 718 | zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1)); |
| 719 | blob_append_escaped_arg(&cmd, blob_str(&remote), 0); |
| 720 | blob_reset(&remote); |
| 721 | } |
| 722 | if( isRetry ){ |
| 723 | fossil_print("First attempt to run \"fossil\" on %s failed\n" |
| 724 | "Retry: ", zRemote); |
| 725 | } |
| 726 | fossil_print("%s\n", blob_str(&cmd)); |
| 727 | fflush(stdout); |
| 728 | f = popen(blob_str(&cmd), zRW); |
| 729 | if( f==0 ){ |
| 730 | fossil_fatal("cannot run command: %s", blob_str(&cmd)); |
| 731 | } |
| 732 | remote_command_error: |
| 733 | fossil_free(zRemote); |
| 734 | blob_reset(&cmd); |
| 735 | blob_reset(&flgs); |
| 736 | return f; |
| 737 | } |
| 738 | |
| 739 | /* |
| 740 | ** Toggle the use-path-for-ssh setting for the remote host defined |
| 741 | ** by g.argv[3]. |
| 742 | */ |
| 743 | static void patch_toggle_ssh_needs_path(void){ |
| 744 | char *zRemote = fossil_strdup(g.argv[3]); |
| 745 | char *zDir = (char*)file_skip_userhost(zRemote); |
| 746 | if( zDir ){ |
| 747 | *(char*)(zDir - 1) = 0; |
| 748 | ssh_needs_path_argument(zRemote, 99); |
| 749 | } |
| 750 | fossil_free(zRemote); |
| 751 | } |
| 752 | |
| 753 | /* |
| 754 | ** Show a diff for the patch currently loaded into database "patch". |
| 755 | */ |
| 756 | static void patch_diff( |
| @@ -934,11 +969,11 @@ | |
| 969 | if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; |
| 970 | if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; |
| 971 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 972 | zIn = patch_find_patch_filename("apply"); |
| 973 | db_must_be_within_tree(); |
| 974 | patch_attach(zIn, stdin, 0); |
| 975 | patch_apply(flags); |
| 976 | fossil_free(zIn); |
| 977 | }else |
| 978 | if( strncmp(zCmd, "create", n)==0 ){ |
| 979 | char *zOut; |
| @@ -963,11 +998,11 @@ | |
| 998 | db_find_and_open_repository(0, 0); |
| 999 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 1000 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 1001 | verify_all_options(); |
| 1002 | zIn = patch_find_patch_filename("apply"); |
| 1003 | patch_attach(zIn, stdin, 0); |
| 1004 | patch_diff(flags, &DCfg); |
| 1005 | fossil_free(zIn); |
| 1006 | }else |
| 1007 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 1008 | FILE *pIn = 0; |
| @@ -979,12 +1014,22 @@ | |
| 1014 | db_must_be_within_tree(); |
| 1015 | verify_all_options(); |
| 1016 | pIn = patch_remote_command(flags & (~PATCH_FORCE), |
| 1017 | "pull", "create", zFossilCmd, "r"); |
| 1018 | if( pIn ){ |
| 1019 | patch_attach(0, pIn, 1); |
| 1020 | if( pclose(pIn) ){ |
| 1021 | flags |= PATCH_RETRY; |
| 1022 | pIn = patch_remote_command(flags & (~PATCH_FORCE), |
| 1023 | "pull", "create", zFossilCmd, "r"); |
| 1024 | if( pIn ){ |
| 1025 | patch_attach(0, pIn, 0); |
| 1026 | if( pclose(pIn)==0 ){ |
| 1027 | patch_toggle_ssh_needs_path(); |
| 1028 | } |
| 1029 | } |
| 1030 | } |
| 1031 | patch_apply(flags); |
| 1032 | } |
| 1033 | }else |
| 1034 | if( strncmp(zCmd, "push", n)==0 ){ |
| 1035 | FILE *pOut = 0; |
| @@ -996,11 +1041,20 @@ | |
| 1041 | db_must_be_within_tree(); |
| 1042 | verify_all_options(); |
| 1043 | pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w"); |
| 1044 | if( pOut ){ |
| 1045 | patch_create(0, 0, pOut); |
| 1046 | if( pclose(pOut)!=0 ){ |
| 1047 | flags |= PATCH_RETRY; |
| 1048 | pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w"); |
| 1049 | if( pOut ){ |
| 1050 | patch_create(0, 0, pOut); |
| 1051 | if( pclose(pOut)==0 ){ |
| 1052 | patch_toggle_ssh_needs_path(); |
| 1053 | } |
| 1054 | } |
| 1055 | } |
| 1056 | } |
| 1057 | }else |
| 1058 | if( strncmp(zCmd, "view", n)==0 ){ |
| 1059 | const char *zIn; |
| 1060 | unsigned int flags = 0; |
| @@ -1009,12 +1063,12 @@ | |
| 1063 | if( g.argc!=4 ){ |
| 1064 | usage("view FILENAME"); |
| 1065 | } |
| 1066 | zIn = g.argv[3]; |
| 1067 | if( fossil_strcmp(zIn, "-")==0 ) zIn = 0; |
| 1068 | patch_attach(zIn, stdin, 0); |
| 1069 | patch_view(flags); |
| 1070 | }else |
| 1071 | { |
| 1072 | goto patch_usage; |
| 1073 | } |
| 1074 | } |
| 1075 |