Fossil SCM

Code in place for "patch pull" and "patch push".

drh 2021-06-22 12:24 patch-cmd
Commit ccfdc89c962ddc7bdad883c4553b57a58e13a1194e897494382823b149c77e86
2 files changed +10 -3 +178 -88
--- src/http_transport.c
+++ src/http_transport.c
@@ -97,25 +97,32 @@
9797
#ifdef _WIN32
9898
static const char zDefaultSshCmd[] = "plink -ssh -T";
9999
#else
100100
static const char zDefaultSshCmd[] = "ssh -e none -T";
101101
#endif
102
+
103
+/*
104
+** Initialize a Blob to the name of the configured SSH command.
105
+*/
106
+void transport_ssh_command(Blob *p){
107
+ char *zSsh; /* The base SSH command */
108
+ zSsh = db_get("ssh-command", zDefaultSshCmd);
109
+ blob_init(p, zSsh, -1);
110
+}
102111
103112
/*
104113
** SSH initialization of the transport layer
105114
*/
106115
int transport_ssh_open(UrlData *pUrlData){
107116
/* For SSH we need to create and run SSH fossil http
108117
** to talk to the remote machine.
109118
*/
110
- char *zSsh; /* The base SSH command */
111119
Blob zCmd; /* The SSH command */
112120
char *zHost; /* The host name to contact */
113121
114122
socket_ssh_resolve_addr(pUrlData);
115
- zSsh = db_get("ssh-command", zDefaultSshCmd);
116
- blob_init(&zCmd, zSsh, -1);
123
+ transport_ssh_command(&zCmd);
117124
if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
118125
#ifdef _WIN32
119126
blob_appendf(&zCmd, " -P %d", pUrlData->port);
120127
#else
121128
blob_appendf(&zCmd, " -p %d", pUrlData->port);
122129
--- src/http_transport.c
+++ src/http_transport.c
@@ -97,25 +97,32 @@
97 #ifdef _WIN32
98 static const char zDefaultSshCmd[] = "plink -ssh -T";
99 #else
100 static const char zDefaultSshCmd[] = "ssh -e none -T";
101 #endif
 
 
 
 
 
 
 
 
 
102
103 /*
104 ** SSH initialization of the transport layer
105 */
106 int transport_ssh_open(UrlData *pUrlData){
107 /* For SSH we need to create and run SSH fossil http
108 ** to talk to the remote machine.
109 */
110 char *zSsh; /* The base SSH command */
111 Blob zCmd; /* The SSH command */
112 char *zHost; /* The host name to contact */
113
114 socket_ssh_resolve_addr(pUrlData);
115 zSsh = db_get("ssh-command", zDefaultSshCmd);
116 blob_init(&zCmd, zSsh, -1);
117 if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
118 #ifdef _WIN32
119 blob_appendf(&zCmd, " -P %d", pUrlData->port);
120 #else
121 blob_appendf(&zCmd, " -p %d", pUrlData->port);
122
--- src/http_transport.c
+++ src/http_transport.c
@@ -97,25 +97,32 @@
97 #ifdef _WIN32
98 static const char zDefaultSshCmd[] = "plink -ssh -T";
99 #else
100 static const char zDefaultSshCmd[] = "ssh -e none -T";
101 #endif
102
103 /*
104 ** Initialize a Blob to the name of the configured SSH command.
105 */
106 void transport_ssh_command(Blob *p){
107 char *zSsh; /* The base SSH command */
108 zSsh = db_get("ssh-command", zDefaultSshCmd);
109 blob_init(p, zSsh, -1);
110 }
111
112 /*
113 ** SSH initialization of the transport layer
114 */
115 int transport_ssh_open(UrlData *pUrlData){
116 /* For SSH we need to create and run SSH fossil http
117 ** to talk to the remote machine.
118 */
 
119 Blob zCmd; /* The SSH command */
120 char *zHost; /* The host name to contact */
121
122 socket_ssh_resolve_addr(pUrlData);
123 transport_ssh_command(&zCmd);
 
124 if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
125 #ifdef _WIN32
126 blob_appendf(&zCmd, " -P %d", pUrlData->port);
127 #else
128 blob_appendf(&zCmd, " -p %d", pUrlData->port);
129
+178 -88
--- src/patch.c
+++ src/patch.c
@@ -19,16 +19,26 @@
1919
*/
2020
#include "config.h"
2121
#include "patch.h"
2222
#include <assert.h>
2323
24
+/*
25
+** Additional windows configuration for popen */
26
+#if defined(_WIN32)
27
+# include <io.h>
28
+# include <fcntl.h>
29
+# undef popen
30
+# define popen _popen
31
+#endif
32
+
2433
/*
2534
** Flags passed from the main patch_cmd() routine into subfunctions used
2635
** to implement the various subcommands.
2736
*/
2837
#define PATCH_DRYRUN 0x0001
2938
#define PATCH_VERBOSE 0x0002
39
+#define PATCH_FORCE 0x0004
3040
3141
/*
3242
** Implementation of the "readfile(X)" SQL function. The entire content
3343
** of the checkout file named X is read and returned as a BLOB.
3444
*/
@@ -113,11 +123,11 @@
113123
114124
/*
115125
** Generate a binary patch file and store it into the file
116126
** named zOut.
117127
*/
118
-void patch_create(const char *zOut){
128
+void patch_create(const char *zOut, FILE *out){
119129
int vid;
120130
121131
if( zOut && file_isdir(zOut, ExtFILE)!=0 ){
122132
fossil_fatal("patch file already exists: %s", zOut);
123133
}
@@ -147,13 +157,10 @@
147157
vid = db_lget_int("checkout", 0);
148158
vfile_check_signature(vid, CKSIG_ENOTFILE);
149159
db_multi_exec(
150160
"INSERT INTO patch.cfg(key,value)"
151161
"SELECT 'baseline',uuid FROM blob WHERE rid=%d", vid);
152
- if( db_exists("SELECT 1 FROM vmerge") ){
153
- db_multi_exec("INSERT INTO patch.cfg(key,value)VALUES('merged',1);");
154
- }
155162
156163
/* New files */
157164
db_multi_exec(
158165
"INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)"
159166
" SELECT pathname, NULL, isexe, islink,"
@@ -198,53 +205,38 @@
198205
unsigned char *pData;
199206
pData = sqlite3_serialize(g.db, "patch", &sz, 0);
200207
if( pData==0 ){
201208
fossil_fatal("out of memory");
202209
}
203
- fossil_print("%025lld\n", sz);
204
- fwrite(pData, sz, 1, stdout);
210
+ fwrite(pData, sz, 1, out);
205211
sqlite3_free(pData);
206
- fflush(stdout);
212
+ fflush(out);
207213
}
208214
}
209215
210216
/*
211217
** Attempt to load and validate a patchfile identified by the first
212218
** argument.
213219
*/
214
-void patch_attach(const char *zIn){
220
+void patch_attach(const char *zIn, FILE *in){
215221
Stmt q;
216222
if( g.db==0 ){
217223
sqlite3_open(":memory:", &g.db);
218224
}
219225
if( zIn==0 ){
220
- char zBuf[26];
221
- size_t n;
222
- sqlite3_int64 sz;
223
- unsigned char *aIn;
224
-
225
- n = fread(zBuf, sizeof(zBuf), 1, stdin);
226
- if( n!=1 ){
227
- fossil_fatal("unable to read size of input\n");
228
- }
229
- zBuf[sizeof(zBuf)-1] = 0;
230
- sz = atoll(zBuf);
231
- if( sz<512 || (sz%512)!=0 ){
232
- fossil_fatal("bad size for input: %lld", sz);
233
- }
234
- aIn = sqlite3_malloc64( sz );
235
- if( aIn==0 ){
236
- fossil_fatal("out of memory");
237
- }
238
- n = fread(aIn, 1, sz, stdin);
239
- if( n!=sz ){
240
- fossil_fatal("got only %lld of %lld input bytes",
241
- (sqlite3_int64)n, sz);
242
- }
226
+ Blob buf;
227
+ int rc;
228
+ int sz;
229
+ const unsigned char *pData;
230
+ blob_init(&buf, 0, 0);
231
+ sz = blob_read_from_channel(&buf, in, -1);
232
+ pData = (const unsigned char*)blob_buffer(&buf);
243233
db_multi_exec("ATTACH ':memory:' AS patch");
244
- sqlite3_deserialize(g.db, "patch", aIn, sz, sz,
245
- SQLITE_DESERIALIZE_FREEONCLOSE);
234
+ rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0);
235
+ if( rc ){
236
+ fossil_fatal("cannot open patch database: %s", sqlite3_errmsg(g.db));
237
+ }
246238
}else if( !file_isfile(zIn, ExtFILE) ){
247239
fossil_fatal("no such file: %s", zIn);
248240
}else{
249241
db_multi_exec("ATTACH %Q AS patch", zIn);
250242
}
@@ -310,10 +302,14 @@
310302
** and update all files.
311303
*/
312304
void patch_apply(unsigned mFlags){
313305
Stmt q;
314306
Blob cmd;
307
+
308
+ if( (mFlags & PATCH_FORCE)==0 && unsaved_changes(0) ){
309
+ fossil_fatal("there are unsaved changes in the current checkout");
310
+ }
315311
blob_init(&cmd, 0, 0);
316312
file_chdir(g.zLocalRoot, 0);
317313
db_prepare(&q,
318314
"SELECT patch.cfg.value"
319315
" FROM patch.cfg, localdb.vvar"
@@ -503,10 +499,107 @@
503499
}
504500
blob_reset(&cmd);
505501
}
506502
}
507503
504
+/*
505
+** Find the filename of the patch file to be used by
506
+** "fossil patch apply" or "fossil patch create".
507
+**
508
+** If the name is "-" return NULL.
509
+**
510
+** Otherwise, if there is a prior DIRECTORY argument, or if
511
+** the --dir64 option is present, first chdir to the specified
512
+** directory, and translate the name in the argument accordingly.
513
+**
514
+**
515
+** The returned name is obtained from fossil_malloc() and should
516
+** be freed by the caller.
517
+*/
518
+static char *patch_find_patch_filename(const char *zCmdName){
519
+ const char *zDir64 = find_option("dir64",0,1);
520
+ const char *zDir = 0;
521
+ const char *zBaseName;
522
+ char *zToFree = 0;
523
+ char *zPatchFile = 0;
524
+ if( zDir64 ){
525
+ zToFree = decode64(zDir64, 0);
526
+ zDir = zToFree;
527
+ }
528
+ verify_all_options();
529
+ if( g.argc!=4 && g.argc!=5 ){
530
+ usage(mprintf("%s [DIRECTORY] FILENAME", zCmdName));
531
+ }
532
+ if( g.argc==5 ){
533
+ zDir = g.argv[3];
534
+ zBaseName = g.argv[4];
535
+ }else{
536
+ zBaseName = g.argv[3];
537
+ }
538
+ if( fossil_strcmp(zBaseName, "-")==0 ){
539
+ zPatchFile = 0;
540
+ }else if( zDir ){
541
+ zPatchFile = file_canonical_name_dup(g.argv[4]);
542
+ }else{
543
+ zPatchFile = fossil_strdup(g.argv[3]);
544
+ }
545
+ if( zDir && file_chdir(zDir,0) ){
546
+ fossil_fatal("cannot change to directory \"%s\"", zDir);
547
+ }
548
+ fossil_free(zToFree);
549
+ return zPatchFile;
550
+}
551
+
552
+/*
553
+** Create a FILE* that will execute the remote side of a push or pull
554
+** using ssh (probably) or fossil for local pushes and pulls. Return
555
+*/
556
+static FILE *patch_remote_command(
557
+ unsigned mFlags, /* flags */
558
+ const char *zThisCmd, /* "push" or "pull" */
559
+ const char *zRemoteCmd, /* "apply" or "create" */
560
+ const char *zRW /* "w" or "r" */
561
+){
562
+ char *zRemote;
563
+ char *zDir;
564
+ Blob cmd;
565
+ FILE *f;
566
+ const char *zForce = (mFlags & PATCH_FORCE)!=0 ? " -f" : "";
567
+ if( g.argc!=4 ){
568
+ usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
569
+ }
570
+ zRemote = fossil_strdup(g.argv[3]);
571
+ zDir = strchr(zRemote,':');
572
+ if( zDir==0 ){
573
+ zDir = zRemote;
574
+ blob_init(&cmd, 0, 0);
575
+ blob_append_escaped_arg(&cmd, g.nameOfExe);
576
+ blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
577
+ }else{
578
+ Blob remote;
579
+ zDir[0] = 0;
580
+ zDir++;
581
+ transport_ssh_command(&cmd);
582
+ blob_append_escaped_arg(&cmd, zRemote);
583
+ blob_init(&remote, 0, 0);
584
+ blob_appendf(&remote, "fossil patch %s%s --dir64 %z -",
585
+ zRemoteCmd, zForce, encode64(zDir, -1));
586
+ blob_append_escaped_arg(&cmd, blob_str(&remote));
587
+ blob_reset(&remote);
588
+ }
589
+ if( mFlags & PATCH_VERBOSE ){
590
+ fossil_print("# %s\n", blob_str(&cmd));
591
+ fflush(stdout);
592
+ }
593
+ f = popen(blob_str(&cmd), zRW);
594
+ if( f==0 ){
595
+ fossil_fatal("cannot run command: %s", blob_str(&cmd));
596
+ }
597
+ blob_reset(&cmd);
598
+ return f;
599
+}
600
+
508601
509602
/*
510603
** COMMAND: patch
511604
**
512605
** Usage: %fossil patch SUBCOMMAND ?ARGS ..?
@@ -545,10 +638,15 @@
545638
** > fossil patch pull REMOTE-CHECKOUT
546639
**
547640
** Create a patch on a remote check-out, transfer that patch to the
548641
** local machine (using ssh) and apply the patch in the local checkout.
549642
**
643
+** -f|--force Apply the patch even though there are unsaved
644
+** changes in the current check-out.
645
+** -n|--dryrun Do nothing, but print what would have happened.
646
+** -v|--verbose Extra output explaining what happens.
647
+**
550648
** > fossil patch view FILENAME
551649
**
552650
** View a summary of the the changes in the binary patch FILENAME.
553651
**
554652
*/
@@ -560,75 +658,67 @@
560658
usage("apply|create|pull|push|view");
561659
}
562660
zCmd = g.argv[2];
563661
n = strlen(zCmd);
564662
if( strncmp(zCmd, "apply", n)==0 ){
565
- int forceFlag = find_option("force","f",0)!=0;
566
- unsigned flags = 0;
567
- const char *zIn;
568
- if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
569
- if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
570
- verify_all_options();
571
- if( g.argc!=4 && g.argc!=5 ){
572
- usage("apply [DIRECTORY] FILENAME");
573
- }
574
- if( g.argc==5 ){
575
- file_chdir(g.argv[3], 0);
576
- zIn = g.argv[4];
577
- }else{
578
- zIn = g.argv[3];
579
- }
580
- db_must_be_within_tree();
581
- if( !forceFlag && unsaved_changes(0) ){
582
- fossil_fatal("there are unsaved changes in the current checkout");
583
- }
584
- if( fossil_strcmp(zIn,"-")==0 ) zIn = 0;
585
- patch_attach(zIn);
586
- patch_apply(flags);
587
- }else
588
- if( strncmp(zCmd, "create", n)==0 ){
589
- const char *zOut;
590
- verify_all_options();
591
- if( g.argc!=4 && g.argc!=5 ){
592
- usage("create [DIRECTORY] FILENAME");
593
- }
594
- if( g.argc==5 ){
595
- file_chdir(g.argv[3], 0);
596
- zOut = g.argv[4];
597
- }else{
598
- zOut = g.argv[3];
599
- }
600
- if( fossil_strcmp(zOut, "-")==0 ) zOut = 0;
601
- db_must_be_within_tree();
602
- patch_create(zOut);
603
- }else
604
- if( strncmp(zCmd, "pull", n)==0 ){
605
- db_must_be_within_tree();
606
- verify_all_options();
607
- if( g.argc!=4 ){
608
- usage("pull REMOTE-CHECKOUT");
609
- }
610
- fossil_print("TBD...\n");
611
- }else
612
- if( strncmp(zCmd, "push", n)==0 ){
613
- db_must_be_within_tree();
614
- verify_all_options();
615
- if( g.argc!=4 ){
616
- usage("push REMOTE-CHECKOUT");
617
- }
618
- fossil_print("TBD...\n");
663
+ char *zIn;
664
+ unsigned flags = 0;
665
+ if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
666
+ if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
667
+ if( find_option("force","f",0) ) flags |= PATCH_FORCE;
668
+ zIn = patch_find_patch_filename("apply");
669
+ db_must_be_within_tree();
670
+ patch_attach(zIn, stdin);
671
+ patch_apply(flags);
672
+ fossil_free(zIn);
673
+ }else
674
+ if( strncmp(zCmd, "create", n)==0 ){
675
+ char *zOut;
676
+ zOut = patch_find_patch_filename("create");
677
+ db_must_be_within_tree();
678
+ patch_create(zOut, stdout);
679
+ fossil_free(zOut);
680
+ }else
681
+ if( strncmp(zCmd, "pull", n)==0 ){
682
+ FILE *pIn = 0;
683
+ unsigned flags = 0;
684
+ if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
685
+ if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
686
+ if( find_option("force","f",0) ) flags |= PATCH_FORCE;
687
+ db_must_be_within_tree();
688
+ verify_all_options();
689
+ pIn = patch_remote_command(flags & (~PATCH_FORCE), "pull", "create", "r");
690
+ if( pIn ){
691
+ patch_attach(0, pIn);
692
+ pclose(pIn);
693
+ patch_apply(flags);
694
+ }
695
+ }else
696
+ if( strncmp(zCmd, "push", n)==0 ){
697
+ FILE *pOut = 0;
698
+ unsigned flags = 0;
699
+ if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
700
+ if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
701
+ if( find_option("force","f",0) ) flags |= PATCH_FORCE;
702
+ db_must_be_within_tree();
703
+ verify_all_options();
704
+ pOut = patch_remote_command(flags, "push", "apply", "w");
705
+ if( pOut ){
706
+ patch_create(0, pOut);
707
+ pclose(pOut);
708
+ }
619709
}else
620710
if( strncmp(zCmd, "view", n)==0 ){
621711
const char *zIn;
622712
verify_all_options();
623713
if( g.argc!=4 ){
624714
usage("view FILENAME");
625715
}
626716
zIn = g.argv[3];
627717
if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
628
- patch_attach(zIn);
718
+ patch_attach(zIn, stdin);
629719
patch_view();
630720
}else
631721
{
632722
goto patch_usage;
633723
}
634724
}
635725
--- src/patch.c
+++ src/patch.c
@@ -19,16 +19,26 @@
19 */
20 #include "config.h"
21 #include "patch.h"
22 #include <assert.h>
23
 
 
 
 
 
 
 
 
 
24 /*
25 ** Flags passed from the main patch_cmd() routine into subfunctions used
26 ** to implement the various subcommands.
27 */
28 #define PATCH_DRYRUN 0x0001
29 #define PATCH_VERBOSE 0x0002
 
30
31 /*
32 ** Implementation of the "readfile(X)" SQL function. The entire content
33 ** of the checkout file named X is read and returned as a BLOB.
34 */
@@ -113,11 +123,11 @@
113
114 /*
115 ** Generate a binary patch file and store it into the file
116 ** named zOut.
117 */
118 void patch_create(const char *zOut){
119 int vid;
120
121 if( zOut && file_isdir(zOut, ExtFILE)!=0 ){
122 fossil_fatal("patch file already exists: %s", zOut);
123 }
@@ -147,13 +157,10 @@
147 vid = db_lget_int("checkout", 0);
148 vfile_check_signature(vid, CKSIG_ENOTFILE);
149 db_multi_exec(
150 "INSERT INTO patch.cfg(key,value)"
151 "SELECT 'baseline',uuid FROM blob WHERE rid=%d", vid);
152 if( db_exists("SELECT 1 FROM vmerge") ){
153 db_multi_exec("INSERT INTO patch.cfg(key,value)VALUES('merged',1);");
154 }
155
156 /* New files */
157 db_multi_exec(
158 "INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)"
159 " SELECT pathname, NULL, isexe, islink,"
@@ -198,53 +205,38 @@
198 unsigned char *pData;
199 pData = sqlite3_serialize(g.db, "patch", &sz, 0);
200 if( pData==0 ){
201 fossil_fatal("out of memory");
202 }
203 fossil_print("%025lld\n", sz);
204 fwrite(pData, sz, 1, stdout);
205 sqlite3_free(pData);
206 fflush(stdout);
207 }
208 }
209
210 /*
211 ** Attempt to load and validate a patchfile identified by the first
212 ** argument.
213 */
214 void patch_attach(const char *zIn){
215 Stmt q;
216 if( g.db==0 ){
217 sqlite3_open(":memory:", &g.db);
218 }
219 if( zIn==0 ){
220 char zBuf[26];
221 size_t n;
222 sqlite3_int64 sz;
223 unsigned char *aIn;
224
225 n = fread(zBuf, sizeof(zBuf), 1, stdin);
226 if( n!=1 ){
227 fossil_fatal("unable to read size of input\n");
228 }
229 zBuf[sizeof(zBuf)-1] = 0;
230 sz = atoll(zBuf);
231 if( sz<512 || (sz%512)!=0 ){
232 fossil_fatal("bad size for input: %lld", sz);
233 }
234 aIn = sqlite3_malloc64( sz );
235 if( aIn==0 ){
236 fossil_fatal("out of memory");
237 }
238 n = fread(aIn, 1, sz, stdin);
239 if( n!=sz ){
240 fossil_fatal("got only %lld of %lld input bytes",
241 (sqlite3_int64)n, sz);
242 }
243 db_multi_exec("ATTACH ':memory:' AS patch");
244 sqlite3_deserialize(g.db, "patch", aIn, sz, sz,
245 SQLITE_DESERIALIZE_FREEONCLOSE);
 
 
246 }else if( !file_isfile(zIn, ExtFILE) ){
247 fossil_fatal("no such file: %s", zIn);
248 }else{
249 db_multi_exec("ATTACH %Q AS patch", zIn);
250 }
@@ -310,10 +302,14 @@
310 ** and update all files.
311 */
312 void patch_apply(unsigned mFlags){
313 Stmt q;
314 Blob cmd;
 
 
 
 
315 blob_init(&cmd, 0, 0);
316 file_chdir(g.zLocalRoot, 0);
317 db_prepare(&q,
318 "SELECT patch.cfg.value"
319 " FROM patch.cfg, localdb.vvar"
@@ -503,10 +499,107 @@
503 }
504 blob_reset(&cmd);
505 }
506 }
507
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
509 /*
510 ** COMMAND: patch
511 **
512 ** Usage: %fossil patch SUBCOMMAND ?ARGS ..?
@@ -545,10 +638,15 @@
545 ** > fossil patch pull REMOTE-CHECKOUT
546 **
547 ** Create a patch on a remote check-out, transfer that patch to the
548 ** local machine (using ssh) and apply the patch in the local checkout.
549 **
 
 
 
 
 
550 ** > fossil patch view FILENAME
551 **
552 ** View a summary of the the changes in the binary patch FILENAME.
553 **
554 */
@@ -560,75 +658,67 @@
560 usage("apply|create|pull|push|view");
561 }
562 zCmd = g.argv[2];
563 n = strlen(zCmd);
564 if( strncmp(zCmd, "apply", n)==0 ){
565 int forceFlag = find_option("force","f",0)!=0;
566 unsigned flags = 0;
567 const char *zIn;
568 if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
569 if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
570 verify_all_options();
571 if( g.argc!=4 && g.argc!=5 ){
572 usage("apply [DIRECTORY] FILENAME");
573 }
574 if( g.argc==5 ){
575 file_chdir(g.argv[3], 0);
576 zIn = g.argv[4];
577 }else{
578 zIn = g.argv[3];
579 }
580 db_must_be_within_tree();
581 if( !forceFlag && unsaved_changes(0) ){
582 fossil_fatal("there are unsaved changes in the current checkout");
583 }
584 if( fossil_strcmp(zIn,"-")==0 ) zIn = 0;
585 patch_attach(zIn);
586 patch_apply(flags);
587 }else
588 if( strncmp(zCmd, "create", n)==0 ){
589 const char *zOut;
590 verify_all_options();
591 if( g.argc!=4 && g.argc!=5 ){
592 usage("create [DIRECTORY] FILENAME");
593 }
594 if( g.argc==5 ){
595 file_chdir(g.argv[3], 0);
596 zOut = g.argv[4];
597 }else{
598 zOut = g.argv[3];
599 }
600 if( fossil_strcmp(zOut, "-")==0 ) zOut = 0;
601 db_must_be_within_tree();
602 patch_create(zOut);
603 }else
604 if( strncmp(zCmd, "pull", n)==0 ){
605 db_must_be_within_tree();
606 verify_all_options();
607 if( g.argc!=4 ){
608 usage("pull REMOTE-CHECKOUT");
609 }
610 fossil_print("TBD...\n");
611 }else
612 if( strncmp(zCmd, "push", n)==0 ){
613 db_must_be_within_tree();
614 verify_all_options();
615 if( g.argc!=4 ){
616 usage("push REMOTE-CHECKOUT");
617 }
618 fossil_print("TBD...\n");
619 }else
620 if( strncmp(zCmd, "view", n)==0 ){
621 const char *zIn;
622 verify_all_options();
623 if( g.argc!=4 ){
624 usage("view FILENAME");
625 }
626 zIn = g.argv[3];
627 if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
628 patch_attach(zIn);
629 patch_view();
630 }else
631 {
632 goto patch_usage;
633 }
634 }
635
--- src/patch.c
+++ src/patch.c
@@ -19,16 +19,26 @@
19 */
20 #include "config.h"
21 #include "patch.h"
22 #include <assert.h>
23
24 /*
25 ** Additional windows configuration for popen */
26 #if defined(_WIN32)
27 # include <io.h>
28 # include <fcntl.h>
29 # undef popen
30 # define popen _popen
31 #endif
32
33 /*
34 ** Flags passed from the main patch_cmd() routine into subfunctions used
35 ** to implement the various subcommands.
36 */
37 #define PATCH_DRYRUN 0x0001
38 #define PATCH_VERBOSE 0x0002
39 #define PATCH_FORCE 0x0004
40
41 /*
42 ** Implementation of the "readfile(X)" SQL function. The entire content
43 ** of the checkout file named X is read and returned as a BLOB.
44 */
@@ -113,11 +123,11 @@
123
124 /*
125 ** Generate a binary patch file and store it into the file
126 ** named zOut.
127 */
128 void patch_create(const char *zOut, FILE *out){
129 int vid;
130
131 if( zOut && file_isdir(zOut, ExtFILE)!=0 ){
132 fossil_fatal("patch file already exists: %s", zOut);
133 }
@@ -147,13 +157,10 @@
157 vid = db_lget_int("checkout", 0);
158 vfile_check_signature(vid, CKSIG_ENOTFILE);
159 db_multi_exec(
160 "INSERT INTO patch.cfg(key,value)"
161 "SELECT 'baseline',uuid FROM blob WHERE rid=%d", vid);
 
 
 
162
163 /* New files */
164 db_multi_exec(
165 "INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)"
166 " SELECT pathname, NULL, isexe, islink,"
@@ -198,53 +205,38 @@
205 unsigned char *pData;
206 pData = sqlite3_serialize(g.db, "patch", &sz, 0);
207 if( pData==0 ){
208 fossil_fatal("out of memory");
209 }
210 fwrite(pData, sz, 1, out);
 
211 sqlite3_free(pData);
212 fflush(out);
213 }
214 }
215
216 /*
217 ** Attempt to load and validate a patchfile identified by the first
218 ** argument.
219 */
220 void patch_attach(const char *zIn, FILE *in){
221 Stmt q;
222 if( g.db==0 ){
223 sqlite3_open(":memory:", &g.db);
224 }
225 if( zIn==0 ){
226 Blob buf;
227 int rc;
228 int sz;
229 const unsigned char *pData;
230 blob_init(&buf, 0, 0);
231 sz = blob_read_from_channel(&buf, in, -1);
232 pData = (const unsigned char*)blob_buffer(&buf);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233 db_multi_exec("ATTACH ':memory:' AS patch");
234 rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0);
235 if( rc ){
236 fossil_fatal("cannot open patch database: %s", sqlite3_errmsg(g.db));
237 }
238 }else if( !file_isfile(zIn, ExtFILE) ){
239 fossil_fatal("no such file: %s", zIn);
240 }else{
241 db_multi_exec("ATTACH %Q AS patch", zIn);
242 }
@@ -310,10 +302,14 @@
302 ** and update all files.
303 */
304 void patch_apply(unsigned mFlags){
305 Stmt q;
306 Blob cmd;
307
308 if( (mFlags & PATCH_FORCE)==0 && unsaved_changes(0) ){
309 fossil_fatal("there are unsaved changes in the current checkout");
310 }
311 blob_init(&cmd, 0, 0);
312 file_chdir(g.zLocalRoot, 0);
313 db_prepare(&q,
314 "SELECT patch.cfg.value"
315 " FROM patch.cfg, localdb.vvar"
@@ -503,10 +499,107 @@
499 }
500 blob_reset(&cmd);
501 }
502 }
503
504 /*
505 ** Find the filename of the patch file to be used by
506 ** "fossil patch apply" or "fossil patch create".
507 **
508 ** If the name is "-" return NULL.
509 **
510 ** Otherwise, if there is a prior DIRECTORY argument, or if
511 ** the --dir64 option is present, first chdir to the specified
512 ** directory, and translate the name in the argument accordingly.
513 **
514 **
515 ** The returned name is obtained from fossil_malloc() and should
516 ** be freed by the caller.
517 */
518 static char *patch_find_patch_filename(const char *zCmdName){
519 const char *zDir64 = find_option("dir64",0,1);
520 const char *zDir = 0;
521 const char *zBaseName;
522 char *zToFree = 0;
523 char *zPatchFile = 0;
524 if( zDir64 ){
525 zToFree = decode64(zDir64, 0);
526 zDir = zToFree;
527 }
528 verify_all_options();
529 if( g.argc!=4 && g.argc!=5 ){
530 usage(mprintf("%s [DIRECTORY] FILENAME", zCmdName));
531 }
532 if( g.argc==5 ){
533 zDir = g.argv[3];
534 zBaseName = g.argv[4];
535 }else{
536 zBaseName = g.argv[3];
537 }
538 if( fossil_strcmp(zBaseName, "-")==0 ){
539 zPatchFile = 0;
540 }else if( zDir ){
541 zPatchFile = file_canonical_name_dup(g.argv[4]);
542 }else{
543 zPatchFile = fossil_strdup(g.argv[3]);
544 }
545 if( zDir && file_chdir(zDir,0) ){
546 fossil_fatal("cannot change to directory \"%s\"", zDir);
547 }
548 fossil_free(zToFree);
549 return zPatchFile;
550 }
551
552 /*
553 ** Create a FILE* that will execute the remote side of a push or pull
554 ** using ssh (probably) or fossil for local pushes and pulls. Return
555 */
556 static FILE *patch_remote_command(
557 unsigned mFlags, /* flags */
558 const char *zThisCmd, /* "push" or "pull" */
559 const char *zRemoteCmd, /* "apply" or "create" */
560 const char *zRW /* "w" or "r" */
561 ){
562 char *zRemote;
563 char *zDir;
564 Blob cmd;
565 FILE *f;
566 const char *zForce = (mFlags & PATCH_FORCE)!=0 ? " -f" : "";
567 if( g.argc!=4 ){
568 usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
569 }
570 zRemote = fossil_strdup(g.argv[3]);
571 zDir = strchr(zRemote,':');
572 if( zDir==0 ){
573 zDir = zRemote;
574 blob_init(&cmd, 0, 0);
575 blob_append_escaped_arg(&cmd, g.nameOfExe);
576 blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
577 }else{
578 Blob remote;
579 zDir[0] = 0;
580 zDir++;
581 transport_ssh_command(&cmd);
582 blob_append_escaped_arg(&cmd, zRemote);
583 blob_init(&remote, 0, 0);
584 blob_appendf(&remote, "fossil patch %s%s --dir64 %z -",
585 zRemoteCmd, zForce, encode64(zDir, -1));
586 blob_append_escaped_arg(&cmd, blob_str(&remote));
587 blob_reset(&remote);
588 }
589 if( mFlags & PATCH_VERBOSE ){
590 fossil_print("# %s\n", blob_str(&cmd));
591 fflush(stdout);
592 }
593 f = popen(blob_str(&cmd), zRW);
594 if( f==0 ){
595 fossil_fatal("cannot run command: %s", blob_str(&cmd));
596 }
597 blob_reset(&cmd);
598 return f;
599 }
600
601
602 /*
603 ** COMMAND: patch
604 **
605 ** Usage: %fossil patch SUBCOMMAND ?ARGS ..?
@@ -545,10 +638,15 @@
638 ** > fossil patch pull REMOTE-CHECKOUT
639 **
640 ** Create a patch on a remote check-out, transfer that patch to the
641 ** local machine (using ssh) and apply the patch in the local checkout.
642 **
643 ** -f|--force Apply the patch even though there are unsaved
644 ** changes in the current check-out.
645 ** -n|--dryrun Do nothing, but print what would have happened.
646 ** -v|--verbose Extra output explaining what happens.
647 **
648 ** > fossil patch view FILENAME
649 **
650 ** View a summary of the the changes in the binary patch FILENAME.
651 **
652 */
@@ -560,75 +658,67 @@
658 usage("apply|create|pull|push|view");
659 }
660 zCmd = g.argv[2];
661 n = strlen(zCmd);
662 if( strncmp(zCmd, "apply", n)==0 ){
663 char *zIn;
664 unsigned flags = 0;
665 if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
666 if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
667 if( find_option("force","f",0) ) flags |= PATCH_FORCE;
668 zIn = patch_find_patch_filename("apply");
669 db_must_be_within_tree();
670 patch_attach(zIn, stdin);
671 patch_apply(flags);
672 fossil_free(zIn);
673 }else
674 if( strncmp(zCmd, "create", n)==0 ){
675 char *zOut;
676 zOut = patch_find_patch_filename("create");
677 db_must_be_within_tree();
678 patch_create(zOut, stdout);
679 fossil_free(zOut);
680 }else
681 if( strncmp(zCmd, "pull", n)==0 ){
682 FILE *pIn = 0;
683 unsigned flags = 0;
684 if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
685 if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
686 if( find_option("force","f",0) ) flags |= PATCH_FORCE;
687 db_must_be_within_tree();
688 verify_all_options();
689 pIn = patch_remote_command(flags & (~PATCH_FORCE), "pull", "create", "r");
690 if( pIn ){
691 patch_attach(0, pIn);
692 pclose(pIn);
693 patch_apply(flags);
694 }
695 }else
696 if( strncmp(zCmd, "push", n)==0 ){
697 FILE *pOut = 0;
698 unsigned flags = 0;
699 if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN;
700 if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
701 if( find_option("force","f",0) ) flags |= PATCH_FORCE;
702 db_must_be_within_tree();
703 verify_all_options();
704 pOut = patch_remote_command(flags, "push", "apply", "w");
705 if( pOut ){
706 patch_create(0, pOut);
707 pclose(pOut);
708 }
 
 
 
 
 
 
 
 
709 }else
710 if( strncmp(zCmd, "view", n)==0 ){
711 const char *zIn;
712 verify_all_options();
713 if( g.argc!=4 ){
714 usage("view FILENAME");
715 }
716 zIn = g.argv[3];
717 if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
718 patch_attach(zIn, stdin);
719 patch_view();
720 }else
721 {
722 goto patch_usage;
723 }
724 }
725

Keyboard Shortcuts

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