Fossil SCM
Code to generate a patchfile.
Commit
7333115dc28b51c52b52704ad4000b662f1b7df5e388644ab3a93abae2ac9abb
Parent
343745fd36ae6aa…
1 file changed
+86
-1
+86
-1
| --- src/patch.c | ||
| +++ src/patch.c | ||
| @@ -19,10 +19,95 @@ | ||
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "patch.h" |
| 22 | 22 | #include <assert.h> |
| 23 | 23 | |
| 24 | +/* | |
| 25 | +** Implementation of the "readfile(X)" SQL function. The entire content | |
| 26 | +** of the checkout file named X is read and returned as a BLOB. | |
| 27 | +*/ | |
| 28 | +static void readfileFunc( | |
| 29 | + sqlite3_context *context, | |
| 30 | + int argc, | |
| 31 | + sqlite3_value **argv | |
| 32 | +){ | |
| 33 | + const char *zName; | |
| 34 | + Blob x; | |
| 35 | + sqlite3_int64 sz; | |
| 36 | + (void)(argc); /* Unused parameter */ | |
| 37 | + zName = (const char*)sqlite3_value_text(argv[0]); | |
| 38 | + if( zName==0 || (zName[0]=='-' && zName[1]==0) ) return; | |
| 39 | + sz = blob_read_from_file(&x, zName, RepoFILE); | |
| 40 | + sqlite3_result_blob64(context, x.aData, sz, SQLITE_TRANSIENT); | |
| 41 | + blob_reset(&x); | |
| 42 | +} | |
| 43 | + | |
| 44 | + | |
| 45 | +/* | |
| 46 | +** Generate a binary patch file and store it into the file | |
| 47 | +** named zOut. | |
| 48 | +*/ | |
| 49 | +void patch_create(const char *zOut){ | |
| 50 | + int vid; | |
| 51 | + if( file_isdir(zOut, ExtFILE)!=0 ){ | |
| 52 | + fossil_fatal("patch file already exists: %s", zOut); | |
| 53 | + } | |
| 54 | + add_content_sql_commands(g.db); | |
| 55 | + deltafunc_init(g.db); | |
| 56 | + sqlite3_create_function(g.db, "read_co_file", 1, SQLITE_UTF8, 0, | |
| 57 | + readfileFunc, 0, 0); | |
| 58 | + db_multi_exec("ATTACH %Q AS patch;", zOut); | |
| 59 | + db_multi_exec( | |
| 60 | + "PRAGMA patch.journal_mode=OFF;\n" | |
| 61 | + "PRAGMA patch.page_size=512;\n" | |
| 62 | + "CREATE TABLE patch.chng(\n" | |
| 63 | + " fname TEXT,\n" /* Filename */ | |
| 64 | + " hash TEXT,\n" /* Baseline hash. NULL for new files. */ | |
| 65 | + " isexe BOOL,\n" /* True if executable */ | |
| 66 | + " islink BOOL,\n" /* True if is a symbolic link */ | |
| 67 | + " delta BLOB\n" /* Delta. NULL if file deleted */ | |
| 68 | + ");" | |
| 69 | + "CREATE TABLE patch.cfg(\n" | |
| 70 | + " key TEXT,\n" | |
| 71 | + " value ANY\n" | |
| 72 | + ");" | |
| 73 | + ); | |
| 74 | + vid = db_lget_int("checkout", 0); | |
| 75 | + vfile_check_signature(vid, CKSIG_ENOTFILE); | |
| 76 | + db_multi_exec( | |
| 77 | + "INSERT INTO patch.cfg(key,value)" | |
| 78 | + "SELECT 'baseline',uuid FROM blob WHERE rid=%d", vid); | |
| 79 | + if( db_exists("SELECT 1 FROM vmerge") ){ | |
| 80 | + db_multi_exec("INSERT INTO patch.cfg(key,value)VALUES('merged',1);"); | |
| 81 | + } | |
| 82 | + | |
| 83 | + /* New files */ | |
| 84 | + db_multi_exec( | |
| 85 | + "INSERT INTO patch.chng(fname,hash,isexe,islink,delta)" | |
| 86 | + " SELECT pathname, NULL, isexe, islink," | |
| 87 | + " compress(read_co_file(%Q||pathname))" | |
| 88 | + " FROM vfile WHERE rid==0;", | |
| 89 | + g.zLocalRoot | |
| 90 | + ); | |
| 91 | + /* Deleted files */ | |
| 92 | + db_multi_exec( | |
| 93 | + "INSERT INTO patch.chng(fname,hash,isexe,islink,delta)" | |
| 94 | + " SELECT pathname, NULL, 0, 0, NULL" | |
| 95 | + " FROM vfile WHERE deleted;" | |
| 96 | + ); | |
| 97 | + /* Changed files */ | |
| 98 | + db_multi_exec( | |
| 99 | + "INSERT INTO patch.chng(fname,hash,isexe,islink,delta)" | |
| 100 | + " SELECT pathname, blob.uuid, isexe, islink," | |
| 101 | + " compress(delta_create(content(blob.uuid)," | |
| 102 | + "read_co_file(%Q||pathname)))" | |
| 103 | + " FROM vfile, blob" | |
| 104 | + " WHERE blob.rid=vfile.rid AND NOT deleted AND chnged;", | |
| 105 | + g.zLocalRoot | |
| 106 | + ); | |
| 107 | +} | |
| 108 | + | |
| 24 | 109 | |
| 25 | 110 | /* |
| 26 | 111 | ** COMMAND: patch |
| 27 | 112 | ** |
| 28 | 113 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| @@ -69,18 +154,18 @@ | ||
| 69 | 154 | db_must_be_within_tree(); |
| 70 | 155 | verify_all_options(); |
| 71 | 156 | if( g.argc!=4 ){ |
| 72 | 157 | usage("apply FILENAME"); |
| 73 | 158 | } |
| 74 | - fossil_print("TBD...\n"); | |
| 75 | 159 | }else |
| 76 | 160 | if( strncmp(zCmd, "create", n)==0 ){ |
| 77 | 161 | db_must_be_within_tree(); |
| 78 | 162 | verify_all_options(); |
| 79 | 163 | if( g.argc!=4 ){ |
| 80 | 164 | usage("create FILENAME"); |
| 81 | 165 | } |
| 166 | + patch_create(g.argv[3]); | |
| 82 | 167 | }else |
| 83 | 168 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 84 | 169 | db_must_be_within_tree(); |
| 85 | 170 | verify_all_options(); |
| 86 | 171 | if( g.argc!=4 ){ |
| 87 | 172 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -19,10 +19,95 @@ | |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "patch.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | |
| 25 | /* |
| 26 | ** COMMAND: patch |
| 27 | ** |
| 28 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| @@ -69,18 +154,18 @@ | |
| 69 | db_must_be_within_tree(); |
| 70 | verify_all_options(); |
| 71 | if( g.argc!=4 ){ |
| 72 | usage("apply FILENAME"); |
| 73 | } |
| 74 | fossil_print("TBD...\n"); |
| 75 | }else |
| 76 | if( strncmp(zCmd, "create", n)==0 ){ |
| 77 | db_must_be_within_tree(); |
| 78 | verify_all_options(); |
| 79 | if( g.argc!=4 ){ |
| 80 | usage("create FILENAME"); |
| 81 | } |
| 82 | }else |
| 83 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 84 | db_must_be_within_tree(); |
| 85 | verify_all_options(); |
| 86 | if( g.argc!=4 ){ |
| 87 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -19,10 +19,95 @@ | |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "patch.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** Implementation of the "readfile(X)" SQL function. The entire content |
| 26 | ** of the checkout file named X is read and returned as a BLOB. |
| 27 | */ |
| 28 | static void readfileFunc( |
| 29 | sqlite3_context *context, |
| 30 | int argc, |
| 31 | sqlite3_value **argv |
| 32 | ){ |
| 33 | const char *zName; |
| 34 | Blob x; |
| 35 | sqlite3_int64 sz; |
| 36 | (void)(argc); /* Unused parameter */ |
| 37 | zName = (const char*)sqlite3_value_text(argv[0]); |
| 38 | if( zName==0 || (zName[0]=='-' && zName[1]==0) ) return; |
| 39 | sz = blob_read_from_file(&x, zName, RepoFILE); |
| 40 | sqlite3_result_blob64(context, x.aData, sz, SQLITE_TRANSIENT); |
| 41 | blob_reset(&x); |
| 42 | } |
| 43 | |
| 44 | |
| 45 | /* |
| 46 | ** Generate a binary patch file and store it into the file |
| 47 | ** named zOut. |
| 48 | */ |
| 49 | void patch_create(const char *zOut){ |
| 50 | int vid; |
| 51 | if( file_isdir(zOut, ExtFILE)!=0 ){ |
| 52 | fossil_fatal("patch file already exists: %s", zOut); |
| 53 | } |
| 54 | add_content_sql_commands(g.db); |
| 55 | deltafunc_init(g.db); |
| 56 | sqlite3_create_function(g.db, "read_co_file", 1, SQLITE_UTF8, 0, |
| 57 | readfileFunc, 0, 0); |
| 58 | db_multi_exec("ATTACH %Q AS patch;", zOut); |
| 59 | db_multi_exec( |
| 60 | "PRAGMA patch.journal_mode=OFF;\n" |
| 61 | "PRAGMA patch.page_size=512;\n" |
| 62 | "CREATE TABLE patch.chng(\n" |
| 63 | " fname TEXT,\n" /* Filename */ |
| 64 | " hash TEXT,\n" /* Baseline hash. NULL for new files. */ |
| 65 | " isexe BOOL,\n" /* True if executable */ |
| 66 | " islink BOOL,\n" /* True if is a symbolic link */ |
| 67 | " delta BLOB\n" /* Delta. NULL if file deleted */ |
| 68 | ");" |
| 69 | "CREATE TABLE patch.cfg(\n" |
| 70 | " key TEXT,\n" |
| 71 | " value ANY\n" |
| 72 | ");" |
| 73 | ); |
| 74 | vid = db_lget_int("checkout", 0); |
| 75 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 76 | db_multi_exec( |
| 77 | "INSERT INTO patch.cfg(key,value)" |
| 78 | "SELECT 'baseline',uuid FROM blob WHERE rid=%d", vid); |
| 79 | if( db_exists("SELECT 1 FROM vmerge") ){ |
| 80 | db_multi_exec("INSERT INTO patch.cfg(key,value)VALUES('merged',1);"); |
| 81 | } |
| 82 | |
| 83 | /* New files */ |
| 84 | db_multi_exec( |
| 85 | "INSERT INTO patch.chng(fname,hash,isexe,islink,delta)" |
| 86 | " SELECT pathname, NULL, isexe, islink," |
| 87 | " compress(read_co_file(%Q||pathname))" |
| 88 | " FROM vfile WHERE rid==0;", |
| 89 | g.zLocalRoot |
| 90 | ); |
| 91 | /* Deleted files */ |
| 92 | db_multi_exec( |
| 93 | "INSERT INTO patch.chng(fname,hash,isexe,islink,delta)" |
| 94 | " SELECT pathname, NULL, 0, 0, NULL" |
| 95 | " FROM vfile WHERE deleted;" |
| 96 | ); |
| 97 | /* Changed files */ |
| 98 | db_multi_exec( |
| 99 | "INSERT INTO patch.chng(fname,hash,isexe,islink,delta)" |
| 100 | " SELECT pathname, blob.uuid, isexe, islink," |
| 101 | " compress(delta_create(content(blob.uuid)," |
| 102 | "read_co_file(%Q||pathname)))" |
| 103 | " FROM vfile, blob" |
| 104 | " WHERE blob.rid=vfile.rid AND NOT deleted AND chnged;", |
| 105 | g.zLocalRoot |
| 106 | ); |
| 107 | } |
| 108 | |
| 109 | |
| 110 | /* |
| 111 | ** COMMAND: patch |
| 112 | ** |
| 113 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| @@ -69,18 +154,18 @@ | |
| 154 | db_must_be_within_tree(); |
| 155 | verify_all_options(); |
| 156 | if( g.argc!=4 ){ |
| 157 | usage("apply FILENAME"); |
| 158 | } |
| 159 | }else |
| 160 | if( strncmp(zCmd, "create", n)==0 ){ |
| 161 | db_must_be_within_tree(); |
| 162 | verify_all_options(); |
| 163 | if( g.argc!=4 ){ |
| 164 | usage("create FILENAME"); |
| 165 | } |
| 166 | patch_create(g.argv[3]); |
| 167 | }else |
| 168 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 169 | db_must_be_within_tree(); |
| 170 | verify_all_options(); |
| 171 | if( g.argc!=4 ){ |
| 172 |