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.

drh 2024-02-06 23:18 trunk
Commit eb135ef204f467963fe293ffa14f88aae7556c38e7a6931849aee0eac39db108
2 files changed +43 -4 +70 -16
+43 -4
--- src/http.c
+++ src/http.c
@@ -275,25 +275,64 @@
275275
/* If iTruth<0 then guess as to whether or not a PATH= argument is required
276276
** when using ssh to run fossil on a remote machine name zHostname.
277277
**
278278
** If iTruth is 1 or 0 then that means that the PATH= is or is not required,
279279
** respectively. Record this fact for future reference.
280
+**
281
+** If iTruth is 99 or more, then toggle the truth value.
280282
*/
281283
int ssh_needs_path_argument(const char *zHostname, int iTruth){
282284
int ans = 0; /* Default to "no" */
283285
char *z = mprintf("use-path-for-ssh:%s", zHostname);
284286
if( iTruth<0 ){
285287
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);
289288
}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
+ }
291298
}
292299
fossil_free(z);
293300
return ans;
294301
}
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
+}
295334
296335
/* Add an approprate PATH= argument to the SSH command under construction
297336
** in pCmd.
298337
*/
299338
void ssh_add_path_argument(Blob *pCmd){
300339
--- 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 @@
4545
** to implement the various subcommands.
4646
*/
4747
#define PATCH_DRYRUN 0x0001
4848
#define PATCH_VERBOSE 0x0002
4949
#define PATCH_FORCE 0x0004
50
+#define PATCH_RETRY 0x0008 /* Second attempt */
5051
5152
/*
5253
** Implementation of the "readfile(X)" SQL function. The entire content
5354
** of the check-out file named X is read and returned as a BLOB.
5455
*/
@@ -132,11 +133,13 @@
132133
}
133134
134135
135136
/*
136137
** 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.
138141
*/
139142
void patch_create(unsigned mFlags, const char *zOut, FILE *out){
140143
int vid;
141144
char *z;
142145
@@ -248,21 +251,22 @@
248251
}
249252
#ifdef _WIN32
250253
fflush(out);
251254
_setmode(_fileno(out), _O_BINARY);
252255
#endif
253
- fwrite(pData, sz, 1, out);
254
- sqlite3_free(pData);
256
+ fwrite(pData, 1, sz, out);
255257
fflush(out);
258
+ sqlite3_free(pData);
256259
}
260
+ db_multi_exec("DETACH patch;");
257261
}
258262
259263
/*
260264
** Attempt to load and validate a patchfile identified by the first
261265
** argument.
262266
*/
263
-void patch_attach(const char *zIn, FILE *in){
267
+void patch_attach(const char *zIn, FILE *in, int bIgnoreEmptyPatch){
264268
Stmt q;
265269
if( g.db==0 ){
266270
sqlite3_open(":memory:", &g.db);
267271
}
268272
if( zIn==0 ){
@@ -274,10 +278,15 @@
274278
#ifdef _WIN32
275279
_setmode(_fileno(in), _O_BINARY);
276280
#endif
277281
sz = blob_read_from_channel(&buf, in, -1);
278282
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
+ }
279288
db_multi_exec("ATTACH ':memory:' AS patch");
280289
if( g.fSqlTrace ){
281290
fossil_trace("-- deserialize(\"patch\", pData, %lld);\n", sz);
282291
}
283292
rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0);
@@ -664,18 +673,20 @@
664673
const char *zThisCmd, /* "push" or "pull" */
665674
const char *zRemoteCmd, /* "apply" or "create" */
666675
const char *zFossilCmd, /* Name of "fossil" on remote system */
667676
const char *zRW /* "w" or "r" */
668677
){
669
- char *zRemote;
670
- char *zDir;
678
+ char *zRemote = 0;
679
+ char *zDir = 0;
671680
Blob cmd;
672
- FILE *f;
681
+ FILE *f = 0;
673682
Blob flgs;
674
- char *zForce;
683
+ char *zForce = 0;
684
+ int isRetry = (mFlags & PATCH_RETRY)!=0;
675685
676686
blob_init(&flgs, 0, 0);
687
+ blob_init(&cmd, 0, 0);
677688
if( mFlags & PATCH_FORCE ) blob_appendf(&flgs, " -f");
678689
if( mFlags & PATCH_VERBOSE ) blob_appendf(&flgs, " -v");
679690
if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n");
680691
zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : "";
681692
if( g.argc!=4 ){
@@ -682,12 +693,12 @@
682693
usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
683694
}
684695
zRemote = fossil_strdup(g.argv[3]);
685696
zDir = (char*)file_skip_userhost(zRemote);
686697
if( zDir==0 ){
698
+ if( isRetry ) goto remote_command_error;
687699
zDir = zRemote;
688
- blob_init(&cmd, 0, 0);
689700
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
690701
blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
691702
}else{
692703
Blob remote;
693704
*(char*)(zDir-1) = 0;
@@ -694,28 +705,52 @@
694705
transport_ssh_command(&cmd);
695706
blob_appendf(&cmd, " -T");
696707
blob_append_escaped_arg(&cmd, zRemote, 0);
697708
blob_init(&remote, 0, 0);
698709
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
+ }
700713
zFossilCmd = "fossil";
714
+ }else if( mFlags & PATCH_RETRY ){
715
+ goto remote_command_error;
701716
}
702717
blob_appendf(&remote, "%$ patch %s%s --dir64 %z -",
703718
zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1));
704719
blob_append_escaped_arg(&cmd, blob_str(&remote), 0);
705720
blob_reset(&remote);
706721
}
722
+ if( isRetry ){
723
+ fossil_print("First attempt to run \"fossil\" on %s failed\n"
724
+ "Retry: ", zRemote);
725
+ }
707726
fossil_print("%s\n", blob_str(&cmd));
708727
fflush(stdout);
709728
f = popen(blob_str(&cmd), zRW);
710729
if( f==0 ){
711730
fossil_fatal("cannot run command: %s", blob_str(&cmd));
712731
}
732
+remote_command_error:
733
+ fossil_free(zRemote);
713734
blob_reset(&cmd);
714735
blob_reset(&flgs);
715736
return f;
716737
}
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
+}
717752
718753
/*
719754
** Show a diff for the patch currently loaded into database "patch".
720755
*/
721756
static void patch_diff(
@@ -934,11 +969,11 @@
934969
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
935970
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
936971
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
937972
zIn = patch_find_patch_filename("apply");
938973
db_must_be_within_tree();
939
- patch_attach(zIn, stdin);
974
+ patch_attach(zIn, stdin, 0);
940975
patch_apply(flags);
941976
fossil_free(zIn);
942977
}else
943978
if( strncmp(zCmd, "create", n)==0 ){
944979
char *zOut;
@@ -963,11 +998,11 @@
963998
db_find_and_open_repository(0, 0);
964999
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
9651000
diff_options(&DCfg, zCmd[0]=='g', 0);
9661001
verify_all_options();
9671002
zIn = patch_find_patch_filename("apply");
968
- patch_attach(zIn, stdin);
1003
+ patch_attach(zIn, stdin, 0);
9691004
patch_diff(flags, &DCfg);
9701005
fossil_free(zIn);
9711006
}else
9721007
if( strncmp(zCmd, "pull", n)==0 ){
9731008
FILE *pIn = 0;
@@ -979,12 +1014,22 @@
9791014
db_must_be_within_tree();
9801015
verify_all_options();
9811016
pIn = patch_remote_command(flags & (~PATCH_FORCE),
9821017
"pull", "create", zFossilCmd, "r");
9831018
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
+ }
9861031
patch_apply(flags);
9871032
}
9881033
}else
9891034
if( strncmp(zCmd, "push", n)==0 ){
9901035
FILE *pOut = 0;
@@ -996,11 +1041,20 @@
9961041
db_must_be_within_tree();
9971042
verify_all_options();
9981043
pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w");
9991044
if( pOut ){
10001045
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
+ }
10021056
}
10031057
}else
10041058
if( strncmp(zCmd, "view", n)==0 ){
10051059
const char *zIn;
10061060
unsigned int flags = 0;
@@ -1009,12 +1063,12 @@
10091063
if( g.argc!=4 ){
10101064
usage("view FILENAME");
10111065
}
10121066
zIn = g.argv[3];
10131067
if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
1014
- patch_attach(zIn, stdin);
1068
+ patch_attach(zIn, stdin, 0);
10151069
patch_view(flags);
10161070
}else
10171071
{
10181072
goto patch_usage;
10191073
}
10201074
}
10211075
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button