Fossil SCM

Pull in the remaining bits from trunk for integration testing.

andybradford 2024-02-07 14:25 clone-resume merge
Commit d27cb05f6ba055ce2c2d1d2dc5491941f02cd75a5148d05e8efa2c975ab9a880
+130 -17
--- src/http.c
+++ src/http.c
@@ -269,10 +269,127 @@
269269
blob_read_from_file(pReply, zDownlink, ExtFILE);
270270
file_delete(zDownlink);
271271
}
272272
return rc;
273273
}
274
+
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. Return
277
+** true if a PATH= should be provided and 0 if not.
278
+**
279
+** If iTruth is 1 or 0 then that means that the PATH= is or is not required,
280
+** respectively. Record this fact for future reference.
281
+**
282
+** If iTruth is 99 or more, then toggle the value that will be returned
283
+** for future iTruth==(-1) queries.
284
+*/
285
+int ssh_needs_path_argument(const char *zHostname, int iTruth){
286
+ int ans = 0; /* Default to "no" */
287
+ char *z = mprintf("use-path-for-ssh:%s", zHostname);
288
+ if( iTruth<0 ){
289
+ if( db_get_boolean(z/*works-like:"x"*/, 0) ) ans = 1;
290
+ }else{
291
+ if( iTruth>=99 ){
292
+ iTruth = !db_get_boolean(z/*works-like:"x"*/, 0);
293
+ }
294
+ if( iTruth ){
295
+ ans = 1;
296
+ db_set(z/*works-like:"x"*/, "1", 1);
297
+ }else{
298
+ db_unset(z/*works-like:"x"*/, 1);
299
+ }
300
+ }
301
+ fossil_free(z);
302
+ return ans;
303
+}
304
+
305
+/*
306
+** COMMAND: test-ssh-needs-path
307
+**
308
+** Usage: fossil test-ssh-needs-path ?HOSTNAME? ?BOOLEAN?
309
+**
310
+** With one argument, show whether or not the PATH= argument is included
311
+** by default for HOSTNAME. If the second argument is a boolean, then
312
+** change the value.
313
+**
314
+** With no arguments, show all hosts for which ssh-needs-path is true.
315
+*/
316
+void test_ssh_needs_path(void){
317
+ db_find_and_open_repository(0,0);
318
+ if( g.argc>=3 ){
319
+ const char *zHost = g.argv[2];
320
+ int a = -1;
321
+ int rc;
322
+ if( g.argc>=4 ) a = is_truth(g.argv[3]);
323
+ rc = ssh_needs_path_argument(zHost, a);
324
+ fossil_print("%-20s %s\n", zHost, rc ? "yes" : "no");
325
+ }else{
326
+ Stmt s;
327
+ db_prepare(&s, "SELECT substr(name,18) FROM config"
328
+ " WHERE name GLOB 'use-path-for-ssh:*'");
329
+ while( db_step(&s)==SQLITE_ROW ){
330
+ const char *zHost = db_column_text(&s,0);
331
+ fossil_print("%-20s yes\n", zHost);
332
+ }
333
+ db_finalize(&s);
334
+ }
335
+}
336
+
337
+/* Add an approprate PATH= argument to the SSH command under construction
338
+** in pCmd.
339
+**
340
+** About This Feature
341
+** ==================
342
+**
343
+** On some ssh servers (Macs in particular are guilty of this) the PATH
344
+** variable in the shell that runs the command that is sent to the remote
345
+** host contains a limited number of read-only system directories:
346
+**
347
+** /usr/bin:/bin:/usr/sbin:/sbin
348
+**
349
+** The fossil executable cannot be installed into any of those directories
350
+** because they are locked down, and so the "fossil" command cannot run.
351
+**
352
+** To work around this, the fossil command is prefixed with the PATH=
353
+** argument, inserted by this function, to augment the PATH with additional
354
+** directories in which the fossil executable is often found.
355
+**
356
+** But other ssh servers are confused by this initial PATH= argument.
357
+** Some ssh servers have a list of programs that they are allowed to run
358
+** and will fail if the first argument is not on that list, and PATH=....
359
+** is not on that list.
360
+**
361
+** So that various commands that use ssh can run seamlessly on a variety
362
+** of systems (commands that use ssh include "fossil sync" with an ssh:
363
+** URL and the "fossil patch pull" and "fossil patch push" commands where
364
+** the destination directory starts with HOSTNAME: or USER@HOSTNAME:.)
365
+** the following algorithm is used:
366
+**
367
+** * First try running the fossil without any PATH= argument. If that
368
+** works (and it does on a majority of systems) then we are done.
369
+**
370
+** * If the first attempt fails, then try again after adding the
371
+** PATH= prefix argument. (This function is what adds that
372
+** argument.) If the retry works, then remember that fact using
373
+** the use-path-for-ssh:HOSTNAME setting so that the first step
374
+** is skipped on subsequent uses of the same command.
375
+**
376
+** See the forum thread at
377
+** https://fossil-scm.org/forum/forumpost/4903cb4b691af7ce for more
378
+** background.
379
+**
380
+** See also:
381
+**
382
+** * The ssh_needs_path_argument() function above.
383
+** * The test-ssh-needs-path command that shows the settings
384
+** that cache whether or not a PATH= is needed for a particular
385
+** HOSTNAME.
386
+*/
387
+void ssh_add_path_argument(Blob *pCmd){
388
+ blob_append_escaped_arg(pCmd,
389
+ "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
390
+}
274391
275392
/*
276393
** Sign the content in pSend, compress it, and send it to the server
277394
** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply
278395
** in pRecv. pRecv is assumed to be uninitialized when
@@ -308,16 +425,15 @@
308425
}
309426
310427
/* Activate the PATH= auxiliary argument to the ssh command if that
311428
** is called for.
312429
*/
313
- if( g.url.isSsh && (g.url.flags & URL_SSH_RETRY)==0 ){
314
- char *z = mprintf("use-path-for-ssh:%s", g.url.hostname);
315
- if( db_get_boolean(z/*works-like:"x"*/, 0) ){
316
- g.url.flags |= URL_SSH_PATH;
317
- }
318
- fossil_free(z);
430
+ if( g.url.isSsh
431
+ && (g.url.flags & URL_SSH_RETRY)==0
432
+ && ssh_needs_path_argument(g.url.hostname, -1)
433
+ ){
434
+ g.url.flags |= URL_SSH_PATH;
319435
}
320436
321437
if( transport_open(&g.url) ){
322438
fossil_warning("%s", transport_errmsg(&g.url));
323439
return 1;
@@ -506,24 +622,21 @@
506622
&& (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
507623
&& (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
508624
){
509625
/* Retry after flipping the SSH_PATH setting */
510626
transport_close(&g.url);
511
- if( g.fSshTrace ){
512
- printf("Retry after %s the PATH= argument to the SSH command\n",
513
- (g.url.flags & URL_SSH_PATH)!=0 ? "removing" : "adding");
514
- }
627
+ fossil_print(
628
+ "First attempt to run fossil on %s using SSH failed.\n"
629
+ "Retrying %s the PATH= argument.\n",
630
+ g.url.hostname,
631
+ (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
632
+ );
515633
g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
516634
rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
517635
if( rc==0 ){
518
- char *z = mprintf("use-path-for-ssh:%s", g.url.hostname);
519
- if( (g.url.flags & URL_SSH_PATH) ){
520
- db_set(z/*works-like:"x"*/, "1", 0);
521
- }else{
522
- db_unset(z/*works-like:"x"*/, 0);
523
- }
524
- fossil_free(z);
636
+ (void)ssh_needs_path_argument(g.url.hostname,
637
+ (g.url.flags & URL_SSH_PATH)!=0);
525638
}
526639
return rc;
527640
}else{
528641
/* The problem could not be corrected by retrying. Report the
529642
** the error. */
530643
--- src/http.c
+++ src/http.c
@@ -269,10 +269,127 @@
269 blob_read_from_file(pReply, zDownlink, ExtFILE);
270 file_delete(zDownlink);
271 }
272 return rc;
273 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
275 /*
276 ** Sign the content in pSend, compress it, and send it to the server
277 ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply
278 ** in pRecv. pRecv is assumed to be uninitialized when
@@ -308,16 +425,15 @@
308 }
309
310 /* Activate the PATH= auxiliary argument to the ssh command if that
311 ** is called for.
312 */
313 if( g.url.isSsh && (g.url.flags & URL_SSH_RETRY)==0 ){
314 char *z = mprintf("use-path-for-ssh:%s", g.url.hostname);
315 if( db_get_boolean(z/*works-like:"x"*/, 0) ){
316 g.url.flags |= URL_SSH_PATH;
317 }
318 fossil_free(z);
319 }
320
321 if( transport_open(&g.url) ){
322 fossil_warning("%s", transport_errmsg(&g.url));
323 return 1;
@@ -506,24 +622,21 @@
506 && (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
507 && (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
508 ){
509 /* Retry after flipping the SSH_PATH setting */
510 transport_close(&g.url);
511 if( g.fSshTrace ){
512 printf("Retry after %s the PATH= argument to the SSH command\n",
513 (g.url.flags & URL_SSH_PATH)!=0 ? "removing" : "adding");
514 }
 
 
515 g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
516 rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
517 if( rc==0 ){
518 char *z = mprintf("use-path-for-ssh:%s", g.url.hostname);
519 if( (g.url.flags & URL_SSH_PATH) ){
520 db_set(z/*works-like:"x"*/, "1", 0);
521 }else{
522 db_unset(z/*works-like:"x"*/, 0);
523 }
524 fossil_free(z);
525 }
526 return rc;
527 }else{
528 /* The problem could not be corrected by retrying. Report the
529 ** the error. */
530
--- src/http.c
+++ src/http.c
@@ -269,10 +269,127 @@
269 blob_read_from_file(pReply, zDownlink, ExtFILE);
270 file_delete(zDownlink);
271 }
272 return rc;
273 }
274
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. Return
277 ** true if a PATH= should be provided and 0 if not.
278 **
279 ** If iTruth is 1 or 0 then that means that the PATH= is or is not required,
280 ** respectively. Record this fact for future reference.
281 **
282 ** If iTruth is 99 or more, then toggle the value that will be returned
283 ** for future iTruth==(-1) queries.
284 */
285 int ssh_needs_path_argument(const char *zHostname, int iTruth){
286 int ans = 0; /* Default to "no" */
287 char *z = mprintf("use-path-for-ssh:%s", zHostname);
288 if( iTruth<0 ){
289 if( db_get_boolean(z/*works-like:"x"*/, 0) ) ans = 1;
290 }else{
291 if( iTruth>=99 ){
292 iTruth = !db_get_boolean(z/*works-like:"x"*/, 0);
293 }
294 if( iTruth ){
295 ans = 1;
296 db_set(z/*works-like:"x"*/, "1", 1);
297 }else{
298 db_unset(z/*works-like:"x"*/, 1);
299 }
300 }
301 fossil_free(z);
302 return ans;
303 }
304
305 /*
306 ** COMMAND: test-ssh-needs-path
307 **
308 ** Usage: fossil test-ssh-needs-path ?HOSTNAME? ?BOOLEAN?
309 **
310 ** With one argument, show whether or not the PATH= argument is included
311 ** by default for HOSTNAME. If the second argument is a boolean, then
312 ** change the value.
313 **
314 ** With no arguments, show all hosts for which ssh-needs-path is true.
315 */
316 void test_ssh_needs_path(void){
317 db_find_and_open_repository(0,0);
318 if( g.argc>=3 ){
319 const char *zHost = g.argv[2];
320 int a = -1;
321 int rc;
322 if( g.argc>=4 ) a = is_truth(g.argv[3]);
323 rc = ssh_needs_path_argument(zHost, a);
324 fossil_print("%-20s %s\n", zHost, rc ? "yes" : "no");
325 }else{
326 Stmt s;
327 db_prepare(&s, "SELECT substr(name,18) FROM config"
328 " WHERE name GLOB 'use-path-for-ssh:*'");
329 while( db_step(&s)==SQLITE_ROW ){
330 const char *zHost = db_column_text(&s,0);
331 fossil_print("%-20s yes\n", zHost);
332 }
333 db_finalize(&s);
334 }
335 }
336
337 /* Add an approprate PATH= argument to the SSH command under construction
338 ** in pCmd.
339 **
340 ** About This Feature
341 ** ==================
342 **
343 ** On some ssh servers (Macs in particular are guilty of this) the PATH
344 ** variable in the shell that runs the command that is sent to the remote
345 ** host contains a limited number of read-only system directories:
346 **
347 ** /usr/bin:/bin:/usr/sbin:/sbin
348 **
349 ** The fossil executable cannot be installed into any of those directories
350 ** because they are locked down, and so the "fossil" command cannot run.
351 **
352 ** To work around this, the fossil command is prefixed with the PATH=
353 ** argument, inserted by this function, to augment the PATH with additional
354 ** directories in which the fossil executable is often found.
355 **
356 ** But other ssh servers are confused by this initial PATH= argument.
357 ** Some ssh servers have a list of programs that they are allowed to run
358 ** and will fail if the first argument is not on that list, and PATH=....
359 ** is not on that list.
360 **
361 ** So that various commands that use ssh can run seamlessly on a variety
362 ** of systems (commands that use ssh include "fossil sync" with an ssh:
363 ** URL and the "fossil patch pull" and "fossil patch push" commands where
364 ** the destination directory starts with HOSTNAME: or USER@HOSTNAME:.)
365 ** the following algorithm is used:
366 **
367 ** * First try running the fossil without any PATH= argument. If that
368 ** works (and it does on a majority of systems) then we are done.
369 **
370 ** * If the first attempt fails, then try again after adding the
371 ** PATH= prefix argument. (This function is what adds that
372 ** argument.) If the retry works, then remember that fact using
373 ** the use-path-for-ssh:HOSTNAME setting so that the first step
374 ** is skipped on subsequent uses of the same command.
375 **
376 ** See the forum thread at
377 ** https://fossil-scm.org/forum/forumpost/4903cb4b691af7ce for more
378 ** background.
379 **
380 ** See also:
381 **
382 ** * The ssh_needs_path_argument() function above.
383 ** * The test-ssh-needs-path command that shows the settings
384 ** that cache whether or not a PATH= is needed for a particular
385 ** HOSTNAME.
386 */
387 void ssh_add_path_argument(Blob *pCmd){
388 blob_append_escaped_arg(pCmd,
389 "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
390 }
391
392 /*
393 ** Sign the content in pSend, compress it, and send it to the server
394 ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply
395 ** in pRecv. pRecv is assumed to be uninitialized when
@@ -308,16 +425,15 @@
425 }
426
427 /* Activate the PATH= auxiliary argument to the ssh command if that
428 ** is called for.
429 */
430 if( g.url.isSsh
431 && (g.url.flags & URL_SSH_RETRY)==0
432 && ssh_needs_path_argument(g.url.hostname, -1)
433 ){
434 g.url.flags |= URL_SSH_PATH;
 
435 }
436
437 if( transport_open(&g.url) ){
438 fossil_warning("%s", transport_errmsg(&g.url));
439 return 1;
@@ -506,24 +622,21 @@
622 && (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
623 && (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
624 ){
625 /* Retry after flipping the SSH_PATH setting */
626 transport_close(&g.url);
627 fossil_print(
628 "First attempt to run fossil on %s using SSH failed.\n"
629 "Retrying %s the PATH= argument.\n",
630 g.url.hostname,
631 (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
632 );
633 g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
634 rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
635 if( rc==0 ){
636 (void)ssh_needs_path_argument(g.url.hostname,
637 (g.url.flags & URL_SSH_PATH)!=0);
 
 
 
 
 
638 }
639 return rc;
640 }else{
641 /* The problem could not be corrected by retrying. Report the
642 ** the error. */
643
--- src/http_transport.c
+++ src/http_transport.c
@@ -140,12 +140,11 @@
140140
"the server.", pUrlData->fossil);
141141
}
142142
if( (pUrlData->flags & URL_SSH_EXE)==0
143143
&& (pUrlData->flags & URL_SSH_PATH)!=0
144144
){
145
- blob_append_escaped_arg(&zCmd,
146
- "PATH=bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
145
+ ssh_add_path_argument(&zCmd);
147146
}
148147
blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
149148
blob_append(&zCmd, " test-http", 10);
150149
if( pUrlData->path && pUrlData->path[0] ){
151150
blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
152151
--- src/http_transport.c
+++ src/http_transport.c
@@ -140,12 +140,11 @@
140 "the server.", pUrlData->fossil);
141 }
142 if( (pUrlData->flags & URL_SSH_EXE)==0
143 && (pUrlData->flags & URL_SSH_PATH)!=0
144 ){
145 blob_append_escaped_arg(&zCmd,
146 "PATH=bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
147 }
148 blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
149 blob_append(&zCmd, " test-http", 10);
150 if( pUrlData->path && pUrlData->path[0] ){
151 blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
152
--- src/http_transport.c
+++ src/http_transport.c
@@ -140,12 +140,11 @@
140 "the server.", pUrlData->fossil);
141 }
142 if( (pUrlData->flags & URL_SSH_EXE)==0
143 && (pUrlData->flags & URL_SSH_PATH)!=0
144 ){
145 ssh_add_path_argument(&zCmd);
 
146 }
147 blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
148 blob_append(&zCmd, " test-http", 10);
149 if( pUrlData->path && pUrlData->path[0] ){
150 blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
151
+56 -37
--- src/main.c
+++ src/main.c
@@ -3343,49 +3343,68 @@
33433343
/* If a USER@HOST:REPO argument is supplied, then use SSH to run
33443344
** "fossil ui --nobrowser" on the remote system and to set up a
33453345
** tunnel from the local machine to the remote. */
33463346
FILE *sshIn;
33473347
Blob ssh;
3348
+ int bRunning = 0; /* True when fossil starts up on the remote */
3349
+ int isRetry; /* True if on the second attempt */
33483350
char zLine[1000];
3351
+
33493352
blob_init(&ssh, 0, 0);
3350
- transport_ssh_command(&ssh);
3351
- db_close_config();
3352
- blob_appendf(&ssh,
3353
- " -t -L 127.0.0.1:%d:127.0.0.1:%d %!$",
3354
- iPort, iPort, zRemote
3355
- );
3356
- if( zFossilCmd==0 ){
3357
- blob_appendf(&ssh, " %$ fossil", "PATH=$HOME/bin:$PATH");
3358
- }else{
3359
- blob_appendf(&ssh, " %$", zFossilCmd);
3360
- }
3361
- blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
3362
- if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
3363
- if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
3364
- if( g.zCkoutAlias ) blob_appendf(&ssh, " --ckout-alias %!$",g.zCkoutAlias);
3365
- if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
3366
- if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
3367
- if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
3368
- if( fCreate ) blob_appendf(&ssh, " --create");
3369
- blob_appendf(&ssh, " %$", g.argv[2]);
3370
- fossil_print("%s\n", blob_str(&ssh));
3371
- sshIn = popen(blob_str(&ssh), "r");
3372
- if( sshIn==0 ){
3373
- fossil_fatal("unable to %s", blob_str(&ssh));
3374
- }
3375
- while( fgets(zLine, sizeof(zLine), sshIn) ){
3376
- fputs(zLine, stdout);
3377
- fflush(stdout);
3378
- if( zBrowserCmd && sqlite3_strglob("*Listening for HTTP*",zLine)==0 ){
3379
- char *zCmd = mprintf(zBrowserCmd/*works-like:"%d"*/,iPort);
3380
- fossil_system(zCmd);
3381
- fossil_free(zCmd);
3382
- fossil_free(zBrowserCmd);
3383
- zBrowserCmd = 0;
3384
- }
3385
- }
3386
- pclose(sshIn);
3353
+ for(isRetry=0; isRetry<2 && !bRunning; isRetry++){
3354
+ blob_reset(&ssh);
3355
+ transport_ssh_command(&ssh);
3356
+ blob_appendf(&ssh,
3357
+ " -t -L 127.0.0.1:%d:127.0.0.1:%d %!$",
3358
+ iPort, iPort, zRemote
3359
+ );
3360
+ if( zFossilCmd==0 ){
3361
+ if( ssh_needs_path_argument(zRemote,-1) ^ isRetry ){
3362
+ ssh_add_path_argument(&ssh);
3363
+ }
3364
+ blob_append_escaped_arg(&ssh, "fossil", 1);
3365
+ }else{
3366
+ blob_appendf(&ssh, " %$", zFossilCmd);
3367
+ }
3368
+ blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
3369
+ if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
3370
+ if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
3371
+ if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias);
3372
+ if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
3373
+ if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
3374
+ if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
3375
+ if( fCreate ) blob_appendf(&ssh, " --create");
3376
+ blob_appendf(&ssh, " %$", g.argv[2]);
3377
+ if( isRetry ){
3378
+ fossil_print("First attempt to run \"fossil\" on %s failed\n"
3379
+ "Retry: ", zRemote);
3380
+ }
3381
+ fossil_print("%s\n", blob_str(&ssh));
3382
+ sshIn = popen(blob_str(&ssh), "r");
3383
+ if( sshIn==0 ){
3384
+ fossil_fatal("unable to %s", blob_str(&ssh));
3385
+ }
3386
+ while( fgets(zLine, sizeof(zLine), sshIn) ){
3387
+ fputs(zLine, stdout);
3388
+ fflush(stdout);
3389
+ if( !bRunning && sqlite3_strglob("*Listening for HTTP*",zLine)==0 ){
3390
+ bRunning = 1;
3391
+ if( isRetry ){
3392
+ ssh_needs_path_argument(zRemote,99);
3393
+ }
3394
+ db_close_config();
3395
+ if( zBrowserCmd ){
3396
+ char *zCmd = mprintf(zBrowserCmd/*works-like:"%d"*/,iPort);
3397
+ fossil_system(zCmd);
3398
+ fossil_free(zCmd);
3399
+ fossil_free(zBrowserCmd);
3400
+ zBrowserCmd = 0;
3401
+ }
3402
+ }
3403
+ }
3404
+ pclose(sshIn);
3405
+ }
33873406
fossil_free(zBrowserCmd);
33883407
return;
33893408
}
33903409
if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
33913410
if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
33923411
--- src/main.c
+++ src/main.c
@@ -3343,49 +3343,68 @@
3343 /* If a USER@HOST:REPO argument is supplied, then use SSH to run
3344 ** "fossil ui --nobrowser" on the remote system and to set up a
3345 ** tunnel from the local machine to the remote. */
3346 FILE *sshIn;
3347 Blob ssh;
 
 
3348 char zLine[1000];
 
3349 blob_init(&ssh, 0, 0);
3350 transport_ssh_command(&ssh);
3351 db_close_config();
3352 blob_appendf(&ssh,
3353 " -t -L 127.0.0.1:%d:127.0.0.1:%d %!$",
3354 iPort, iPort, zRemote
3355 );
3356 if( zFossilCmd==0 ){
3357 blob_appendf(&ssh, " %$ fossil", "PATH=$HOME/bin:$PATH");
3358 }else{
3359 blob_appendf(&ssh, " %$", zFossilCmd);
3360 }
3361 blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
3362 if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
3363 if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
3364 if( g.zCkoutAlias ) blob_appendf(&ssh, " --ckout-alias %!$",g.zCkoutAlias);
3365 if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
3366 if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
3367 if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
3368 if( fCreate ) blob_appendf(&ssh, " --create");
3369 blob_appendf(&ssh, " %$", g.argv[2]);
3370 fossil_print("%s\n", blob_str(&ssh));
3371 sshIn = popen(blob_str(&ssh), "r");
3372 if( sshIn==0 ){
3373 fossil_fatal("unable to %s", blob_str(&ssh));
3374 }
3375 while( fgets(zLine, sizeof(zLine), sshIn) ){
3376 fputs(zLine, stdout);
3377 fflush(stdout);
3378 if( zBrowserCmd && sqlite3_strglob("*Listening for HTTP*",zLine)==0 ){
3379 char *zCmd = mprintf(zBrowserCmd/*works-like:"%d"*/,iPort);
3380 fossil_system(zCmd);
3381 fossil_free(zCmd);
3382 fossil_free(zBrowserCmd);
3383 zBrowserCmd = 0;
3384 }
3385 }
3386 pclose(sshIn);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3387 fossil_free(zBrowserCmd);
3388 return;
3389 }
3390 if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
3391 if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
3392
--- src/main.c
+++ src/main.c
@@ -3343,49 +3343,68 @@
3343 /* If a USER@HOST:REPO argument is supplied, then use SSH to run
3344 ** "fossil ui --nobrowser" on the remote system and to set up a
3345 ** tunnel from the local machine to the remote. */
3346 FILE *sshIn;
3347 Blob ssh;
3348 int bRunning = 0; /* True when fossil starts up on the remote */
3349 int isRetry; /* True if on the second attempt */
3350 char zLine[1000];
3351
3352 blob_init(&ssh, 0, 0);
3353 for(isRetry=0; isRetry<2 && !bRunning; isRetry++){
3354 blob_reset(&ssh);
3355 transport_ssh_command(&ssh);
3356 blob_appendf(&ssh,
3357 " -t -L 127.0.0.1:%d:127.0.0.1:%d %!$",
3358 iPort, iPort, zRemote
3359 );
3360 if( zFossilCmd==0 ){
3361 if( ssh_needs_path_argument(zRemote,-1) ^ isRetry ){
3362 ssh_add_path_argument(&ssh);
3363 }
3364 blob_append_escaped_arg(&ssh, "fossil", 1);
3365 }else{
3366 blob_appendf(&ssh, " %$", zFossilCmd);
3367 }
3368 blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
3369 if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
3370 if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
3371 if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias);
3372 if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
3373 if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
3374 if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
3375 if( fCreate ) blob_appendf(&ssh, " --create");
3376 blob_appendf(&ssh, " %$", g.argv[2]);
3377 if( isRetry ){
3378 fossil_print("First attempt to run \"fossil\" on %s failed\n"
3379 "Retry: ", zRemote);
3380 }
3381 fossil_print("%s\n", blob_str(&ssh));
3382 sshIn = popen(blob_str(&ssh), "r");
3383 if( sshIn==0 ){
3384 fossil_fatal("unable to %s", blob_str(&ssh));
3385 }
3386 while( fgets(zLine, sizeof(zLine), sshIn) ){
3387 fputs(zLine, stdout);
3388 fflush(stdout);
3389 if( !bRunning && sqlite3_strglob("*Listening for HTTP*",zLine)==0 ){
3390 bRunning = 1;
3391 if( isRetry ){
3392 ssh_needs_path_argument(zRemote,99);
3393 }
3394 db_close_config();
3395 if( zBrowserCmd ){
3396 char *zCmd = mprintf(zBrowserCmd/*works-like:"%d"*/,iPort);
3397 fossil_system(zCmd);
3398 fossil_free(zCmd);
3399 fossil_free(zBrowserCmd);
3400 zBrowserCmd = 0;
3401 }
3402 }
3403 }
3404 pclose(sshIn);
3405 }
3406 fossil_free(zBrowserCmd);
3407 return;
3408 }
3409 if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
3410 if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
3411
+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
- blob_append_escaped_arg(&cmd, "PATH=$HOME/bin:$PATH", 0);
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 blob_append_escaped_arg(&cmd, "PATH=$HOME/bin:$PATH", 0);
 
 
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