Index: src/http_transport.c ================================================================== --- src/http_transport.c +++ src/http_transport.c @@ -97,25 +97,32 @@ #ifdef _WIN32 static const char zDefaultSshCmd[] = "plink -ssh -T"; #else static const char zDefaultSshCmd[] = "ssh -e none -T"; #endif + +/* +** Initialize a Blob to the name of the configured SSH command. +*/ +void transport_ssh_command(Blob *p){ + char *zSsh; /* The base SSH command */ + zSsh = db_get("ssh-command", zDefaultSshCmd); + blob_init(p, zSsh, -1); +} /* ** SSH initialization of the transport layer */ int transport_ssh_open(UrlData *pUrlData){ /* For SSH we need to create and run SSH fossil http ** to talk to the remote machine. */ - char *zSsh; /* The base SSH command */ Blob zCmd; /* The SSH command */ char *zHost; /* The host name to contact */ socket_ssh_resolve_addr(pUrlData); - zSsh = db_get("ssh-command", zDefaultSshCmd); - blob_init(&zCmd, zSsh, -1); + transport_ssh_command(&zCmd); if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){ #ifdef _WIN32 blob_appendf(&zCmd, " -P %d", pUrlData->port); #else blob_appendf(&zCmd, " -p %d", pUrlData->port); Index: src/main.mk ================================================================== --- src/main.mk +++ src/main.mk @@ -104,10 +104,11 @@ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ + $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ $(SRCDIR)/piechart.c \ $(SRCDIR)/pikchr.c \ $(SRCDIR)/pikchrshow.c \ $(SRCDIR)/pivot.c \ @@ -361,10 +362,11 @@ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ + $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/piechart_.c \ $(OBJDIR)/pikchr_.c \ $(OBJDIR)/pikchrshow_.c \ $(OBJDIR)/pivot_.c \ @@ -510,10 +512,11 @@ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ + $(OBJDIR)/patch.o \ $(OBJDIR)/path.o \ $(OBJDIR)/piechart.o \ $(OBJDIR)/pikchr.o \ $(OBJDIR)/pikchrshow.o \ $(OBJDIR)/pivot.o \ @@ -849,10 +852,11 @@ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ + $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \ $(OBJDIR)/pikchr_.c:$(OBJDIR)/pikchr.h \ $(OBJDIR)/pikchrshow_.c:$(OBJDIR)/pikchrshow.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ @@ -1632,10 +1636,18 @@ $(OBJDIR)/name.o: $(OBJDIR)/name_.c $(OBJDIR)/name.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c $(OBJDIR)/name.h: $(OBJDIR)/headers + +$(OBJDIR)/patch_.c: $(SRCDIR)/patch.c $(OBJDIR)/translate + $(OBJDIR)/translate $(SRCDIR)/patch.c >$@ + +$(OBJDIR)/patch.o: $(OBJDIR)/patch_.c $(OBJDIR)/patch.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/patch.o -c $(OBJDIR)/patch_.c + +$(OBJDIR)/patch.h: $(OBJDIR)/headers $(OBJDIR)/path_.c: $(SRCDIR)/path.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/path.c >$@ $(OBJDIR)/path.o: $(OBJDIR)/path_.c $(OBJDIR)/path.h $(SRCDIR)/config.h Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -125,10 +125,11 @@ md5 merge merge3 moderate name + patch path piechart pikchr pikchrshow pivot ADDED src/patch.c Index: src/patch.c ================================================================== --- /dev/null +++ src/patch.c @@ -0,0 +1,741 @@ +/* +** Copyright (c) 2021 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the Simplified BSD License (also +** known as the "2-Clause License" or "FreeBSD License".) +** +** This program is distributed in the hope that it will be useful, +** but without any warranty; without even the implied warranty of +** merchantability or fitness for a particular purpose. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +******************************************************************************* +** +** This file contains code used to implement the "diff" command +*/ +#include "config.h" +#include "patch.h" +#include + +/* +** Additional windows configuration for popen */ +#if defined(_WIN32) +# include +# include +# undef popen +# define popen _popen +# undef pclose +# define pclose _pclose +#endif + +/* +** Flags passed from the main patch_cmd() routine into subfunctions used +** to implement the various subcommands. +*/ +#define PATCH_DRYRUN 0x0001 +#define PATCH_VERBOSE 0x0002 +#define PATCH_FORCE 0x0004 + +/* +** Implementation of the "readfile(X)" SQL function. The entire content +** of the checkout file named X is read and returned as a BLOB. +*/ +static void readfileFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zName; + Blob x; + sqlite3_int64 sz; + (void)(argc); /* Unused parameter */ + zName = (const char*)sqlite3_value_text(argv[0]); + if( zName==0 || (zName[0]=='-' && zName[1]==0) ) return; + sz = blob_read_from_file(&x, zName, RepoFILE); + sqlite3_result_blob64(context, x.aData, sz, SQLITE_TRANSIENT); + blob_reset(&x); +} + +/* +** mkdelta(X,Y) +** +** X is an numeric artifact id. Y is a filename. +** +** Compute a compressed delta that carries X into Y. Or return NULL +** if X is equal to Y. +*/ +static void mkdeltaFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zFile; + Blob x, y; + int rid; + char *aOut; + int nOut; + sqlite3_int64 sz; + + rid = sqlite3_value_int(argv[0]); + if( !content_get(rid, &x) ){ + sqlite3_result_error(context, "mkdelta(X,Y): no content for X", -1); + return; + } + zFile = (const char*)sqlite3_value_text(argv[1]); + if( zFile==0 ){ + sqlite3_result_error(context, "mkdelta(X,Y): NULL Y argument", -1); + blob_reset(&x); + return; + } + sz = blob_read_from_file(&y, zFile, RepoFILE); + if( sz<0 ){ + sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1); + blob_reset(&x); + return; + } + aOut = sqlite3_malloc64(sz+70); + if( aOut==0 ){ + sqlite3_result_error_nomem(context); + blob_reset(&y); + blob_reset(&x); + return; + } + if( blob_size(&x)==blob_size(&y) + && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 + ){ + blob_reset(&y); + blob_reset(&x); + return; + } + nOut = delta_create(blob_buffer(&x),blob_size(&x), + blob_buffer(&y),blob_size(&y), aOut); + blob_reset(&x); + blob_reset(&y); + blob_init(&x, aOut, nOut); + blob_compress(&x, &x); + sqlite3_result_blob64(context, blob_buffer(&x), blob_size(&x), + SQLITE_TRANSIENT); + blob_reset(&x); +} + + +/* +** Generate a binary patch file and store it into the file +** named zOut. +*/ +void patch_create(const char *zOut, FILE *out){ + int vid; + + if( zOut && file_isdir(zOut, ExtFILE)!=0 ){ + fossil_fatal("patch file already exists: %s", zOut); + } + add_content_sql_commands(g.db); + deltafunc_init(g.db); + sqlite3_create_function(g.db, "read_co_file", 1, SQLITE_UTF8, 0, + readfileFunc, 0, 0); + sqlite3_create_function(g.db, "mkdelta", 2, SQLITE_UTF8, 0, + mkdeltaFunc, 0, 0); + db_multi_exec("ATTACH %Q AS patch;", zOut ? zOut : ":memory:"); + db_multi_exec( + "PRAGMA patch.journal_mode=OFF;\n" + "PRAGMA patch.page_size=512;\n" + "CREATE TABLE patch.chng(\n" + " pathname TEXT,\n" /* Filename */ + " origname TEXT,\n" /* Name before rename. NULL if not renamed */ + " hash TEXT,\n" /* Baseline hash. NULL for new files. */ + " isexe BOOL,\n" /* True if executable */ + " islink BOOL,\n" /* True if is a symbolic link */ + " delta BLOB\n" /* Delta. NULL if file deleted or unchanged */ + ");" + "CREATE TABLE patch.cfg(\n" + " key TEXT,\n" + " value ANY\n" + ");" + ); + vid = db_lget_int("checkout", 0); + vfile_check_signature(vid, CKSIG_ENOTFILE); + db_multi_exec( + "INSERT INTO patch.cfg(key,value)" + "SELECT 'baseline',uuid FROM blob WHERE rid=%d", vid); + + /* New files */ + db_multi_exec( + "INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)" + " SELECT pathname, NULL, isexe, islink," + " compress(read_co_file(%Q||pathname))" + " FROM vfile WHERE rid==0;", + g.zLocalRoot + ); + + /* Deleted files */ + db_multi_exec( + "INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)" + " SELECT pathname, NULL, 0, 0, NULL" + " FROM vfile WHERE deleted;" + ); + + /* Changed files */ + db_multi_exec( + "INSERT INTO patch.chng(pathname,origname,hash,isexe,islink,delta)" + " SELECT pathname, nullif(origname,pathname), blob.uuid, isexe, islink," + " mkdelta(blob.rid, %Q||pathname)" + " FROM vfile, blob" + " WHERE blob.rid=vfile.rid" + " AND NOT deleted AND (chnged OR origname<>pathname);", + g.zLocalRoot + ); + + /* Merges */ + if( db_exists("SELECT 1 FROM localdb.vmerge WHERE id<=0") ){ + db_multi_exec( + "CREATE TABLE patch.patchmerge(type TEXT,mhash TEXT);\n" + "WITH tmap(id,type) AS (VALUES(0,'merge'),(-1,'cherrypick')," + "(-2,'backout'),(-4,'integrate'))" + "INSERT INTO patch.patchmerge(type,mhash)" + " SELECT tmap.type,vmerge.mhash FROM vmerge, tmap" + " WHERE tmap.id=vmerge.id;" + ); + } + + /* Write the database to standard output if zOut==0 */ + if( zOut==0 ){ + sqlite3_int64 sz; + unsigned char *pData; + pData = sqlite3_serialize(g.db, "patch", &sz, 0); + if( pData==0 ){ + fossil_fatal("out of memory"); + } +#ifdef _WIN32 + fflush(out); + _setmode(_fileno(out), _O_BINARY); +#endif + fwrite(pData, sz, 1, out); + sqlite3_free(pData); + fflush(out); + } +} + +/* +** Attempt to load and validate a patchfile identified by the first +** argument. +*/ +void patch_attach(const char *zIn, FILE *in){ + Stmt q; + if( g.db==0 ){ + sqlite3_open(":memory:", &g.db); + } + if( zIn==0 ){ + Blob buf; + int rc; + int sz; + unsigned char *pData; + blob_init(&buf, 0, 0); +#ifdef _WIN32 + _setmode(_fileno(in), _O_BINARY); +#endif + sz = blob_read_from_channel(&buf, in, -1); + pData = (unsigned char*)blob_buffer(&buf); + db_multi_exec("ATTACH ':memory:' AS patch"); + if( g.fSqlTrace ){ + fossil_trace("-- deserialize(\"patch\", pData, %lld);\n", sz); + } + rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0); + if( rc ){ + fossil_fatal("cannot open patch database: %s", sqlite3_errmsg(g.db)); + } + }else if( !file_isfile(zIn, ExtFILE) ){ + fossil_fatal("no such file: %s", zIn); + }else{ + db_multi_exec("ATTACH %Q AS patch", zIn); + } + db_prepare(&q, "PRAGMA patch.quick_check"); + while( db_step(&q)==SQLITE_ROW ){ + if( fossil_strcmp(db_column_text(&q,0),"ok")!=0 ){ + fossil_fatal("file %s is not a well-formed Fossil patchfile", zIn); + } + } + db_finalize(&q); +} + +/* +** Show a summary of the content of a patch on standard output +*/ +void patch_view(void){ + Stmt q; + db_prepare(&q, "SELECT value FROM patch.cfg WHERE key='baseline'"); + if( db_step(&q)==SQLITE_ROW ){ + fossil_print("%-10s %s\n", "BASELINE", db_column_text(&q,0)); + }else{ + fossil_fatal("ERROR: Missing patch baseline"); + } + db_finalize(&q); + if( db_table_exists("patch","patchmerge") ){ + db_prepare(&q, "SELECT upper(type),mhash FROM patchmerge"); + while( db_step(&q)==SQLITE_ROW ){ + fossil_print("%-10s %s\n", + db_column_text(&q,0), + db_column_text(&q,1)); + } + db_finalize(&q); + } + db_prepare(&q, + "SELECT pathname," + " hash IS NULL AND delta IS NOT NULL," /* isNew */ + " delta IS NULL," /* delete if origname NULL */ + " origname" + " FROM patch.chng ORDER BY 1"); + while( db_step(&q)==SQLITE_ROW ){ + const char *zClass = "EDIT"; + const char *zName = db_column_text(&q,0); + const char *zOrigName = db_column_text(&q, 3); + if( db_column_int(&q, 1) && zOrigName==0 ){ + zClass = "NEW"; + }else if( db_column_int(&q, 2) ){ + zClass = zOrigName==0 ? "DELETE" : 0; + } + if( zOrigName!=0 && zOrigName[0]!=0 ){ + fossil_print("%-10s %s -> %s\n", "RENAME",zOrigName,zName); + } + if( zClass ){ + fossil_print("%-10s %s\n", zClass, zName); + } + } + db_finalize(&q); +} + +/* +** Apply the patch currently attached as database "patch". +** +** First update the check-out to be at "baseline". Then loop through +** and update all files. +*/ +void patch_apply(unsigned mFlags){ + Stmt q; + Blob cmd; + + if( (mFlags & PATCH_FORCE)==0 && unsaved_changes(0) ){ + fossil_fatal("there are unsaved changes in the current checkout"); + } + blob_init(&cmd, 0, 0); + file_chdir(g.zLocalRoot, 0); + db_prepare(&q, + "SELECT patch.cfg.value" + " FROM patch.cfg, localdb.vvar" + " WHERE patch.cfg.key='baseline'" + " AND localdb.vvar.name='checkout-hash'" + " AND patch.cfg.key<>localdb.vvar.name" + ); + if( db_step(&q)==SQLITE_ROW ){ + blob_append_escaped_arg(&cmd, g.nameOfExe); + blob_appendf(&cmd, " update %s", db_column_text(&q, 0)); + if( mFlags & PATCH_VERBOSE ){ + fossil_print("%-10s %s\n", "BASELINE", db_column_text(&q,0)); + } + } + db_finalize(&q); + if( blob_size(&cmd)>0 ){ + if( mFlags & PATCH_DRYRUN ){ + fossil_print("%s\n", blob_str(&cmd)); + }else{ + int rc = fossil_system(blob_str(&cmd)); + if( rc ){ + fossil_fatal("unable to update to the baseline check-out: %s", + blob_str(&cmd)); + } + } + } + blob_reset(&cmd); + if( db_table_exists("patch","patchmerge") ){ + db_prepare(&q, + "SELECT type, mhash, upper(type) FROM patch.patchmerge" + " WHERE type IN ('merge','cherrypick','backout','integrate')" + " AND mhash NOT GLOB '*[^a-fA-F0-9]*';" + ); + while( db_step(&q)==SQLITE_ROW ){ + const char *zType = db_column_text(&q,0); + blob_append_escaped_arg(&cmd, g.nameOfExe); + if( strcmp(zType,"merge")==0 ){ + blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1)); + }else{ + blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1)); + } + if( mFlags & PATCH_VERBOSE ){ + fossil_print("%-10s %s\n", db_column_text(&q,2), + db_column_text(&q,0)); + } + } + db_finalize(&q); + if( mFlags & PATCH_DRYRUN ){ + fossil_print("%s", blob_str(&cmd)); + }else{ + int rc = fossil_unsafe_system(blob_str(&cmd)); + if( rc ){ + fossil_fatal("unable to do merges:\n%s", + blob_str(&cmd)); + } + } + blob_reset(&cmd); + } + + /* Deletions */ + db_prepare(&q, "SELECT pathname FROM patch.chng" + " WHERE origname IS NULL AND delta IS NULL"); + while( db_step(&q)==SQLITE_ROW ){ + blob_append_escaped_arg(&cmd, g.nameOfExe); + blob_appendf(&cmd, " rm --hard %$\n", db_column_text(&q,0)); + if( mFlags & PATCH_VERBOSE ){ + fossil_print("%-10s %s\n", "DELETE", db_column_text(&q,0)); + } + } + db_finalize(&q); + if( blob_size(&cmd)>0 ){ + if( mFlags & PATCH_DRYRUN ){ + fossil_print("%s", blob_str(&cmd)); + }else{ + int rc = fossil_unsafe_system(blob_str(&cmd)); + if( rc ){ + fossil_fatal("unable to do merges:\n%s", + blob_str(&cmd)); + } + } + blob_reset(&cmd); + } + + /* Renames */ + db_prepare(&q, + "SELECT origname, pathname FROM patch.chng" + " WHERE origname IS NOT NULL" + " AND origname<>pathname" + ); + while( db_step(&q)==SQLITE_ROW ){ + blob_append_escaped_arg(&cmd, g.nameOfExe); + blob_appendf(&cmd, " mv --hard %$ %$\n", + db_column_text(&q,0), db_column_text(&q,1)); + if( mFlags & PATCH_VERBOSE ){ + fossil_print("%-10s %s -> %s\n", "RENAME", + db_column_text(&q,0), db_column_text(&q,1)); + } + } + db_finalize(&q); + if( blob_size(&cmd)>0 ){ + if( mFlags & PATCH_DRYRUN ){ + fossil_print("%s", blob_str(&cmd)); + }else{ + int rc = fossil_unsafe_system(blob_str(&cmd)); + if( rc ){ + fossil_fatal("unable to rename files:\n%s", + blob_str(&cmd)); + } + } + blob_reset(&cmd); + } + + /* Edits and new files */ + db_prepare(&q, + "SELECT pathname, hash, isexe, islink, delta FROM patch.chng" + " WHERE delta IS NOT NULL" + ); + while( db_step(&q)==SQLITE_ROW ){ + const char *zPathname = db_column_text(&q,0); + const char *zHash = db_column_text(&q,1); + int isExe = db_column_int(&q,2); + int isLink = db_column_int(&q,3); + Blob data; + + blob_init(&data, 0, 0); + db_column_blob(&q, 4, &data); + blob_uncompress(&data, &data); + if( zHash ){ + Blob basis; + int rid = fast_uuid_to_rid(zHash); + int outSize, sz; + char *aOut; + if( rid==0 ){ + fossil_fatal("cannot locate basis artifact %s for %s", + zHash, zPathname); + } + if( !content_get(rid, &basis) ){ + fossil_fatal("cannot load basis artifact %d for %s", rid, zPathname); + } + outSize = delta_output_size(blob_buffer(&data),blob_size(&data)); + if( outSize<=0 ){ + fossil_fatal("malformed delta for %s", zPathname); + } + aOut = sqlite3_malloc64( outSize+1 ); + if( aOut==0 ){ + fossil_fatal("out of memory"); + } + sz = delta_apply(blob_buffer(&basis), blob_size(&basis), + blob_buffer(&data), blob_size(&data), aOut); + if( sz<0 ){ + fossil_fatal("malformed delta for %s", zPathname); + } + blob_reset(&basis); + blob_reset(&data); + blob_append(&data, aOut, sz); + sqlite3_free(aOut); + if( mFlags & PATCH_VERBOSE ){ + fossil_print("%-10s %s\n", "EDIT", zPathname); + } + }else{ + blob_append_escaped_arg(&cmd, g.nameOfExe); + blob_appendf(&cmd, " add %$\n", zPathname); + if( mFlags & PATCH_VERBOSE ){ + fossil_print("%-10s %s\n", "NEW", zPathname); + } + } + if( (mFlags & PATCH_DRYRUN)==0 ){ + if( isLink ){ + symlink_create(blob_str(&data), zPathname); + }else{ + blob_write_to_file(&data, zPathname); + } + file_setexe(zPathname, isExe); + blob_reset(&data); + } + } + db_finalize(&q); + if( blob_size(&cmd)>0 ){ + if( mFlags & PATCH_DRYRUN ){ + fossil_print("%s", blob_str(&cmd)); + }else{ + int rc = fossil_system(blob_str(&cmd)); + if( rc ){ + fossil_fatal("unable to add new files:\n%s", + blob_str(&cmd)); + } + } + blob_reset(&cmd); + } +} + +/* +** Find the filename of the patch file to be used by +** "fossil patch apply" or "fossil patch create". +** +** If the name is "-" return NULL. +** +** Otherwise, if there is a prior DIRECTORY argument, or if +** the --dir64 option is present, first chdir to the specified +** directory, and translate the name in the argument accordingly. +** +** +** The returned name is obtained from fossil_malloc() and should +** be freed by the caller. +*/ +static char *patch_find_patch_filename(const char *zCmdName){ + const char *zDir64 = find_option("dir64",0,1); + const char *zDir = 0; + const char *zBaseName; + char *zToFree = 0; + char *zPatchFile = 0; + if( zDir64 ){ + int n = 0; + zToFree = decode64(zDir64, &n); + zDir = zToFree; + } + verify_all_options(); + if( g.argc!=4 && g.argc!=5 ){ + usage(mprintf("%s [DIRECTORY] FILENAME", zCmdName)); + } + if( g.argc==5 ){ + zDir = g.argv[3]; + zBaseName = g.argv[4]; + }else{ + zBaseName = g.argv[3]; + } + if( fossil_strcmp(zBaseName, "-")==0 ){ + zPatchFile = 0; + }else if( zDir ){ + zPatchFile = file_canonical_name_dup(g.argv[4]); + }else{ + zPatchFile = fossil_strdup(g.argv[3]); + } + if( zDir && file_chdir(zDir,0) ){ + fossil_fatal("cannot change to directory \"%s\"", zDir); + } + fossil_free(zToFree); + return zPatchFile; +} + +/* +** Create a FILE* that will execute the remote side of a push or pull +** using ssh (probably) or fossil for local pushes and pulls. Return +*/ +static FILE *patch_remote_command( + unsigned mFlags, /* flags */ + const char *zThisCmd, /* "push" or "pull" */ + const char *zRemoteCmd, /* "apply" or "create" */ + const char *zRW /* "w" or "r" */ +){ + char *zRemote; + char *zDir; + Blob cmd; + FILE *f; + const char *zForce = (mFlags & PATCH_FORCE)!=0 ? " -f" : ""; + if( g.argc!=4 ){ + usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd)); + } + zRemote = fossil_strdup(g.argv[3]); + zDir = strchr(zRemote,':'); + if( zDir==0 ){ + zDir = zRemote; + blob_init(&cmd, 0, 0); + blob_append_escaped_arg(&cmd, g.nameOfExe); + blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir); + }else{ + Blob remote; + zDir[0] = 0; + zDir++; + transport_ssh_command(&cmd); + blob_append_escaped_arg(&cmd, zRemote); + blob_init(&remote, 0, 0); + blob_appendf(&remote, "fossil patch %s%s --dir64 %z -", + zRemoteCmd, zForce, encode64(zDir, -1)); + blob_append_escaped_arg(&cmd, blob_str(&remote)); + blob_reset(&remote); + } + if( mFlags & PATCH_VERBOSE ){ + fossil_print("# %s\n", blob_str(&cmd)); + fflush(stdout); + } + f = popen(blob_str(&cmd), zRW); + if( f==0 ){ + fossil_fatal("cannot run command: %s", blob_str(&cmd)); + } + blob_reset(&cmd); + return f; +} + + +/* +** COMMAND: patch +** +** Usage: %fossil patch SUBCOMMAND ?ARGS ..? +** +** This command is used to creates, view, and apply Fossil binary patches. +** A Fossil binary patch is a single (binary) file that captures all of the +** uncommitted changes of a check-out. Use Fossil binary patches to transfer +** proposed or incomplete changes between machines for testing or analysis. +** +** > fossil patch create [DIRECTORY] FILENAME +** +** Create a new binary patch in FILENAME that captures all uncommitted +** changes in the check-out at DIRECTORY, or the current directory if +** DIRECTORY is omitted. If FILENAME is "-" then the binary patch +** is written to standard output. +** +** > fossil patch apply [DIRECTORY] FILENAME +** +** Apply the changes in FILENAME to the check-out a DIRECTORY, or +** in the current directory if DIRECTORY is omitted. Options: +** +** -f|--force Apply the patch even though there are unsaved +** changes in the current check-out. +** -n|--dryrun Do nothing, but print what would have happened. +** -v|--verbose Extra output explaining what happens. +** +** > fossil patch push REMOTE-CHECKOUT +** +** Create a patch for the current check-out, transfer that patch to +** a remote machine (using ssh) and apply the patch there. The +** REMOTE-CHECKOUT is in one of the following formats: +** +** * DIRECTORY +** * HOST:DIRECTORY +** * USER@HOST:DIRECTORY +** +** This command will only work if "fossil" is on the default PATH +** of the remote machine. +** +** > fossil patch pull REMOTE-CHECKOUT +** +** Create a patch on a remote check-out, transfer that patch to the +** local machine (using ssh) and apply the patch in the local checkout. +** +** -f|--force Apply the patch even though there are unsaved +** changes in the current check-out. +** -n|--dryrun Do nothing, but print what would have happened. +** -v|--verbose Extra output explaining what happens. +** +** > fossil patch view FILENAME +** +** View a summary of the the changes in the binary patch FILENAME. +** +*/ +void patch_cmd(void){ + const char *zCmd; + size_t n; + if( g.argc<3 ){ + patch_usage: + usage("apply|create|pull|push|view"); + } + zCmd = g.argv[2]; + n = strlen(zCmd); + if( strncmp(zCmd, "apply", n)==0 ){ + char *zIn; + unsigned flags = 0; + if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN; + if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; + if( find_option("force","f",0) ) flags |= PATCH_FORCE; + zIn = patch_find_patch_filename("apply"); + db_must_be_within_tree(); + patch_attach(zIn, stdin); + patch_apply(flags); + fossil_free(zIn); + }else + if( strncmp(zCmd, "create", n)==0 ){ + char *zOut; + zOut = patch_find_patch_filename("create"); + db_must_be_within_tree(); + patch_create(zOut, stdout); + fossil_free(zOut); + }else + if( strncmp(zCmd, "pull", n)==0 ){ + FILE *pIn = 0; + unsigned flags = 0; + if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN; + if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; + if( find_option("force","f",0) ) flags |= PATCH_FORCE; + db_must_be_within_tree(); + verify_all_options(); + pIn = patch_remote_command(flags & (~PATCH_FORCE), "pull", "create", "r"); + if( pIn ){ + patch_attach(0, pIn); + pclose(pIn); + patch_apply(flags); + } + }else + if( strncmp(zCmd, "push", n)==0 ){ + FILE *pOut = 0; + unsigned flags = 0; + if( find_option("dryrun","n",0) ) flags |= PATCH_DRYRUN; + if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; + if( find_option("force","f",0) ) flags |= PATCH_FORCE; + db_must_be_within_tree(); + verify_all_options(); + pOut = patch_remote_command(flags, "push", "apply", "w"); + if( pOut ){ + patch_create(0, pOut); + pclose(pOut); + } + }else + if( strncmp(zCmd, "view", n)==0 ){ + const char *zIn; + verify_all_options(); + if( g.argc!=4 ){ + usage("view FILENAME"); + } + zIn = g.argv[3]; + if( fossil_strcmp(zIn, "-")==0 ) zIn = 0; + patch_attach(zIn, stdin); + patch_view(); + }else + { + goto patch_usage; + } +} Index: win/Makefile.dmc ================================================================== --- win/Makefile.dmc +++ win/Makefile.dmc @@ -28,13 +28,13 @@ SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen -SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c +SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c -OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O +OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ @@ -49,11 +49,11 @@ $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res - +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ + +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name patch path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ @@ -661,10 +661,16 @@ $(OBJDIR)\name$O : name_.c name.h $(TCC) -o$@ -c name_.c name_.c : $(SRCDIR)\name.c +translate$E $** > $@ + +$(OBJDIR)\patch$O : patch_.c patch.h + $(TCC) -o$@ -c patch_.c + +patch_.c : $(SRCDIR)\patch.c + +translate$E $** > $@ $(OBJDIR)\path$O : path_.c path.h $(TCC) -o$@ -c path_.c path_.c : $(SRCDIR)\path.c @@ -1005,7 +1011,7 @@ zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h - +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h + +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers Index: win/Makefile.mingw ================================================================== --- win/Makefile.mingw +++ win/Makefile.mingw @@ -513,10 +513,11 @@ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ + $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ $(SRCDIR)/piechart.c \ $(SRCDIR)/pikchr.c \ $(SRCDIR)/pikchrshow.c \ $(SRCDIR)/pivot.c \ @@ -770,10 +771,11 @@ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ + $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/piechart_.c \ $(OBJDIR)/pikchr_.c \ $(OBJDIR)/pikchrshow_.c \ $(OBJDIR)/pivot_.c \ @@ -919,10 +921,11 @@ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ + $(OBJDIR)/patch.o \ $(OBJDIR)/path.o \ $(OBJDIR)/piechart.o \ $(OBJDIR)/pikchr.o \ $(OBJDIR)/pikchrshow.o \ $(OBJDIR)/pivot.o \ @@ -1283,10 +1286,11 @@ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ + $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \ $(OBJDIR)/pikchr_.c:$(OBJDIR)/pikchr.h \ $(OBJDIR)/pikchrshow_.c:$(OBJDIR)/pikchrshow.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ @@ -2068,10 +2072,18 @@ $(OBJDIR)/name.o: $(OBJDIR)/name_.c $(OBJDIR)/name.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c $(OBJDIR)/name.h: $(OBJDIR)/headers + +$(OBJDIR)/patch_.c: $(SRCDIR)/patch.c $(TRANSLATE) + $(TRANSLATE) $(SRCDIR)/patch.c >$@ + +$(OBJDIR)/patch.o: $(OBJDIR)/patch_.c $(OBJDIR)/patch.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/patch.o -c $(OBJDIR)/patch_.c + +$(OBJDIR)/patch.h: $(OBJDIR)/headers $(OBJDIR)/path_.c: $(SRCDIR)/path.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/path.c >$@ $(OBJDIR)/path.o: $(OBJDIR)/path_.c $(OBJDIR)/path.h $(SRCDIR)/config.h Index: win/Makefile.msc ================================================================== --- win/Makefile.msc +++ win/Makefile.msc @@ -456,10 +456,11 @@ "$(OX)\md5_.c" \ "$(OX)\merge_.c" \ "$(OX)\merge3_.c" \ "$(OX)\moderate_.c" \ "$(OX)\name_.c" \ + "$(OX)\patch_.c" \ "$(OX)\path_.c" \ "$(OX)\piechart_.c" \ "$(OX)\pikchr_.c" \ "$(OX)\pikchrshow_.c" \ "$(OX)\pivot_.c" \ @@ -712,10 +713,11 @@ "$(OX)\md5$O" \ "$(OX)\merge$O" \ "$(OX)\merge3$O" \ "$(OX)\moderate$O" \ "$(OX)\name$O" \ + "$(OX)\patch$O" \ "$(OX)\path$O" \ "$(OX)\piechart$O" \ "$(OX)\pikchr$O" \ "$(OX)\pikchrshow$O" \ "$(OX)\pivot$O" \ @@ -942,10 +944,11 @@ echo "$(OX)\md5.obj" >> $@ echo "$(OX)\merge.obj" >> $@ echo "$(OX)\merge3.obj" >> $@ echo "$(OX)\moderate.obj" >> $@ echo "$(OX)\name.obj" >> $@ + echo "$(OX)\patch.obj" >> $@ echo "$(OX)\path.obj" >> $@ echo "$(OX)\piechart.obj" >> $@ echo "$(OX)\pikchr.obj" >> $@ echo "$(OX)\pikchrshow.obj" >> $@ echo "$(OX)\pivot.obj" >> $@ @@ -1766,10 +1769,16 @@ "$(OX)\name$O" : "$(OX)\name_.c" "$(OX)\name.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\name_.c" "$(OX)\name_.c" : "$(SRCDIR)\name.c" "$(OBJDIR)\translate$E" $** > $@ + +"$(OX)\patch$O" : "$(OX)\patch_.c" "$(OX)\patch.h" + $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\patch_.c" + +"$(OX)\patch_.c" : "$(SRCDIR)\patch.c" + "$(OBJDIR)\translate$E" $** > $@ "$(OX)\path$O" : "$(OX)\path_.c" "$(OX)\path.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\path_.c" "$(OX)\path_.c" : "$(SRCDIR)\path.c" @@ -2203,10 +2212,11 @@ "$(OX)\md5_.c":"$(OX)\md5.h" \ "$(OX)\merge_.c":"$(OX)\merge.h" \ "$(OX)\merge3_.c":"$(OX)\merge3.h" \ "$(OX)\moderate_.c":"$(OX)\moderate.h" \ "$(OX)\name_.c":"$(OX)\name.h" \ + "$(OX)\patch_.c":"$(OX)\patch.h" \ "$(OX)\path_.c":"$(OX)\path.h" \ "$(OX)\piechart_.c":"$(OX)\piechart.h" \ "$(OX)\pikchr_.c":"$(OX)\pikchr.h" \ "$(OX)\pikchrshow_.c":"$(OX)\pikchrshow.h" \ "$(OX)\pivot_.c":"$(OX)\pivot.h" \ Index: www/changes.wiki ================================================================== --- www/changes.wiki +++ www/changes.wiki @@ -3,10 +3,11 @@

Changes for Version 2.16 (pending)

* Security: Fix the client-side TLS so that it verifies that the server hostname matches its certificate. Upgrading to the patch is recommended. + * Added the [./patchcmd.md|fossil patch] command. * The [/brlist|/brlist web page] allows the user to select multiple branches to be displayed together in a single timeline. * The [./forum.wiki|Forum] provides a hyperlink on the author of each post that goes to a timeline of recent posts by that same author. Index: www/mkindex.tcl ================================================================== --- www/mkindex.tcl +++ www/mkindex.tcl @@ -83,10 +83,11 @@ makefile.wiki {The Fossil Build Process} mirrorlimitations.md {Limitations On Git Mirrors} mirrortogithub.md {How To Mirror A Fossil Repository On GitHub} /md_rules {Markdown Formatting Rules} newrepo.wiki {How To Create A New Fossil Repository} + patchcmd.md {The "fossil patch" Command} password.wiki {Password Management And Authentication} pikchr.md {The Pikchr Diagram Language} pop.wiki {Principles Of Operation} private.wiki {Creating, Syncing, and Deleting Private Branches} qandc.wiki {Questions And Criticisms} ADDED www/patchcmd.md Index: www/patchcmd.md ================================================================== --- /dev/null +++ www/patchcmd.md @@ -0,0 +1,74 @@ +# The "fossil patch" command + +The "[fossil patch](/help?cmd=patch)" command is designed to transfer +uncommitted changes from one check-out to another, including transfering +those changes to other machines. + +For example, if you are working on a Windows desktop and you want to +test your changes on a Linux server before you commit, you can use the +"fossil patch push" command to make a copy of all your changes on the +remote Linux server, where they can be tested. + +> fossil patch push linuxserver:/path/to/checkout + +In the previous "linuxserver" is the name of the remote machine and +"/path/to/checkout" is an existing checkout directory for the same project +on the remote machine. + +The "fossil patch push" command works by first creating a patch file, +then transfering that patch file to the remote machine using "ssh", then +applying the patch. If you do not have ssh available, you can break these +steps apart as follows: + + 1. On the local machine: `fossil patch create mypatch.patch` + 2. Move "mypatch.patch" to the remote machine. + 3. On the remote machine: `fossil patch apply mypatch.patch` + +## Setup + +The "fossil push" and "fossil pull" commands will only work if you have +"ssh" available on the local machine and if "fossil" is on the default +PATH on the remote machine. + +To check if Fossil is installed correctly on the remote, try a command +like this: + +> `ssh -T remote "fossil version"` + +If the command above shows a recent version of Fossil, then you should be +set to go. If you get "fossil not found", or if the version shown is too +old, put a newer fossil executable on the default PATH. The default PATH +can be shown using: + +> `ssh -T remote 'echo $PATH'` + +## Implementation Details + +The "fossil patch create" command records all of the local, uncommitted +changes in an SQLite database file. If the argument to "fossil patch create" +is a filename, then the patch-file database is written into that file. +If the argument is "-" then the database is written on standard output. + +The "fossil patch apply" command reads the database that is the patch file +and applies it to the local check-out. If a filename is given as an +argument, then the database is read from that file. If the argument is "-" +then the database is read from standard input. + +Hence the command: + +> `fossil patch push remote:projectA` + +Is equivalent to: + +> `fossil patch create - | ssh -T remote 'cd projectA;fossil patch apply -'` + +Likewise, a command like this: + +> `fossil patch pull remote:projB + +Could be entered like this: + +> `ssh -T remote 'cd projB;fossil patch create -' | fossil patch apply -` + +The "fossil patch view" command just opens the database file and prints +a summary of its contents on standard output. Index: www/permutedindex.html ================================================================== --- www/permutedindex.html +++ www/permutedindex.html @@ -106,10 +106,11 @@
  • Shunning: Deleting Content From Fossil
  • Site Map
  • Source Code Style Guidelines
  • SQLite Databases Used By Fossil
  • The "Backoffice" mechanism of Fossil
  • +
  • The "fossil patch" Command
  • The Annotate/Blame Algorithm Of Fossil
  • The Default Content Security Policy
  • The fileedit Page
  • The Fossil Build Process
  • The Fossil Sync Protocol