Fossil SCM

Sync with trunk.

florian 2024-12-17 06:56 diff-word-wrap merge
Commit 5fbb14f73aaf0c7e02cf12f1a1d1cdfb30e44b13b76910c7e4796d2ef5d92122
+2 -2
--- src/checkin.c
+++ src/checkin.c
@@ -1563,17 +1563,17 @@
15631563
break;
15641564
}
15651565
diffFiles[i].nName = strlen(diffFiles[i].zName);
15661566
diffFiles[i].nUsed = 0;
15671567
}
1568
- diff_against_disk(0, &DCfg, diffFiles, &prompt);
1568
+ diff_version_to_checkout(0, &DCfg, diffFiles, &prompt);
15691569
for( i=0; diffFiles[i].zName; ++i ){
15701570
fossil_free(diffFiles[i].zName);
15711571
}
15721572
fossil_free(diffFiles);
15731573
}else{
1574
- diff_against_disk(0, &DCfg, 0, &prompt);
1574
+ diff_version_to_checkout(0, &DCfg, 0, &prompt);
15751575
}
15761576
}
15771577
prompt_for_user_comment(pComment, &prompt);
15781578
blob_reset(&prompt);
15791579
}
15801580
--- src/checkin.c
+++ src/checkin.c
@@ -1563,17 +1563,17 @@
1563 break;
1564 }
1565 diffFiles[i].nName = strlen(diffFiles[i].zName);
1566 diffFiles[i].nUsed = 0;
1567 }
1568 diff_against_disk(0, &DCfg, diffFiles, &prompt);
1569 for( i=0; diffFiles[i].zName; ++i ){
1570 fossil_free(diffFiles[i].zName);
1571 }
1572 fossil_free(diffFiles);
1573 }else{
1574 diff_against_disk(0, &DCfg, 0, &prompt);
1575 }
1576 }
1577 prompt_for_user_comment(pComment, &prompt);
1578 blob_reset(&prompt);
1579 }
1580
--- src/checkin.c
+++ src/checkin.c
@@ -1563,17 +1563,17 @@
1563 break;
1564 }
1565 diffFiles[i].nName = strlen(diffFiles[i].zName);
1566 diffFiles[i].nUsed = 0;
1567 }
1568 diff_version_to_checkout(0, &DCfg, diffFiles, &prompt);
1569 for( i=0; diffFiles[i].zName; ++i ){
1570 fossil_free(diffFiles[i].zName);
1571 }
1572 fossil_free(diffFiles);
1573 }else{
1574 diff_version_to_checkout(0, &DCfg, 0, &prompt);
1575 }
1576 }
1577 prompt_for_user_comment(pComment, &prompt);
1578 blob_reset(&prompt);
1579 }
1580
+5 -1
--- src/diff.tcl
+++ src/diff.tcl
@@ -110,11 +110,15 @@
110110
111111
set fromIndex [lsearch -glob $fossilcmd *-from]
112112
set toIndex [lsearch -glob $fossilcmd *-to]
113113
set branchIndex [lsearch -glob $fossilcmd *-branch]
114114
set checkinIndex [lsearch -glob $fossilcmd *-checkin]
115
- set fA {base check-in}
115
+ if {[string match *?--external-baseline* $fossilcmd]} {
116
+ set fA {external baseline}
117
+ } else {
118
+ set fA {base check-in}
119
+ }
116120
set fB {current check-out}
117121
if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
118122
if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
119123
if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
120124
if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
121125
--- src/diff.tcl
+++ src/diff.tcl
@@ -110,11 +110,15 @@
110
111 set fromIndex [lsearch -glob $fossilcmd *-from]
112 set toIndex [lsearch -glob $fossilcmd *-to]
113 set branchIndex [lsearch -glob $fossilcmd *-branch]
114 set checkinIndex [lsearch -glob $fossilcmd *-checkin]
115 set fA {base check-in}
 
 
 
 
116 set fB {current check-out}
117 if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
118 if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
119 if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
120 if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
121
--- src/diff.tcl
+++ src/diff.tcl
@@ -110,11 +110,15 @@
110
111 set fromIndex [lsearch -glob $fossilcmd *-from]
112 set toIndex [lsearch -glob $fossilcmd *-to]
113 set branchIndex [lsearch -glob $fossilcmd *-branch]
114 set checkinIndex [lsearch -glob $fossilcmd *-checkin]
115 if {[string match *?--external-baseline* $fossilcmd]} {
116 set fA {external baseline}
117 } else {
118 set fA {base check-in}
119 }
120 set fB {current check-out}
121 if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
122 if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
123 if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
124 if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
125
+92 -16
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -764,26 +764,27 @@
764764
blob_reset(&file);
765765
return rc;
766766
}
767767
768768
/*
769
-** Run a diff between the version zFrom and files on disk. zFrom might
770
-** be NULL which means to simply show the difference between the edited
771
-** files on disk and the check-out on which they are based.
769
+** Run a diff between the version zFrom and files on disk in the current
770
+** working checkout. zFrom might be NULL which means to simply show the
771
+** difference between the edited files on disk and the check-out on which
772
+** they are based.
772773
**
773774
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
774775
** command zDiffCmd to do the diffing.
775776
**
776777
** When using an external diff program, zBinGlob contains the GLOB patterns
777778
** for file names to treat as binary. If fIncludeBinary is zero, these files
778779
** will be skipped in addition to files that may contain binary content.
779780
*/
780
-void diff_against_disk(
781
+void diff_version_to_checkout(
781782
const char *zFrom, /* Version to difference from */
782783
DiffConfig *pCfg, /* Flags controlling diff output */
783784
FileDirList *pFileDir, /* Which files to diff */
784
- Blob *pOut /* Blob to output diff instead of stdout */
785
+ Blob *pOut /* Blob to output diff instead of stdout */
785786
){
786787
int vid;
787788
Blob sql;
788789
Stmt q;
789790
int asNewFile; /* Treat non-existant files as empty files */
@@ -908,20 +909,20 @@
908909
db_finalize(&q);
909910
db_end_transaction(1); /* ROLLBACK */
910911
}
911912
912913
/*
913
-** Run a diff between the undo buffer and files on disk.
914
+** Run a diff from the undo buffer to files on disk.
914915
**
915916
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
916917
** command zDiffCmd to do the diffing.
917918
**
918919
** When using an external diff program, zBinGlob contains the GLOB patterns
919920
** for file names to treat as binary. If fIncludeBinary is zero, these files
920921
** will be skipped in addition to files that may contain binary content.
921922
*/
922
-static void diff_against_undo(
923
+static void diff_undo_to_checkout(
923924
DiffConfig *pCfg, /* Flags controlling diff output */
924925
FileDirList *pFileDir /* List of files and directories to diff */
925926
){
926927
Stmt q;
927928
Blob content;
@@ -1068,10 +1069,67 @@
10681069
}
10691070
}
10701071
manifest_destroy(pFrom);
10711072
manifest_destroy(pTo);
10721073
}
1074
+
1075
+/*
1076
+** Compute the difference from an external tree of files to the current
1077
+** working checkout with its edits.
1078
+**
1079
+** To put it another way: Every managed file in the current working
1080
+** checkout is compared to the file with same name under zExternBase. The
1081
+** zExternBase files are on the left and the files in the current working
1082
+** directory are on the right.
1083
+*/
1084
+void diff_externbase_to_checkout(
1085
+ const char *zExternBase, /* Remote tree to use as the baseline */
1086
+ DiffConfig *pCfg, /* Diff settings */
1087
+ FileDirList *pFileDir /* Only look at these files */
1088
+){
1089
+ int vid;
1090
+ Stmt q;
1091
+
1092
+ vid = db_lget_int("checkout",0);
1093
+ if( file_isdir(zExternBase, ExtFILE)!=1 ){
1094
+ fossil_fatal("\"%s\" is not a directory", zExternBase);
1095
+ }
1096
+ db_prepare(&q,
1097
+ "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
1098
+ vid
1099
+ );
1100
+ while( db_step(&q)==SQLITE_ROW ){
1101
+ const char *zFile; /* Name of file in the repository */
1102
+ char *zLhs; /* Full name of left-hand side file */
1103
+ char *zRhs; /* Full name of right-hand side file */
1104
+ Blob rhs; /* Full text of RHS */
1105
+ Blob lhs; /* Full text of LHS */
1106
+
1107
+ zFile = db_column_text(&q,0);
1108
+ if( !file_dir_match(pFileDir, zFile) ) continue;
1109
+ zLhs = mprintf("%s/%s", zExternBase, zFile);
1110
+ zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
1111
+ if( file_size(zLhs, ExtFILE)<0 ){
1112
+ blob_zero(&lhs);
1113
+ }else{
1114
+ blob_read_from_file(&lhs, zLhs, ExtFILE);
1115
+ }
1116
+ blob_read_from_file(&rhs, zRhs, ExtFILE);
1117
+ if( blob_size(&lhs)!=blob_size(&rhs)
1118
+ || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
1119
+ ){
1120
+ diff_print_index(zFile, pCfg, 0);
1121
+ diff_file_mem(&lhs, &rhs, zFile, pCfg);
1122
+ }
1123
+ blob_reset(&lhs);
1124
+ blob_reset(&rhs);
1125
+ fossil_free(zLhs);
1126
+ fossil_free(zRhs);
1127
+ }
1128
+ db_finalize(&q);
1129
+}
1130
+
10731131
10741132
/*
10751133
** Return the name of the external diff command, or return NULL if
10761134
** no external diff command is defined.
10771135
*/
@@ -1204,10 +1262,14 @@
12041262
** option specifies the check-in from which the second version of the file
12051263
** or files is taken. If there is no "--to" option then the (possibly edited)
12061264
** files in the current check-out are used. The "--checkin VERSION" option
12071265
** shows the changes made by check-in VERSION relative to its primary parent.
12081266
** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
1267
+**
1268
+** With the "--from VERSION" option, if VERSION is actually a directory name
1269
+** (not a tag or check-in hash) then the files under that directory are used
1270
+** as the baseline for the diff.
12091271
**
12101272
** The "-i" command-line option forces the use of Fossil's own internal
12111273
** diff logic rather than any external diff program that might be configured
12121274
** using the "setting" command. If no external diff program is configured,
12131275
** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1236,11 +1298,13 @@
12361298
** with negative N meaning show all content
12371299
** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
12381300
** --diff-binary BOOL Include binary files with external commands
12391301
** --exec-abs-paths Force absolute path names on external commands
12401302
** --exec-rel-paths Force relative path names on external commands
1241
-** -r|--from VERSION Select VERSION as source for the diff
1303
+** -r|--from VERSION Use VERSION as the baseline for the diff, or
1304
+** if VERSION is a directory name, use files in
1305
+** that directory as the baseline.
12421306
** -w|--ignore-all-space Ignore white space when comparing lines
12431307
** -i|--internal Use internal diff logic
12441308
** --invert Invert the diff
12451309
** --json Output formatted as JSON
12461310
** -n|--linenum Show line numbers
@@ -1250,11 +1314,11 @@
12501314
** --strip-trailing-cr Strip trailing CR
12511315
** --tcl Tcl-formatted output used internally by --tk
12521316
** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
12531317
** --tk Launch a Tcl/Tk GUI for display
12541318
** --to VERSION Select VERSION as target for the diff
1255
-** --undo Diff against the "undo" buffer
1319
+** --undo Use the undo buffer as the baseline
12561320
** --unified Unified diff
12571321
** -v|--verbose Output complete text of added or deleted files
12581322
** -h|--versions Show compared versions in the diff header
12591323
** --webpage Format output as a stand-alone HTML webpage
12601324
** -W|--width N Width of lines in side-by-side diff
@@ -1267,10 +1331,11 @@
12671331
const char *zCheckin; /* Check-in version number */
12681332
const char *zBranch; /* Branch to diff */
12691333
int againstUndo = 0; /* Diff against files in the undo buffer */
12701334
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
12711335
DiffConfig DCfg; /* Diff configuration object */
1336
+ int bFromIsDir = 0; /* True if zFrom is a directory name */
12721337
12731338
if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
12741339
diff_tk("diff", 2);
12751340
return;
12761341
}
@@ -1278,11 +1343,11 @@
12781343
zFrom = find_option("from", "r", 1);
12791344
zTo = find_option("to", 0, 1);
12801345
zCheckin = find_option("checkin", "ci", 1);
12811346
zBranch = find_option("branch", 0, 1);
12821347
againstUndo = find_option("undo",0,0)!=0;
1283
- if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1348
+ if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
12841349
fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
12851350
" or --branch");
12861351
}
12871352
if( zBranch ){
12881353
if( zTo || zFrom || zCheckin ){
@@ -1289,15 +1354,13 @@
12891354
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
12901355
}
12911356
zTo = zBranch;
12921357
zFrom = mprintf("root:%s", zBranch);
12931358
}
1294
- if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
1359
+ if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
12951360
fossil_fatal("cannot use --checkin together with --from or --to");
12961361
}
1297
- diff_options(&DCfg, isGDiff, 0);
1298
- determine_exec_relative_option(1);
12991362
if( 0==zCheckin ){
13001363
if( zTo==0 || againstUndo ){
13011364
db_must_be_within_tree();
13021365
}else if( zFrom==0 ){
13031366
fossil_fatal("must use --from if --to is present");
@@ -1305,10 +1368,21 @@
13051368
db_find_and_open_repository(0, 0);
13061369
}
13071370
}else{
13081371
db_find_and_open_repository(0, 0);
13091372
}
1373
+ determine_exec_relative_option(1);
1374
+ if( zFrom!=file_tail(zFrom)
1375
+ && file_isdir(zFrom, ExtFILE)==1
1376
+ && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
1377
+ ){
1378
+ bFromIsDir = 1;
1379
+ if( zTo ){
1380
+ fossil_fatal("cannot use --to together with \"--from PATH\"");
1381
+ }
1382
+ }
1383
+ diff_options(&DCfg, isGDiff, 0);
13101384
verify_all_options();
13111385
g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
13121386
if( g.argc>=3 ){
13131387
int i;
13141388
Blob fname;
@@ -1337,18 +1411,20 @@
13371411
if( zFrom==0 ){
13381412
fossil_fatal("check-in %s has no parent", zTo);
13391413
}
13401414
}
13411415
diff_begin(&DCfg);
1342
- if( againstUndo ){
1416
+ if( bFromIsDir ){
1417
+ diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
1418
+ }else if( againstUndo ){
13431419
if( db_lget_int("undo_available",0)==0 ){
13441420
fossil_print("No undo or redo is available\n");
13451421
return;
13461422
}
1347
- diff_against_undo(&DCfg, pFileDir);
1423
+ diff_undo_to_checkout(&DCfg, pFileDir);
13481424
}else if( zTo==0 ){
1349
- diff_against_disk(zFrom, &DCfg, pFileDir, 0);
1425
+ diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
13501426
}else{
13511427
diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
13521428
}
13531429
if( pFileDir ){
13541430
int i;
13551431
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -764,26 +764,27 @@
764 blob_reset(&file);
765 return rc;
766 }
767
768 /*
769 ** Run a diff between the version zFrom and files on disk. zFrom might
770 ** be NULL which means to simply show the difference between the edited
771 ** files on disk and the check-out on which they are based.
 
772 **
773 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
774 ** command zDiffCmd to do the diffing.
775 **
776 ** When using an external diff program, zBinGlob contains the GLOB patterns
777 ** for file names to treat as binary. If fIncludeBinary is zero, these files
778 ** will be skipped in addition to files that may contain binary content.
779 */
780 void diff_against_disk(
781 const char *zFrom, /* Version to difference from */
782 DiffConfig *pCfg, /* Flags controlling diff output */
783 FileDirList *pFileDir, /* Which files to diff */
784 Blob *pOut /* Blob to output diff instead of stdout */
785 ){
786 int vid;
787 Blob sql;
788 Stmt q;
789 int asNewFile; /* Treat non-existant files as empty files */
@@ -908,20 +909,20 @@
908 db_finalize(&q);
909 db_end_transaction(1); /* ROLLBACK */
910 }
911
912 /*
913 ** Run a diff between the undo buffer and files on disk.
914 **
915 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
916 ** command zDiffCmd to do the diffing.
917 **
918 ** When using an external diff program, zBinGlob contains the GLOB patterns
919 ** for file names to treat as binary. If fIncludeBinary is zero, these files
920 ** will be skipped in addition to files that may contain binary content.
921 */
922 static void diff_against_undo(
923 DiffConfig *pCfg, /* Flags controlling diff output */
924 FileDirList *pFileDir /* List of files and directories to diff */
925 ){
926 Stmt q;
927 Blob content;
@@ -1068,10 +1069,67 @@
1068 }
1069 }
1070 manifest_destroy(pFrom);
1071 manifest_destroy(pTo);
1072 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
1074 /*
1075 ** Return the name of the external diff command, or return NULL if
1076 ** no external diff command is defined.
1077 */
@@ -1204,10 +1262,14 @@
1204 ** option specifies the check-in from which the second version of the file
1205 ** or files is taken. If there is no "--to" option then the (possibly edited)
1206 ** files in the current check-out are used. The "--checkin VERSION" option
1207 ** shows the changes made by check-in VERSION relative to its primary parent.
1208 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
 
 
 
 
1209 **
1210 ** The "-i" command-line option forces the use of Fossil's own internal
1211 ** diff logic rather than any external diff program that might be configured
1212 ** using the "setting" command. If no external diff program is configured,
1213 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1236,11 +1298,13 @@
1236 ** with negative N meaning show all content
1237 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
1238 ** --diff-binary BOOL Include binary files with external commands
1239 ** --exec-abs-paths Force absolute path names on external commands
1240 ** --exec-rel-paths Force relative path names on external commands
1241 ** -r|--from VERSION Select VERSION as source for the diff
 
 
1242 ** -w|--ignore-all-space Ignore white space when comparing lines
1243 ** -i|--internal Use internal diff logic
1244 ** --invert Invert the diff
1245 ** --json Output formatted as JSON
1246 ** -n|--linenum Show line numbers
@@ -1250,11 +1314,11 @@
1250 ** --strip-trailing-cr Strip trailing CR
1251 ** --tcl Tcl-formatted output used internally by --tk
1252 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1253 ** --tk Launch a Tcl/Tk GUI for display
1254 ** --to VERSION Select VERSION as target for the diff
1255 ** --undo Diff against the "undo" buffer
1256 ** --unified Unified diff
1257 ** -v|--verbose Output complete text of added or deleted files
1258 ** -h|--versions Show compared versions in the diff header
1259 ** --webpage Format output as a stand-alone HTML webpage
1260 ** -W|--width N Width of lines in side-by-side diff
@@ -1267,10 +1331,11 @@
1267 const char *zCheckin; /* Check-in version number */
1268 const char *zBranch; /* Branch to diff */
1269 int againstUndo = 0; /* Diff against files in the undo buffer */
1270 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1271 DiffConfig DCfg; /* Diff configuration object */
 
1272
1273 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1274 diff_tk("diff", 2);
1275 return;
1276 }
@@ -1278,11 +1343,11 @@
1278 zFrom = find_option("from", "r", 1);
1279 zTo = find_option("to", 0, 1);
1280 zCheckin = find_option("checkin", "ci", 1);
1281 zBranch = find_option("branch", 0, 1);
1282 againstUndo = find_option("undo",0,0)!=0;
1283 if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1284 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
1285 " or --branch");
1286 }
1287 if( zBranch ){
1288 if( zTo || zFrom || zCheckin ){
@@ -1289,15 +1354,13 @@
1289 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1290 }
1291 zTo = zBranch;
1292 zFrom = mprintf("root:%s", zBranch);
1293 }
1294 if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
1295 fossil_fatal("cannot use --checkin together with --from or --to");
1296 }
1297 diff_options(&DCfg, isGDiff, 0);
1298 determine_exec_relative_option(1);
1299 if( 0==zCheckin ){
1300 if( zTo==0 || againstUndo ){
1301 db_must_be_within_tree();
1302 }else if( zFrom==0 ){
1303 fossil_fatal("must use --from if --to is present");
@@ -1305,10 +1368,21 @@
1305 db_find_and_open_repository(0, 0);
1306 }
1307 }else{
1308 db_find_and_open_repository(0, 0);
1309 }
 
 
 
 
 
 
 
 
 
 
 
1310 verify_all_options();
1311 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
1312 if( g.argc>=3 ){
1313 int i;
1314 Blob fname;
@@ -1337,18 +1411,20 @@
1337 if( zFrom==0 ){
1338 fossil_fatal("check-in %s has no parent", zTo);
1339 }
1340 }
1341 diff_begin(&DCfg);
1342 if( againstUndo ){
 
 
1343 if( db_lget_int("undo_available",0)==0 ){
1344 fossil_print("No undo or redo is available\n");
1345 return;
1346 }
1347 diff_against_undo(&DCfg, pFileDir);
1348 }else if( zTo==0 ){
1349 diff_against_disk(zFrom, &DCfg, pFileDir, 0);
1350 }else{
1351 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
1352 }
1353 if( pFileDir ){
1354 int i;
1355
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -764,26 +764,27 @@
764 blob_reset(&file);
765 return rc;
766 }
767
768 /*
769 ** Run a diff between the version zFrom and files on disk in the current
770 ** working checkout. zFrom might be NULL which means to simply show the
771 ** difference between the edited files on disk and the check-out on which
772 ** they are based.
773 **
774 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
775 ** command zDiffCmd to do the diffing.
776 **
777 ** When using an external diff program, zBinGlob contains the GLOB patterns
778 ** for file names to treat as binary. If fIncludeBinary is zero, these files
779 ** will be skipped in addition to files that may contain binary content.
780 */
781 void diff_version_to_checkout(
782 const char *zFrom, /* Version to difference from */
783 DiffConfig *pCfg, /* Flags controlling diff output */
784 FileDirList *pFileDir, /* Which files to diff */
785 Blob *pOut /* Blob to output diff instead of stdout */
786 ){
787 int vid;
788 Blob sql;
789 Stmt q;
790 int asNewFile; /* Treat non-existant files as empty files */
@@ -908,20 +909,20 @@
909 db_finalize(&q);
910 db_end_transaction(1); /* ROLLBACK */
911 }
912
913 /*
914 ** Run a diff from the undo buffer to files on disk.
915 **
916 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
917 ** command zDiffCmd to do the diffing.
918 **
919 ** When using an external diff program, zBinGlob contains the GLOB patterns
920 ** for file names to treat as binary. If fIncludeBinary is zero, these files
921 ** will be skipped in addition to files that may contain binary content.
922 */
923 static void diff_undo_to_checkout(
924 DiffConfig *pCfg, /* Flags controlling diff output */
925 FileDirList *pFileDir /* List of files and directories to diff */
926 ){
927 Stmt q;
928 Blob content;
@@ -1068,10 +1069,67 @@
1069 }
1070 }
1071 manifest_destroy(pFrom);
1072 manifest_destroy(pTo);
1073 }
1074
1075 /*
1076 ** Compute the difference from an external tree of files to the current
1077 ** working checkout with its edits.
1078 **
1079 ** To put it another way: Every managed file in the current working
1080 ** checkout is compared to the file with same name under zExternBase. The
1081 ** zExternBase files are on the left and the files in the current working
1082 ** directory are on the right.
1083 */
1084 void diff_externbase_to_checkout(
1085 const char *zExternBase, /* Remote tree to use as the baseline */
1086 DiffConfig *pCfg, /* Diff settings */
1087 FileDirList *pFileDir /* Only look at these files */
1088 ){
1089 int vid;
1090 Stmt q;
1091
1092 vid = db_lget_int("checkout",0);
1093 if( file_isdir(zExternBase, ExtFILE)!=1 ){
1094 fossil_fatal("\"%s\" is not a directory", zExternBase);
1095 }
1096 db_prepare(&q,
1097 "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
1098 vid
1099 );
1100 while( db_step(&q)==SQLITE_ROW ){
1101 const char *zFile; /* Name of file in the repository */
1102 char *zLhs; /* Full name of left-hand side file */
1103 char *zRhs; /* Full name of right-hand side file */
1104 Blob rhs; /* Full text of RHS */
1105 Blob lhs; /* Full text of LHS */
1106
1107 zFile = db_column_text(&q,0);
1108 if( !file_dir_match(pFileDir, zFile) ) continue;
1109 zLhs = mprintf("%s/%s", zExternBase, zFile);
1110 zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
1111 if( file_size(zLhs, ExtFILE)<0 ){
1112 blob_zero(&lhs);
1113 }else{
1114 blob_read_from_file(&lhs, zLhs, ExtFILE);
1115 }
1116 blob_read_from_file(&rhs, zRhs, ExtFILE);
1117 if( blob_size(&lhs)!=blob_size(&rhs)
1118 || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
1119 ){
1120 diff_print_index(zFile, pCfg, 0);
1121 diff_file_mem(&lhs, &rhs, zFile, pCfg);
1122 }
1123 blob_reset(&lhs);
1124 blob_reset(&rhs);
1125 fossil_free(zLhs);
1126 fossil_free(zRhs);
1127 }
1128 db_finalize(&q);
1129 }
1130
1131
1132 /*
1133 ** Return the name of the external diff command, or return NULL if
1134 ** no external diff command is defined.
1135 */
@@ -1204,10 +1262,14 @@
1262 ** option specifies the check-in from which the second version of the file
1263 ** or files is taken. If there is no "--to" option then the (possibly edited)
1264 ** files in the current check-out are used. The "--checkin VERSION" option
1265 ** shows the changes made by check-in VERSION relative to its primary parent.
1266 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
1267 **
1268 ** With the "--from VERSION" option, if VERSION is actually a directory name
1269 ** (not a tag or check-in hash) then the files under that directory are used
1270 ** as the baseline for the diff.
1271 **
1272 ** The "-i" command-line option forces the use of Fossil's own internal
1273 ** diff logic rather than any external diff program that might be configured
1274 ** using the "setting" command. If no external diff program is configured,
1275 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1236,11 +1298,13 @@
1298 ** with negative N meaning show all content
1299 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
1300 ** --diff-binary BOOL Include binary files with external commands
1301 ** --exec-abs-paths Force absolute path names on external commands
1302 ** --exec-rel-paths Force relative path names on external commands
1303 ** -r|--from VERSION Use VERSION as the baseline for the diff, or
1304 ** if VERSION is a directory name, use files in
1305 ** that directory as the baseline.
1306 ** -w|--ignore-all-space Ignore white space when comparing lines
1307 ** -i|--internal Use internal diff logic
1308 ** --invert Invert the diff
1309 ** --json Output formatted as JSON
1310 ** -n|--linenum Show line numbers
@@ -1250,11 +1314,11 @@
1314 ** --strip-trailing-cr Strip trailing CR
1315 ** --tcl Tcl-formatted output used internally by --tk
1316 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1317 ** --tk Launch a Tcl/Tk GUI for display
1318 ** --to VERSION Select VERSION as target for the diff
1319 ** --undo Use the undo buffer as the baseline
1320 ** --unified Unified diff
1321 ** -v|--verbose Output complete text of added or deleted files
1322 ** -h|--versions Show compared versions in the diff header
1323 ** --webpage Format output as a stand-alone HTML webpage
1324 ** -W|--width N Width of lines in side-by-side diff
@@ -1267,10 +1331,11 @@
1331 const char *zCheckin; /* Check-in version number */
1332 const char *zBranch; /* Branch to diff */
1333 int againstUndo = 0; /* Diff against files in the undo buffer */
1334 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1335 DiffConfig DCfg; /* Diff configuration object */
1336 int bFromIsDir = 0; /* True if zFrom is a directory name */
1337
1338 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1339 diff_tk("diff", 2);
1340 return;
1341 }
@@ -1278,11 +1343,11 @@
1343 zFrom = find_option("from", "r", 1);
1344 zTo = find_option("to", 0, 1);
1345 zCheckin = find_option("checkin", "ci", 1);
1346 zBranch = find_option("branch", 0, 1);
1347 againstUndo = find_option("undo",0,0)!=0;
1348 if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1349 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
1350 " or --branch");
1351 }
1352 if( zBranch ){
1353 if( zTo || zFrom || zCheckin ){
@@ -1289,15 +1354,13 @@
1354 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1355 }
1356 zTo = zBranch;
1357 zFrom = mprintf("root:%s", zBranch);
1358 }
1359 if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
1360 fossil_fatal("cannot use --checkin together with --from or --to");
1361 }
 
 
1362 if( 0==zCheckin ){
1363 if( zTo==0 || againstUndo ){
1364 db_must_be_within_tree();
1365 }else if( zFrom==0 ){
1366 fossil_fatal("must use --from if --to is present");
@@ -1305,10 +1368,21 @@
1368 db_find_and_open_repository(0, 0);
1369 }
1370 }else{
1371 db_find_and_open_repository(0, 0);
1372 }
1373 determine_exec_relative_option(1);
1374 if( zFrom!=file_tail(zFrom)
1375 && file_isdir(zFrom, ExtFILE)==1
1376 && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
1377 ){
1378 bFromIsDir = 1;
1379 if( zTo ){
1380 fossil_fatal("cannot use --to together with \"--from PATH\"");
1381 }
1382 }
1383 diff_options(&DCfg, isGDiff, 0);
1384 verify_all_options();
1385 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
1386 if( g.argc>=3 ){
1387 int i;
1388 Blob fname;
@@ -1337,18 +1411,20 @@
1411 if( zFrom==0 ){
1412 fossil_fatal("check-in %s has no parent", zTo);
1413 }
1414 }
1415 diff_begin(&DCfg);
1416 if( bFromIsDir ){
1417 diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
1418 }else if( againstUndo ){
1419 if( db_lget_int("undo_available",0)==0 ){
1420 fossil_print("No undo or redo is available\n");
1421 return;
1422 }
1423 diff_undo_to_checkout(&DCfg, pFileDir);
1424 }else if( zTo==0 ){
1425 diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
1426 }else{
1427 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
1428 }
1429 if( pFileDir ){
1430 int i;
1431
+92 -16
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -764,26 +764,27 @@
764764
blob_reset(&file);
765765
return rc;
766766
}
767767
768768
/*
769
-** Run a diff between the version zFrom and files on disk. zFrom might
770
-** be NULL which means to simply show the difference between the edited
771
-** files on disk and the check-out on which they are based.
769
+** Run a diff between the version zFrom and files on disk in the current
770
+** working checkout. zFrom might be NULL which means to simply show the
771
+** difference between the edited files on disk and the check-out on which
772
+** they are based.
772773
**
773774
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
774775
** command zDiffCmd to do the diffing.
775776
**
776777
** When using an external diff program, zBinGlob contains the GLOB patterns
777778
** for file names to treat as binary. If fIncludeBinary is zero, these files
778779
** will be skipped in addition to files that may contain binary content.
779780
*/
780
-void diff_against_disk(
781
+void diff_version_to_checkout(
781782
const char *zFrom, /* Version to difference from */
782783
DiffConfig *pCfg, /* Flags controlling diff output */
783784
FileDirList *pFileDir, /* Which files to diff */
784
- Blob *pOut /* Blob to output diff instead of stdout */
785
+ Blob *pOut /* Blob to output diff instead of stdout */
785786
){
786787
int vid;
787788
Blob sql;
788789
Stmt q;
789790
int asNewFile; /* Treat non-existant files as empty files */
@@ -908,20 +909,20 @@
908909
db_finalize(&q);
909910
db_end_transaction(1); /* ROLLBACK */
910911
}
911912
912913
/*
913
-** Run a diff between the undo buffer and files on disk.
914
+** Run a diff from the undo buffer to files on disk.
914915
**
915916
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
916917
** command zDiffCmd to do the diffing.
917918
**
918919
** When using an external diff program, zBinGlob contains the GLOB patterns
919920
** for file names to treat as binary. If fIncludeBinary is zero, these files
920921
** will be skipped in addition to files that may contain binary content.
921922
*/
922
-static void diff_against_undo(
923
+static void diff_undo_to_checkout(
923924
DiffConfig *pCfg, /* Flags controlling diff output */
924925
FileDirList *pFileDir /* List of files and directories to diff */
925926
){
926927
Stmt q;
927928
Blob content;
@@ -1068,10 +1069,67 @@
10681069
}
10691070
}
10701071
manifest_destroy(pFrom);
10711072
manifest_destroy(pTo);
10721073
}
1074
+
1075
+/*
1076
+** Compute the difference from an external tree of files to the current
1077
+** working checkout with its edits.
1078
+**
1079
+** To put it another way: Every managed file in the current working
1080
+** checkout is compared to the file with same name under zExternBase. The
1081
+** zExternBase files are on the left and the files in the current working
1082
+** directory are on the right.
1083
+*/
1084
+void diff_externbase_to_checkout(
1085
+ const char *zExternBase, /* Remote tree to use as the baseline */
1086
+ DiffConfig *pCfg, /* Diff settings */
1087
+ FileDirList *pFileDir /* Only look at these files */
1088
+){
1089
+ int vid;
1090
+ Stmt q;
1091
+
1092
+ vid = db_lget_int("checkout",0);
1093
+ if( file_isdir(zExternBase, ExtFILE)!=1 ){
1094
+ fossil_fatal("\"%s\" is not a directory", zExternBase);
1095
+ }
1096
+ db_prepare(&q,
1097
+ "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
1098
+ vid
1099
+ );
1100
+ while( db_step(&q)==SQLITE_ROW ){
1101
+ const char *zFile; /* Name of file in the repository */
1102
+ char *zLhs; /* Full name of left-hand side file */
1103
+ char *zRhs; /* Full name of right-hand side file */
1104
+ Blob rhs; /* Full text of RHS */
1105
+ Blob lhs; /* Full text of LHS */
1106
+
1107
+ zFile = db_column_text(&q,0);
1108
+ if( !file_dir_match(pFileDir, zFile) ) continue;
1109
+ zLhs = mprintf("%s/%s", zExternBase, zFile);
1110
+ zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
1111
+ if( file_size(zLhs, ExtFILE)<0 ){
1112
+ blob_zero(&lhs);
1113
+ }else{
1114
+ blob_read_from_file(&lhs, zLhs, ExtFILE);
1115
+ }
1116
+ blob_read_from_file(&rhs, zRhs, ExtFILE);
1117
+ if( blob_size(&lhs)!=blob_size(&rhs)
1118
+ || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
1119
+ ){
1120
+ diff_print_index(zFile, pCfg, 0);
1121
+ diff_file_mem(&lhs, &rhs, zFile, pCfg);
1122
+ }
1123
+ blob_reset(&lhs);
1124
+ blob_reset(&rhs);
1125
+ fossil_free(zLhs);
1126
+ fossil_free(zRhs);
1127
+ }
1128
+ db_finalize(&q);
1129
+}
1130
+
10731131
10741132
/*
10751133
** Return the name of the external diff command, or return NULL if
10761134
** no external diff command is defined.
10771135
*/
@@ -1204,10 +1262,14 @@
12041262
** option specifies the check-in from which the second version of the file
12051263
** or files is taken. If there is no "--to" option then the (possibly edited)
12061264
** files in the current check-out are used. The "--checkin VERSION" option
12071265
** shows the changes made by check-in VERSION relative to its primary parent.
12081266
** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
1267
+**
1268
+** With the "--from VERSION" option, if VERSION is actually a directory name
1269
+** (not a tag or check-in hash) then the files under that directory are used
1270
+** as the baseline for the diff.
12091271
**
12101272
** The "-i" command-line option forces the use of Fossil's own internal
12111273
** diff logic rather than any external diff program that might be configured
12121274
** using the "setting" command. If no external diff program is configured,
12131275
** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1236,11 +1298,13 @@
12361298
** with negative N meaning show all content
12371299
** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
12381300
** --diff-binary BOOL Include binary files with external commands
12391301
** --exec-abs-paths Force absolute path names on external commands
12401302
** --exec-rel-paths Force relative path names on external commands
1241
-** -r|--from VERSION Select VERSION as source for the diff
1303
+** -r|--from VERSION Use VERSION as the baseline for the diff, or
1304
+** if VERSION is a directory name, use files in
1305
+** that directory as the baseline.
12421306
** -w|--ignore-all-space Ignore white space when comparing lines
12431307
** -i|--internal Use internal diff logic
12441308
** --invert Invert the diff
12451309
** --json Output formatted as JSON
12461310
** -n|--linenum Show line numbers
@@ -1250,11 +1314,11 @@
12501314
** --strip-trailing-cr Strip trailing CR
12511315
** --tcl Tcl-formatted output used internally by --tk
12521316
** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
12531317
** --tk Launch a Tcl/Tk GUI for display
12541318
** --to VERSION Select VERSION as target for the diff
1255
-** --undo Diff against the "undo" buffer
1319
+** --undo Use the undo buffer as the baseline
12561320
** --unified Unified diff
12571321
** -v|--verbose Output complete text of added or deleted files
12581322
** -h|--versions Show compared versions in the diff header
12591323
** --webpage Format output as a stand-alone HTML webpage
12601324
** -W|--width N Width of lines in side-by-side diff
@@ -1267,10 +1331,11 @@
12671331
const char *zCheckin; /* Check-in version number */
12681332
const char *zBranch; /* Branch to diff */
12691333
int againstUndo = 0; /* Diff against files in the undo buffer */
12701334
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
12711335
DiffConfig DCfg; /* Diff configuration object */
1336
+ int bFromIsDir = 0; /* True if zFrom is a directory name */
12721337
12731338
if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
12741339
diff_tk("diff", 2);
12751340
return;
12761341
}
@@ -1278,11 +1343,11 @@
12781343
zFrom = find_option("from", "r", 1);
12791344
zTo = find_option("to", 0, 1);
12801345
zCheckin = find_option("checkin", "ci", 1);
12811346
zBranch = find_option("branch", 0, 1);
12821347
againstUndo = find_option("undo",0,0)!=0;
1283
- if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1348
+ if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
12841349
fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
12851350
" or --branch");
12861351
}
12871352
if( zBranch ){
12881353
if( zTo || zFrom || zCheckin ){
@@ -1289,15 +1354,13 @@
12891354
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
12901355
}
12911356
zTo = zBranch;
12921357
zFrom = mprintf("root:%s", zBranch);
12931358
}
1294
- if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
1359
+ if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
12951360
fossil_fatal("cannot use --checkin together with --from or --to");
12961361
}
1297
- diff_options(&DCfg, isGDiff, 0);
1298
- determine_exec_relative_option(1);
12991362
if( 0==zCheckin ){
13001363
if( zTo==0 || againstUndo ){
13011364
db_must_be_within_tree();
13021365
}else if( zFrom==0 ){
13031366
fossil_fatal("must use --from if --to is present");
@@ -1305,10 +1368,21 @@
13051368
db_find_and_open_repository(0, 0);
13061369
}
13071370
}else{
13081371
db_find_and_open_repository(0, 0);
13091372
}
1373
+ determine_exec_relative_option(1);
1374
+ if( zFrom!=file_tail(zFrom)
1375
+ && file_isdir(zFrom, ExtFILE)==1
1376
+ && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
1377
+ ){
1378
+ bFromIsDir = 1;
1379
+ if( zTo ){
1380
+ fossil_fatal("cannot use --to together with \"--from PATH\"");
1381
+ }
1382
+ }
1383
+ diff_options(&DCfg, isGDiff, 0);
13101384
verify_all_options();
13111385
g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
13121386
if( g.argc>=3 ){
13131387
int i;
13141388
Blob fname;
@@ -1337,18 +1411,20 @@
13371411
if( zFrom==0 ){
13381412
fossil_fatal("check-in %s has no parent", zTo);
13391413
}
13401414
}
13411415
diff_begin(&DCfg);
1342
- if( againstUndo ){
1416
+ if( bFromIsDir ){
1417
+ diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
1418
+ }else if( againstUndo ){
13431419
if( db_lget_int("undo_available",0)==0 ){
13441420
fossil_print("No undo or redo is available\n");
13451421
return;
13461422
}
1347
- diff_against_undo(&DCfg, pFileDir);
1423
+ diff_undo_to_checkout(&DCfg, pFileDir);
13481424
}else if( zTo==0 ){
1349
- diff_against_disk(zFrom, &DCfg, pFileDir, 0);
1425
+ diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
13501426
}else{
13511427
diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
13521428
}
13531429
if( pFileDir ){
13541430
int i;
13551431
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -764,26 +764,27 @@
764 blob_reset(&file);
765 return rc;
766 }
767
768 /*
769 ** Run a diff between the version zFrom and files on disk. zFrom might
770 ** be NULL which means to simply show the difference between the edited
771 ** files on disk and the check-out on which they are based.
 
772 **
773 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
774 ** command zDiffCmd to do the diffing.
775 **
776 ** When using an external diff program, zBinGlob contains the GLOB patterns
777 ** for file names to treat as binary. If fIncludeBinary is zero, these files
778 ** will be skipped in addition to files that may contain binary content.
779 */
780 void diff_against_disk(
781 const char *zFrom, /* Version to difference from */
782 DiffConfig *pCfg, /* Flags controlling diff output */
783 FileDirList *pFileDir, /* Which files to diff */
784 Blob *pOut /* Blob to output diff instead of stdout */
785 ){
786 int vid;
787 Blob sql;
788 Stmt q;
789 int asNewFile; /* Treat non-existant files as empty files */
@@ -908,20 +909,20 @@
908 db_finalize(&q);
909 db_end_transaction(1); /* ROLLBACK */
910 }
911
912 /*
913 ** Run a diff between the undo buffer and files on disk.
914 **
915 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
916 ** command zDiffCmd to do the diffing.
917 **
918 ** When using an external diff program, zBinGlob contains the GLOB patterns
919 ** for file names to treat as binary. If fIncludeBinary is zero, these files
920 ** will be skipped in addition to files that may contain binary content.
921 */
922 static void diff_against_undo(
923 DiffConfig *pCfg, /* Flags controlling diff output */
924 FileDirList *pFileDir /* List of files and directories to diff */
925 ){
926 Stmt q;
927 Blob content;
@@ -1068,10 +1069,67 @@
1068 }
1069 }
1070 manifest_destroy(pFrom);
1071 manifest_destroy(pTo);
1072 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
1074 /*
1075 ** Return the name of the external diff command, or return NULL if
1076 ** no external diff command is defined.
1077 */
@@ -1204,10 +1262,14 @@
1204 ** option specifies the check-in from which the second version of the file
1205 ** or files is taken. If there is no "--to" option then the (possibly edited)
1206 ** files in the current check-out are used. The "--checkin VERSION" option
1207 ** shows the changes made by check-in VERSION relative to its primary parent.
1208 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
 
 
 
 
1209 **
1210 ** The "-i" command-line option forces the use of Fossil's own internal
1211 ** diff logic rather than any external diff program that might be configured
1212 ** using the "setting" command. If no external diff program is configured,
1213 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1236,11 +1298,13 @@
1236 ** with negative N meaning show all content
1237 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
1238 ** --diff-binary BOOL Include binary files with external commands
1239 ** --exec-abs-paths Force absolute path names on external commands
1240 ** --exec-rel-paths Force relative path names on external commands
1241 ** -r|--from VERSION Select VERSION as source for the diff
 
 
1242 ** -w|--ignore-all-space Ignore white space when comparing lines
1243 ** -i|--internal Use internal diff logic
1244 ** --invert Invert the diff
1245 ** --json Output formatted as JSON
1246 ** -n|--linenum Show line numbers
@@ -1250,11 +1314,11 @@
1250 ** --strip-trailing-cr Strip trailing CR
1251 ** --tcl Tcl-formatted output used internally by --tk
1252 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1253 ** --tk Launch a Tcl/Tk GUI for display
1254 ** --to VERSION Select VERSION as target for the diff
1255 ** --undo Diff against the "undo" buffer
1256 ** --unified Unified diff
1257 ** -v|--verbose Output complete text of added or deleted files
1258 ** -h|--versions Show compared versions in the diff header
1259 ** --webpage Format output as a stand-alone HTML webpage
1260 ** -W|--width N Width of lines in side-by-side diff
@@ -1267,10 +1331,11 @@
1267 const char *zCheckin; /* Check-in version number */
1268 const char *zBranch; /* Branch to diff */
1269 int againstUndo = 0; /* Diff against files in the undo buffer */
1270 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1271 DiffConfig DCfg; /* Diff configuration object */
 
1272
1273 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1274 diff_tk("diff", 2);
1275 return;
1276 }
@@ -1278,11 +1343,11 @@
1278 zFrom = find_option("from", "r", 1);
1279 zTo = find_option("to", 0, 1);
1280 zCheckin = find_option("checkin", "ci", 1);
1281 zBranch = find_option("branch", 0, 1);
1282 againstUndo = find_option("undo",0,0)!=0;
1283 if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1284 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
1285 " or --branch");
1286 }
1287 if( zBranch ){
1288 if( zTo || zFrom || zCheckin ){
@@ -1289,15 +1354,13 @@
1289 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1290 }
1291 zTo = zBranch;
1292 zFrom = mprintf("root:%s", zBranch);
1293 }
1294 if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
1295 fossil_fatal("cannot use --checkin together with --from or --to");
1296 }
1297 diff_options(&DCfg, isGDiff, 0);
1298 determine_exec_relative_option(1);
1299 if( 0==zCheckin ){
1300 if( zTo==0 || againstUndo ){
1301 db_must_be_within_tree();
1302 }else if( zFrom==0 ){
1303 fossil_fatal("must use --from if --to is present");
@@ -1305,10 +1368,21 @@
1305 db_find_and_open_repository(0, 0);
1306 }
1307 }else{
1308 db_find_and_open_repository(0, 0);
1309 }
 
 
 
 
 
 
 
 
 
 
 
1310 verify_all_options();
1311 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
1312 if( g.argc>=3 ){
1313 int i;
1314 Blob fname;
@@ -1337,18 +1411,20 @@
1337 if( zFrom==0 ){
1338 fossil_fatal("check-in %s has no parent", zTo);
1339 }
1340 }
1341 diff_begin(&DCfg);
1342 if( againstUndo ){
 
 
1343 if( db_lget_int("undo_available",0)==0 ){
1344 fossil_print("No undo or redo is available\n");
1345 return;
1346 }
1347 diff_against_undo(&DCfg, pFileDir);
1348 }else if( zTo==0 ){
1349 diff_against_disk(zFrom, &DCfg, pFileDir, 0);
1350 }else{
1351 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
1352 }
1353 if( pFileDir ){
1354 int i;
1355
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -764,26 +764,27 @@
764 blob_reset(&file);
765 return rc;
766 }
767
768 /*
769 ** Run a diff between the version zFrom and files on disk in the current
770 ** working checkout. zFrom might be NULL which means to simply show the
771 ** difference between the edited files on disk and the check-out on which
772 ** they are based.
773 **
774 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
775 ** command zDiffCmd to do the diffing.
776 **
777 ** When using an external diff program, zBinGlob contains the GLOB patterns
778 ** for file names to treat as binary. If fIncludeBinary is zero, these files
779 ** will be skipped in addition to files that may contain binary content.
780 */
781 void diff_version_to_checkout(
782 const char *zFrom, /* Version to difference from */
783 DiffConfig *pCfg, /* Flags controlling diff output */
784 FileDirList *pFileDir, /* Which files to diff */
785 Blob *pOut /* Blob to output diff instead of stdout */
786 ){
787 int vid;
788 Blob sql;
789 Stmt q;
790 int asNewFile; /* Treat non-existant files as empty files */
@@ -908,20 +909,20 @@
909 db_finalize(&q);
910 db_end_transaction(1); /* ROLLBACK */
911 }
912
913 /*
914 ** Run a diff from the undo buffer to files on disk.
915 **
916 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
917 ** command zDiffCmd to do the diffing.
918 **
919 ** When using an external diff program, zBinGlob contains the GLOB patterns
920 ** for file names to treat as binary. If fIncludeBinary is zero, these files
921 ** will be skipped in addition to files that may contain binary content.
922 */
923 static void diff_undo_to_checkout(
924 DiffConfig *pCfg, /* Flags controlling diff output */
925 FileDirList *pFileDir /* List of files and directories to diff */
926 ){
927 Stmt q;
928 Blob content;
@@ -1068,10 +1069,67 @@
1069 }
1070 }
1071 manifest_destroy(pFrom);
1072 manifest_destroy(pTo);
1073 }
1074
1075 /*
1076 ** Compute the difference from an external tree of files to the current
1077 ** working checkout with its edits.
1078 **
1079 ** To put it another way: Every managed file in the current working
1080 ** checkout is compared to the file with same name under zExternBase. The
1081 ** zExternBase files are on the left and the files in the current working
1082 ** directory are on the right.
1083 */
1084 void diff_externbase_to_checkout(
1085 const char *zExternBase, /* Remote tree to use as the baseline */
1086 DiffConfig *pCfg, /* Diff settings */
1087 FileDirList *pFileDir /* Only look at these files */
1088 ){
1089 int vid;
1090 Stmt q;
1091
1092 vid = db_lget_int("checkout",0);
1093 if( file_isdir(zExternBase, ExtFILE)!=1 ){
1094 fossil_fatal("\"%s\" is not a directory", zExternBase);
1095 }
1096 db_prepare(&q,
1097 "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
1098 vid
1099 );
1100 while( db_step(&q)==SQLITE_ROW ){
1101 const char *zFile; /* Name of file in the repository */
1102 char *zLhs; /* Full name of left-hand side file */
1103 char *zRhs; /* Full name of right-hand side file */
1104 Blob rhs; /* Full text of RHS */
1105 Blob lhs; /* Full text of LHS */
1106
1107 zFile = db_column_text(&q,0);
1108 if( !file_dir_match(pFileDir, zFile) ) continue;
1109 zLhs = mprintf("%s/%s", zExternBase, zFile);
1110 zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
1111 if( file_size(zLhs, ExtFILE)<0 ){
1112 blob_zero(&lhs);
1113 }else{
1114 blob_read_from_file(&lhs, zLhs, ExtFILE);
1115 }
1116 blob_read_from_file(&rhs, zRhs, ExtFILE);
1117 if( blob_size(&lhs)!=blob_size(&rhs)
1118 || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
1119 ){
1120 diff_print_index(zFile, pCfg, 0);
1121 diff_file_mem(&lhs, &rhs, zFile, pCfg);
1122 }
1123 blob_reset(&lhs);
1124 blob_reset(&rhs);
1125 fossil_free(zLhs);
1126 fossil_free(zRhs);
1127 }
1128 db_finalize(&q);
1129 }
1130
1131
1132 /*
1133 ** Return the name of the external diff command, or return NULL if
1134 ** no external diff command is defined.
1135 */
@@ -1204,10 +1262,14 @@
1262 ** option specifies the check-in from which the second version of the file
1263 ** or files is taken. If there is no "--to" option then the (possibly edited)
1264 ** files in the current check-out are used. The "--checkin VERSION" option
1265 ** shows the changes made by check-in VERSION relative to its primary parent.
1266 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
1267 **
1268 ** With the "--from VERSION" option, if VERSION is actually a directory name
1269 ** (not a tag or check-in hash) then the files under that directory are used
1270 ** as the baseline for the diff.
1271 **
1272 ** The "-i" command-line option forces the use of Fossil's own internal
1273 ** diff logic rather than any external diff program that might be configured
1274 ** using the "setting" command. If no external diff program is configured,
1275 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1236,11 +1298,13 @@
1298 ** with negative N meaning show all content
1299 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
1300 ** --diff-binary BOOL Include binary files with external commands
1301 ** --exec-abs-paths Force absolute path names on external commands
1302 ** --exec-rel-paths Force relative path names on external commands
1303 ** -r|--from VERSION Use VERSION as the baseline for the diff, or
1304 ** if VERSION is a directory name, use files in
1305 ** that directory as the baseline.
1306 ** -w|--ignore-all-space Ignore white space when comparing lines
1307 ** -i|--internal Use internal diff logic
1308 ** --invert Invert the diff
1309 ** --json Output formatted as JSON
1310 ** -n|--linenum Show line numbers
@@ -1250,11 +1314,11 @@
1314 ** --strip-trailing-cr Strip trailing CR
1315 ** --tcl Tcl-formatted output used internally by --tk
1316 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1317 ** --tk Launch a Tcl/Tk GUI for display
1318 ** --to VERSION Select VERSION as target for the diff
1319 ** --undo Use the undo buffer as the baseline
1320 ** --unified Unified diff
1321 ** -v|--verbose Output complete text of added or deleted files
1322 ** -h|--versions Show compared versions in the diff header
1323 ** --webpage Format output as a stand-alone HTML webpage
1324 ** -W|--width N Width of lines in side-by-side diff
@@ -1267,10 +1331,11 @@
1331 const char *zCheckin; /* Check-in version number */
1332 const char *zBranch; /* Branch to diff */
1333 int againstUndo = 0; /* Diff against files in the undo buffer */
1334 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1335 DiffConfig DCfg; /* Diff configuration object */
1336 int bFromIsDir = 0; /* True if zFrom is a directory name */
1337
1338 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1339 diff_tk("diff", 2);
1340 return;
1341 }
@@ -1278,11 +1343,11 @@
1343 zFrom = find_option("from", "r", 1);
1344 zTo = find_option("to", 0, 1);
1345 zCheckin = find_option("checkin", "ci", 1);
1346 zBranch = find_option("branch", 0, 1);
1347 againstUndo = find_option("undo",0,0)!=0;
1348 if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1349 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
1350 " or --branch");
1351 }
1352 if( zBranch ){
1353 if( zTo || zFrom || zCheckin ){
@@ -1289,15 +1354,13 @@
1354 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1355 }
1356 zTo = zBranch;
1357 zFrom = mprintf("root:%s", zBranch);
1358 }
1359 if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
1360 fossil_fatal("cannot use --checkin together with --from or --to");
1361 }
 
 
1362 if( 0==zCheckin ){
1363 if( zTo==0 || againstUndo ){
1364 db_must_be_within_tree();
1365 }else if( zFrom==0 ){
1366 fossil_fatal("must use --from if --to is present");
@@ -1305,10 +1368,21 @@
1368 db_find_and_open_repository(0, 0);
1369 }
1370 }else{
1371 db_find_and_open_repository(0, 0);
1372 }
1373 determine_exec_relative_option(1);
1374 if( zFrom!=file_tail(zFrom)
1375 && file_isdir(zFrom, ExtFILE)==1
1376 && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
1377 ){
1378 bFromIsDir = 1;
1379 if( zTo ){
1380 fossil_fatal("cannot use --to together with \"--from PATH\"");
1381 }
1382 }
1383 diff_options(&DCfg, isGDiff, 0);
1384 verify_all_options();
1385 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
1386 if( g.argc>=3 ){
1387 int i;
1388 Blob fname;
@@ -1337,18 +1411,20 @@
1411 if( zFrom==0 ){
1412 fossil_fatal("check-in %s has no parent", zTo);
1413 }
1414 }
1415 diff_begin(&DCfg);
1416 if( bFromIsDir ){
1417 diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
1418 }else if( againstUndo ){
1419 if( db_lget_int("undo_available",0)==0 ){
1420 fossil_print("No undo or redo is available\n");
1421 return;
1422 }
1423 diff_undo_to_checkout(&DCfg, pFileDir);
1424 }else if( zTo==0 ){
1425 diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
1426 }else{
1427 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
1428 }
1429 if( pFileDir ){
1430 int i;
1431
+24 -13
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -28,48 +28,59 @@
2828
window.fossil.onPageLoad(function(){
2929
/**
3030
Adds toggle checkboxes to each file entry in the diff views for
3131
/info and similar pages.
3232
*/
33
+ if( !window.fossil.page.diffControlContainer ){
34
+ return;
35
+ }
3336
const D = window.fossil.dom;
34
- const allToggles = [/*collection of all diff-toggle checkboxes */];
37
+ const allToggles = [/*collection of all diff-toggle checkboxes*/];
38
+ let checkedCount =
39
+ 0 /* When showing more than one diff, keep track of how many
40
+ "show/hide" checkboxes are are checked so we can update the
41
+ "show/hide all" label dynamically. */;
42
+ let btnAll /* UI control to show/hide all diffs */;
43
+ /* Install a diff-toggle button for the given diff table element. */
3544
const addToggle = function(diffElem){
3645
const sib = diffElem.previousElementSibling,
3746
ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
3847
if(!sib) return;
3948
const lblToggle = D.label();
4049
D.append(lblToggle, ckbox, D.text(" show/hide "));
4150
const wrapper = D.append(D.span(), lblToggle);
4251
allToggles.push(ckbox);
52
+ ++checkedCount;
4353
D.append(sib, D.append(wrapper, lblToggle));
4454
ckbox.addEventListener('change', function(){
4555
diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
56
+ if(btnAll){
57
+ checkedCount += (this.checked ? 1 : -1);
58
+ btnAll.innerText = (checkedCount < allToggles.length)
59
+ ? "Show diffs" : "Hide diffs";
60
+ }
4661
}, false);
4762
};
4863
if( !document.querySelector('body.fdiff') ){
4964
/* Don't show the diff toggle button for /fdiff because it only
5065
has a single file to show (and also a different DOM layout). */
5166
document.querySelectorAll('table.diff').forEach(addToggle);
5267
}
68
+ /**
69
+ Set up a "toggle all diffs" button which toggles all of the
70
+ above-installed checkboxes, but only if more than one diff is
71
+ rendered.
72
+ */
5373
const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
5474
if(icm) {
55
- const btnAll = D.addClass(D.a("#", "Show/Hide"), "button");
75
+ btnAll = D.addClass(D.a("#", "Hide diffs"), "button");
5676
D.append( icm, btnAll );
5777
btnAll.addEventListener('click', function(ev){
5878
ev.preventDefault();
5979
ev.stopPropagation();
60
- /* Figure out whether we want to show all or hide all: if any diffs are
61
- toggled off, show all, else hide all. */
62
- let show = false;
63
- let ckbox;
64
- for( ckbox of allToggles ){
65
- if( !ckbox.checked ){
66
- show = true;
67
- break;
68
- }
69
- }
70
- for( ckbox of allToggles ){
80
+ const show = checkedCount < allToggles.length;
81
+ for( const ckbox of allToggles ){
7182
/* Toggle all entries to match this new state. We use click()
7283
instead of ckbox.checked=... so that the on-change event handler
7384
fires. */
7485
if(ckbox.checked!==show) ckbox.click();
7586
}
7687
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -28,48 +28,59 @@
28 window.fossil.onPageLoad(function(){
29 /**
30 Adds toggle checkboxes to each file entry in the diff views for
31 /info and similar pages.
32 */
 
 
 
33 const D = window.fossil.dom;
34 const allToggles = [/*collection of all diff-toggle checkboxes */];
 
 
 
 
 
 
35 const addToggle = function(diffElem){
36 const sib = diffElem.previousElementSibling,
37 ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
38 if(!sib) return;
39 const lblToggle = D.label();
40 D.append(lblToggle, ckbox, D.text(" show/hide "));
41 const wrapper = D.append(D.span(), lblToggle);
42 allToggles.push(ckbox);
 
43 D.append(sib, D.append(wrapper, lblToggle));
44 ckbox.addEventListener('change', function(){
45 diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
 
 
 
 
 
46 }, false);
47 };
48 if( !document.querySelector('body.fdiff') ){
49 /* Don't show the diff toggle button for /fdiff because it only
50 has a single file to show (and also a different DOM layout). */
51 document.querySelectorAll('table.diff').forEach(addToggle);
52 }
 
 
 
 
 
53 const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
54 if(icm) {
55 const btnAll = D.addClass(D.a("#", "Show/Hide"), "button");
56 D.append( icm, btnAll );
57 btnAll.addEventListener('click', function(ev){
58 ev.preventDefault();
59 ev.stopPropagation();
60 /* Figure out whether we want to show all or hide all: if any diffs are
61 toggled off, show all, else hide all. */
62 let show = false;
63 let ckbox;
64 for( ckbox of allToggles ){
65 if( !ckbox.checked ){
66 show = true;
67 break;
68 }
69 }
70 for( ckbox of allToggles ){
71 /* Toggle all entries to match this new state. We use click()
72 instead of ckbox.checked=... so that the on-change event handler
73 fires. */
74 if(ckbox.checked!==show) ckbox.click();
75 }
76
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -28,48 +28,59 @@
28 window.fossil.onPageLoad(function(){
29 /**
30 Adds toggle checkboxes to each file entry in the diff views for
31 /info and similar pages.
32 */
33 if( !window.fossil.page.diffControlContainer ){
34 return;
35 }
36 const D = window.fossil.dom;
37 const allToggles = [/*collection of all diff-toggle checkboxes*/];
38 let checkedCount =
39 0 /* When showing more than one diff, keep track of how many
40 "show/hide" checkboxes are are checked so we can update the
41 "show/hide all" label dynamically. */;
42 let btnAll /* UI control to show/hide all diffs */;
43 /* Install a diff-toggle button for the given diff table element. */
44 const addToggle = function(diffElem){
45 const sib = diffElem.previousElementSibling,
46 ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
47 if(!sib) return;
48 const lblToggle = D.label();
49 D.append(lblToggle, ckbox, D.text(" show/hide "));
50 const wrapper = D.append(D.span(), lblToggle);
51 allToggles.push(ckbox);
52 ++checkedCount;
53 D.append(sib, D.append(wrapper, lblToggle));
54 ckbox.addEventListener('change', function(){
55 diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
56 if(btnAll){
57 checkedCount += (this.checked ? 1 : -1);
58 btnAll.innerText = (checkedCount < allToggles.length)
59 ? "Show diffs" : "Hide diffs";
60 }
61 }, false);
62 };
63 if( !document.querySelector('body.fdiff') ){
64 /* Don't show the diff toggle button for /fdiff because it only
65 has a single file to show (and also a different DOM layout). */
66 document.querySelectorAll('table.diff').forEach(addToggle);
67 }
68 /**
69 Set up a "toggle all diffs" button which toggles all of the
70 above-installed checkboxes, but only if more than one diff is
71 rendered.
72 */
73 const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
74 if(icm) {
75 btnAll = D.addClass(D.a("#", "Hide diffs"), "button");
76 D.append( icm, btnAll );
77 btnAll.addEventListener('click', function(ev){
78 ev.preventDefault();
79 ev.stopPropagation();
80 const show = checkedCount < allToggles.length;
81 for( const ckbox of allToggles ){
 
 
 
 
 
 
 
 
 
82 /* Toggle all entries to match this new state. We use click()
83 instead of ckbox.checked=... so that the on-change event handler
84 fires. */
85 if(ckbox.checked!==show) ckbox.click();
86 }
87
+24 -13
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -28,48 +28,59 @@
2828
window.fossil.onPageLoad(function(){
2929
/**
3030
Adds toggle checkboxes to each file entry in the diff views for
3131
/info and similar pages.
3232
*/
33
+ if( !window.fossil.page.diffControlContainer ){
34
+ return;
35
+ }
3336
const D = window.fossil.dom;
34
- const allToggles = [/*collection of all diff-toggle checkboxes */];
37
+ const allToggles = [/*collection of all diff-toggle checkboxes*/];
38
+ let checkedCount =
39
+ 0 /* When showing more than one diff, keep track of how many
40
+ "show/hide" checkboxes are are checked so we can update the
41
+ "show/hide all" label dynamically. */;
42
+ let btnAll /* UI control to show/hide all diffs */;
43
+ /* Install a diff-toggle button for the given diff table element. */
3544
const addToggle = function(diffElem){
3645
const sib = diffElem.previousElementSibling,
3746
ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
3847
if(!sib) return;
3948
const lblToggle = D.label();
4049
D.append(lblToggle, ckbox, D.text(" show/hide "));
4150
const wrapper = D.append(D.span(), lblToggle);
4251
allToggles.push(ckbox);
52
+ ++checkedCount;
4353
D.append(sib, D.append(wrapper, lblToggle));
4454
ckbox.addEventListener('change', function(){
4555
diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
56
+ if(btnAll){
57
+ checkedCount += (this.checked ? 1 : -1);
58
+ btnAll.innerText = (checkedCount < allToggles.length)
59
+ ? "Show diffs" : "Hide diffs";
60
+ }
4661
}, false);
4762
};
4863
if( !document.querySelector('body.fdiff') ){
4964
/* Don't show the diff toggle button for /fdiff because it only
5065
has a single file to show (and also a different DOM layout). */
5166
document.querySelectorAll('table.diff').forEach(addToggle);
5267
}
68
+ /**
69
+ Set up a "toggle all diffs" button which toggles all of the
70
+ above-installed checkboxes, but only if more than one diff is
71
+ rendered.
72
+ */
5373
const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
5474
if(icm) {
55
- const btnAll = D.addClass(D.a("#", "Show/Hide"), "button");
75
+ btnAll = D.addClass(D.a("#", "Hide diffs"), "button");
5676
D.append( icm, btnAll );
5777
btnAll.addEventListener('click', function(ev){
5878
ev.preventDefault();
5979
ev.stopPropagation();
60
- /* Figure out whether we want to show all or hide all: if any diffs are
61
- toggled off, show all, else hide all. */
62
- let show = false;
63
- let ckbox;
64
- for( ckbox of allToggles ){
65
- if( !ckbox.checked ){
66
- show = true;
67
- break;
68
- }
69
- }
70
- for( ckbox of allToggles ){
80
+ const show = checkedCount < allToggles.length;
81
+ for( const ckbox of allToggles ){
7182
/* Toggle all entries to match this new state. We use click()
7283
instead of ckbox.checked=... so that the on-change event handler
7384
fires. */
7485
if(ckbox.checked!==show) ckbox.click();
7586
}
7687
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -28,48 +28,59 @@
28 window.fossil.onPageLoad(function(){
29 /**
30 Adds toggle checkboxes to each file entry in the diff views for
31 /info and similar pages.
32 */
 
 
 
33 const D = window.fossil.dom;
34 const allToggles = [/*collection of all diff-toggle checkboxes */];
 
 
 
 
 
 
35 const addToggle = function(diffElem){
36 const sib = diffElem.previousElementSibling,
37 ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
38 if(!sib) return;
39 const lblToggle = D.label();
40 D.append(lblToggle, ckbox, D.text(" show/hide "));
41 const wrapper = D.append(D.span(), lblToggle);
42 allToggles.push(ckbox);
 
43 D.append(sib, D.append(wrapper, lblToggle));
44 ckbox.addEventListener('change', function(){
45 diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
 
 
 
 
 
46 }, false);
47 };
48 if( !document.querySelector('body.fdiff') ){
49 /* Don't show the diff toggle button for /fdiff because it only
50 has a single file to show (and also a different DOM layout). */
51 document.querySelectorAll('table.diff').forEach(addToggle);
52 }
 
 
 
 
 
53 const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
54 if(icm) {
55 const btnAll = D.addClass(D.a("#", "Show/Hide"), "button");
56 D.append( icm, btnAll );
57 btnAll.addEventListener('click', function(ev){
58 ev.preventDefault();
59 ev.stopPropagation();
60 /* Figure out whether we want to show all or hide all: if any diffs are
61 toggled off, show all, else hide all. */
62 let show = false;
63 let ckbox;
64 for( ckbox of allToggles ){
65 if( !ckbox.checked ){
66 show = true;
67 break;
68 }
69 }
70 for( ckbox of allToggles ){
71 /* Toggle all entries to match this new state. We use click()
72 instead of ckbox.checked=... so that the on-change event handler
73 fires. */
74 if(ckbox.checked!==show) ckbox.click();
75 }
76
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -28,48 +28,59 @@
28 window.fossil.onPageLoad(function(){
29 /**
30 Adds toggle checkboxes to each file entry in the diff views for
31 /info and similar pages.
32 */
33 if( !window.fossil.page.diffControlContainer ){
34 return;
35 }
36 const D = window.fossil.dom;
37 const allToggles = [/*collection of all diff-toggle checkboxes*/];
38 let checkedCount =
39 0 /* When showing more than one diff, keep track of how many
40 "show/hide" checkboxes are are checked so we can update the
41 "show/hide all" label dynamically. */;
42 let btnAll /* UI control to show/hide all diffs */;
43 /* Install a diff-toggle button for the given diff table element. */
44 const addToggle = function(diffElem){
45 const sib = diffElem.previousElementSibling,
46 ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
47 if(!sib) return;
48 const lblToggle = D.label();
49 D.append(lblToggle, ckbox, D.text(" show/hide "));
50 const wrapper = D.append(D.span(), lblToggle);
51 allToggles.push(ckbox);
52 ++checkedCount;
53 D.append(sib, D.append(wrapper, lblToggle));
54 ckbox.addEventListener('change', function(){
55 diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
56 if(btnAll){
57 checkedCount += (this.checked ? 1 : -1);
58 btnAll.innerText = (checkedCount < allToggles.length)
59 ? "Show diffs" : "Hide diffs";
60 }
61 }, false);
62 };
63 if( !document.querySelector('body.fdiff') ){
64 /* Don't show the diff toggle button for /fdiff because it only
65 has a single file to show (and also a different DOM layout). */
66 document.querySelectorAll('table.diff').forEach(addToggle);
67 }
68 /**
69 Set up a "toggle all diffs" button which toggles all of the
70 above-installed checkboxes, but only if more than one diff is
71 rendered.
72 */
73 const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
74 if(icm) {
75 btnAll = D.addClass(D.a("#", "Hide diffs"), "button");
76 D.append( icm, btnAll );
77 btnAll.addEventListener('click', function(ev){
78 ev.preventDefault();
79 ev.stopPropagation();
80 const show = checkedCount < allToggles.length;
81 for( const ckbox of allToggles ){
 
 
 
 
 
 
 
 
 
82 /* Toggle all entries to match this new state. We use click()
83 instead of ckbox.checked=... so that the on-change event handler
84 fires. */
85 if(ckbox.checked!==show) ckbox.click();
86 }
87
+207 -59
--- src/info.c
+++ src/info.c
@@ -609,59 +609,25 @@
609609
db_finalize(&q);
610610
style_finish_page();
611611
}
612612
613613
/*
614
-** WEBPAGE: ckout
615
-**
616
-** Show information about the current checkout. This page only functions
617
-** if the web server is run on a loopback interface (in other words, was
618
-** started using "fossil ui" or similar) from with on open check-out.
614
+** Render a web-page diff of the changes in the working check-out
619615
*/
620
-void ckout_page(void){
621
- int vid;
622
- char *zHostname;
623
- char *zCwd;
616
+static void ckout_normal_diff(int vid){
624617
int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
625618
DiffConfig DCfg,*pCfg; /* Diff details */
626
- const char *zHome; /* Home directory */
627619
const char *zW; /* The "w" query parameter */
628620
int nChng; /* Number of changes */
629621
Stmt q;
630622
631
- if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
632
- cgi_redirectf("%R/home");
633
- return;
634
- }
635
- file_chdir(g.zLocalRoot, 0);
636623
diffType = preferred_diff_type();
637624
pCfg = construct_diff_flags(diffType, &DCfg);
638
- vid = db_lget_int("checkout", 0);
639
- db_unprotect(PROTECT_ALL);
640
- vfile_check_signature(vid, CKSIG_ENOTFILE);
641
- db_protect_pop();
642
- style_set_current_feature("vinfo");
643
- zHostname = fossil_hostname();
644
- zCwd = file_getcwd(0,0);
645
- zHome = fossil_getenv("HOME");
646
- if( zHome ){
647
- int nHome = (int)strlen(zHome);
648
- if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
649
- zCwd = mprintf("~%s", zCwd+nHome);
650
- }
651
- }
652
- if( zHostname ){
653
- style_header("Checkout Status: %h on %h", zCwd, zHostname);
654
- }else{
655
- style_header("Checkout Status: %h", zCwd);
656
- }
657
- render_checkin_context(vid, 0, 0, 0);
658625
nChng = db_int(0, "SELECT count(*) FROM vfile"
659626
" WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
660627
if( nChng==0 ){
661628
@ <p>No uncommitted changes</p>
662
- style_finish_page();
663629
return;
664630
}
665631
db_prepare(&q,
666632
/* 0 1 2 3 4 5 6 */
667633
"SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
@@ -674,11 +640,10 @@
674640
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
675641
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
676642
}else{
677643
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
678644
}
679
- @ <hr>
680645
@ <div class="sectionmenu info-changes-menu">
681646
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
682647
if( diffType!=1 ){
683648
@ %z(chref("button","%R?diff=1%s",zW))Unified&nbsp;Diff</a>
684649
}
@@ -750,12 +715,167 @@
750715
blob_reset(&old);
751716
blob_reset(&new);
752717
}
753718
}
754719
db_finalize(&q);
755
- // @ </div> <!-- ap-002 -->
720
+ append_diff_javascript(diffType);
721
+}
722
+
723
+/*
724
+** Render a web-page diff of the changes in the working check-out to
725
+** an external reference.
726
+*/
727
+static void ckout_external_base_diff(int vid, const char *zExBase){
728
+ int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
729
+ DiffConfig DCfg,*pCfg; /* Diff details */
730
+ const char *zW; /* The "w" query parameter */
731
+ Stmt q;
732
+
733
+ diffType = preferred_diff_type();
734
+ pCfg = construct_diff_flags(diffType, &DCfg);
735
+ db_prepare(&q,
736
+ "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid
737
+ );
738
+ if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
739
+ DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
740
+ }else{
741
+ DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
742
+ }
743
+ @ <div class="sectionmenu info-changes-menu">
744
+ zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
745
+ if( diffType!=1 ){
746
+ @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
747
+ @ Unified&nbsp;Diff</a>
748
+ }
749
+ if( diffType!=2 ){
750
+ @ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\
751
+ @ Side-by-Side&nbsp;Diff</a>
752
+ }
753
+ if( diffType!=0 ){
754
+ if( *zW ){
755
+ @ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\
756
+ @ Show&nbsp;Whitespace&nbsp;Changes</a>
757
+ }else{
758
+ @ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\
759
+ @ Ignore&nbsp;Whitespace</a>
760
+ }
761
+ }
762
+ @ </div>
763
+ while( db_step(&q)==SQLITE_ROW ){
764
+ const char *zFile; /* Name of file in the repository */
765
+ char *zLhs; /* Full name of left-hand side file */
766
+ char *zRhs; /* Full name of right-hand side file */
767
+ Blob rhs; /* Full text of RHS */
768
+ Blob lhs; /* Full text of LHS */
769
+
770
+ zFile = db_column_text(&q,0);
771
+ zLhs = mprintf("%s/%s", zExBase, zFile);
772
+ zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
773
+ if( file_size(zLhs, ExtFILE)<0 ){
774
+ @ <div class='file-change-line'><span>
775
+ @ Missing from external baseline: %h(zFile)
776
+ @ </span></div>
777
+ }else{
778
+ blob_read_from_file(&lhs, zLhs, ExtFILE);
779
+ blob_read_from_file(&rhs, zRhs, ExtFILE);
780
+ if( blob_size(&lhs)!=blob_size(&rhs)
781
+ || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
782
+ ){
783
+ @ <div class='file-change-line'><span>
784
+ @ Changes to %h(zFile)
785
+ @ </span></div>
786
+ if( pCfg ){
787
+ char *zFullFN;
788
+ char *zHexFN;
789
+ int nFullFN;
790
+ zFullFN = file_canonical_name_dup(zLhs);
791
+ nFullFN = (int)strlen(zFullFN);
792
+ zHexFN = fossil_malloc( nFullFN*2 + 5 );
793
+ zHexFN[0] = 'x';
794
+ encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN);
795
+ zHexFN[1+nFullFN*2] = 0;
796
+ fossil_free(zFullFN);
797
+ pCfg->zLeftHash = zHexFN;
798
+ text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
799
+ pCfg->zLeftHash = 0;
800
+ fossil_free(zHexFN);
801
+ }
802
+ }
803
+ blob_reset(&lhs);
804
+ blob_reset(&rhs);
805
+ }
806
+ fossil_free(zLhs);
807
+ fossil_free(zRhs);
808
+ }
809
+ db_finalize(&q);
756810
append_diff_javascript(diffType);
811
+}
812
+
813
+/*
814
+** WEBPAGE: ckout
815
+**
816
+** Show information about the current checkout. This page only functions
817
+** if the web server is run on a loopback interface (in other words, was
818
+** started using "fossil ui" or similar) from within an open check-out.
819
+**
820
+** If the "exbase=PATH" query parameter is provided, then the diff shown
821
+** uses the files in PATH as the baseline. This is the same as using
822
+** the "--from PATH" argument to the "fossil diff" command-line. In fact,
823
+** when using "fossil ui --from PATH", the --from argument becomes the value
824
+** of the exbase query parameter for the start page.
825
+**
826
+** Other query parameters related to diffs are also accepted.
827
+*/
828
+void ckout_page(void){
829
+ int vid;
830
+ const char *zHome; /* Home directory */
831
+ int nHome;
832
+ const char *zExBase;
833
+ char *zHostname;
834
+ char *zCwd;
835
+
836
+ if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
837
+ cgi_redirectf("%R/home");
838
+ return;
839
+ }
840
+ file_chdir(g.zLocalRoot, 0);
841
+ vid = db_lget_int("checkout", 0);
842
+ db_unprotect(PROTECT_ALL);
843
+ vfile_check_signature(vid, CKSIG_ENOTFILE);
844
+ db_protect_pop();
845
+ style_set_current_feature("vinfo");
846
+ zHostname = fossil_hostname();
847
+ zCwd = file_getcwd(0,0);
848
+ zHome = fossil_getenv("HOME");
849
+ if( zHome ){
850
+ nHome = (int)strlen(zHome);
851
+ if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
852
+ zCwd = mprintf("~%s", zCwd+nHome);
853
+ }
854
+ }else{
855
+ nHome = 0;
856
+ }
857
+ if( zHostname ){
858
+ style_header("Checkout Status: %h on %h", zCwd, zHostname);
859
+ }else{
860
+ style_header("Checkout Status: %h", zCwd);
861
+ }
862
+ render_checkin_context(vid, 0, 0, 0);
863
+ @ <hr>
864
+ zExBase = P("exbase");
865
+ if( zExBase && zExBase[0] ){
866
+ char *zCBase = file_canonical_name_dup(zExBase);
867
+ if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
868
+ @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
869
+ }else{
870
+ @ <p>Using external baseline: %h(zCBase)</p>
871
+ }
872
+ ckout_external_base_diff(vid, zCBase);
873
+ fossil_free(zCBase);
874
+ }else{
875
+ ckout_normal_diff(vid);
876
+ }
757877
style_finish_page();
758878
}
759879
760880
/*
761881
** WEBPAGE: vinfo
@@ -2077,10 +2197,16 @@
20772197
** WEBPAGE: jchunk hidden
20782198
** URL: /jchunk/HASH?from=N&to=M
20792199
**
20802200
** Return lines of text from a file as a JSON array - one entry in the
20812201
** array for each line of text.
2202
+**
2203
+** The HASH is normally a sha1 or sha3 hash that identifies an artifact
2204
+** in the BLOB table of the database. However, if HASH starts with an "x"
2205
+** and is followed by valid hexadecimal, and if we are running in a
2206
+** "fossil ui" situation (locally and with privilege), then decode the hex
2207
+** into a filename and read the file content from that name.
20822208
**
20832209
** **Warning:** This is an internal-use-only interface that is subject to
20842210
** change at any moment. External application should not use this interface
20852211
** since the application will break when this interface changes, and this
20862212
** interface will undoubtedly change.
@@ -2092,10 +2218,11 @@
20922218
** ajax_route_error().
20932219
*/
20942220
void jchunk_page(void){
20952221
int rid = 0;
20962222
const char *zName = PD("name", "");
2223
+ int nName = (int)(strlen(zName)&0x7fffffff);
20972224
int iFrom = atoi(PD("from","0"));
20982225
int iTo = atoi(PD("to","0"));
20992226
int ln;
21002227
int go = 1;
21012228
const char *zSep;
@@ -2112,36 +2239,57 @@
21122239
cgi_check_for_malice();
21132240
if( !g.perm.Read ){
21142241
ajax_route_error(403, "Access requires Read permissions.");
21152242
return;
21162243
}
2117
-#if 1
2118
- /* Re-enable this block once this code is integrated somewhere into
2119
- the UI. */
2120
- rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
2121
- if( rid==0 ){
2122
- ajax_route_error(404, "Unknown artifact: %h", zName);
2123
- return;
2124
- }
2125
-#else
2126
- /* This impl is only to simplify "manual" testing via the JS
2127
- console. */
2128
- rid = symbolic_name_to_rid(zName, "*");
2129
- if( rid==0 ){
2130
- ajax_route_error(404, "Unknown artifact: %h", zName);
2131
- return;
2132
- }else if( rid<0 ){
2133
- ajax_route_error(418, "Ambiguous artifact name: %h", zName);
2134
- return;
2135
- }
2136
-#endif
21372244
if( iFrom<1 || iTo<iFrom ){
21382245
ajax_route_error(500, "Invalid line range from=%d, to=%d.",
21392246
iFrom, iTo);
21402247
return;
21412248
}
2142
- content_get(rid, &content);
2249
+ if( zName[0]=='x'
2250
+ && ((nName-1)&1)==0
2251
+ && validate16(&zName[1],nName-1)
2252
+ && g.perm.Admin
2253
+ && db_open_local(0)
2254
+ && cgi_is_loopback(g.zIpAddr)
2255
+ ){
2256
+ /* Treat the HASH as a hex-encoded filename */
2257
+ int n = (nName-1)/2;
2258
+ char *zFN = fossil_malloc(n+1);
2259
+ decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
2260
+ zFN[n] = 0;
2261
+ if( file_size(zFN, ExtFILE)<0 ){
2262
+ blob_zero(&content);
2263
+ }else{
2264
+ blob_read_from_file(&content, zFN, ExtFILE);
2265
+ }
2266
+ fossil_free(zFN);
2267
+ }else{
2268
+ /* Treat the HASH as an artifact hash matching BLOB.UUID */
2269
+#if 1
2270
+ /* Re-enable this block once this code is integrated somewhere into
2271
+ the UI. */
2272
+ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
2273
+ if( rid==0 ){
2274
+ ajax_route_error(404, "Unknown artifact: %h", zName);
2275
+ return;
2276
+ }
2277
+#else
2278
+ /* This impl is only to simplify "manual" testing via the JS
2279
+ console. */
2280
+ rid = symbolic_name_to_rid(zName, "*");
2281
+ if( rid==0 ){
2282
+ ajax_route_error(404, "Unknown artifact: %h", zName);
2283
+ return;
2284
+ }else if( rid<0 ){
2285
+ ajax_route_error(418, "Ambiguous artifact name: %h", zName);
2286
+ return;
2287
+ }
2288
+#endif
2289
+ content_get(rid, &content);
2290
+ }
21432291
g.isConst = 1;
21442292
cgi_set_content_type("application/json");
21452293
ln = 0;
21462294
while( go && ln<iFrom ){
21472295
go = blob_line(&content, &line);
21482296
--- src/info.c
+++ src/info.c
@@ -609,59 +609,25 @@
609 db_finalize(&q);
610 style_finish_page();
611 }
612
613 /*
614 ** WEBPAGE: ckout
615 **
616 ** Show information about the current checkout. This page only functions
617 ** if the web server is run on a loopback interface (in other words, was
618 ** started using "fossil ui" or similar) from with on open check-out.
619 */
620 void ckout_page(void){
621 int vid;
622 char *zHostname;
623 char *zCwd;
624 int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
625 DiffConfig DCfg,*pCfg; /* Diff details */
626 const char *zHome; /* Home directory */
627 const char *zW; /* The "w" query parameter */
628 int nChng; /* Number of changes */
629 Stmt q;
630
631 if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
632 cgi_redirectf("%R/home");
633 return;
634 }
635 file_chdir(g.zLocalRoot, 0);
636 diffType = preferred_diff_type();
637 pCfg = construct_diff_flags(diffType, &DCfg);
638 vid = db_lget_int("checkout", 0);
639 db_unprotect(PROTECT_ALL);
640 vfile_check_signature(vid, CKSIG_ENOTFILE);
641 db_protect_pop();
642 style_set_current_feature("vinfo");
643 zHostname = fossil_hostname();
644 zCwd = file_getcwd(0,0);
645 zHome = fossil_getenv("HOME");
646 if( zHome ){
647 int nHome = (int)strlen(zHome);
648 if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
649 zCwd = mprintf("~%s", zCwd+nHome);
650 }
651 }
652 if( zHostname ){
653 style_header("Checkout Status: %h on %h", zCwd, zHostname);
654 }else{
655 style_header("Checkout Status: %h", zCwd);
656 }
657 render_checkin_context(vid, 0, 0, 0);
658 nChng = db_int(0, "SELECT count(*) FROM vfile"
659 " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
660 if( nChng==0 ){
661 @ <p>No uncommitted changes</p>
662 style_finish_page();
663 return;
664 }
665 db_prepare(&q,
666 /* 0 1 2 3 4 5 6 */
667 "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
@@ -674,11 +640,10 @@
674 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
675 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
676 }else{
677 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
678 }
679 @ <hr>
680 @ <div class="sectionmenu info-changes-menu">
681 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
682 if( diffType!=1 ){
683 @ %z(chref("button","%R?diff=1%s",zW))Unified&nbsp;Diff</a>
684 }
@@ -750,12 +715,167 @@
750 blob_reset(&old);
751 blob_reset(&new);
752 }
753 }
754 db_finalize(&q);
755 // @ </div> <!-- ap-002 -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
756 append_diff_javascript(diffType);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
757 style_finish_page();
758 }
759
760 /*
761 ** WEBPAGE: vinfo
@@ -2077,10 +2197,16 @@
2077 ** WEBPAGE: jchunk hidden
2078 ** URL: /jchunk/HASH?from=N&to=M
2079 **
2080 ** Return lines of text from a file as a JSON array - one entry in the
2081 ** array for each line of text.
 
 
 
 
 
 
2082 **
2083 ** **Warning:** This is an internal-use-only interface that is subject to
2084 ** change at any moment. External application should not use this interface
2085 ** since the application will break when this interface changes, and this
2086 ** interface will undoubtedly change.
@@ -2092,10 +2218,11 @@
2092 ** ajax_route_error().
2093 */
2094 void jchunk_page(void){
2095 int rid = 0;
2096 const char *zName = PD("name", "");
 
2097 int iFrom = atoi(PD("from","0"));
2098 int iTo = atoi(PD("to","0"));
2099 int ln;
2100 int go = 1;
2101 const char *zSep;
@@ -2112,36 +2239,57 @@
2112 cgi_check_for_malice();
2113 if( !g.perm.Read ){
2114 ajax_route_error(403, "Access requires Read permissions.");
2115 return;
2116 }
2117 #if 1
2118 /* Re-enable this block once this code is integrated somewhere into
2119 the UI. */
2120 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
2121 if( rid==0 ){
2122 ajax_route_error(404, "Unknown artifact: %h", zName);
2123 return;
2124 }
2125 #else
2126 /* This impl is only to simplify "manual" testing via the JS
2127 console. */
2128 rid = symbolic_name_to_rid(zName, "*");
2129 if( rid==0 ){
2130 ajax_route_error(404, "Unknown artifact: %h", zName);
2131 return;
2132 }else if( rid<0 ){
2133 ajax_route_error(418, "Ambiguous artifact name: %h", zName);
2134 return;
2135 }
2136 #endif
2137 if( iFrom<1 || iTo<iFrom ){
2138 ajax_route_error(500, "Invalid line range from=%d, to=%d.",
2139 iFrom, iTo);
2140 return;
2141 }
2142 content_get(rid, &content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2143 g.isConst = 1;
2144 cgi_set_content_type("application/json");
2145 ln = 0;
2146 while( go && ln<iFrom ){
2147 go = blob_line(&content, &line);
2148
--- src/info.c
+++ src/info.c
@@ -609,59 +609,25 @@
609 db_finalize(&q);
610 style_finish_page();
611 }
612
613 /*
614 ** Render a web-page diff of the changes in the working check-out
 
 
 
 
615 */
616 static void ckout_normal_diff(int vid){
 
 
 
617 int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
618 DiffConfig DCfg,*pCfg; /* Diff details */
 
619 const char *zW; /* The "w" query parameter */
620 int nChng; /* Number of changes */
621 Stmt q;
622
 
 
 
 
 
623 diffType = preferred_diff_type();
624 pCfg = construct_diff_flags(diffType, &DCfg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625 nChng = db_int(0, "SELECT count(*) FROM vfile"
626 " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
627 if( nChng==0 ){
628 @ <p>No uncommitted changes</p>
 
629 return;
630 }
631 db_prepare(&q,
632 /* 0 1 2 3 4 5 6 */
633 "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
@@ -674,11 +640,10 @@
640 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
641 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
642 }else{
643 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
644 }
 
645 @ <div class="sectionmenu info-changes-menu">
646 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
647 if( diffType!=1 ){
648 @ %z(chref("button","%R?diff=1%s",zW))Unified&nbsp;Diff</a>
649 }
@@ -750,12 +715,167 @@
715 blob_reset(&old);
716 blob_reset(&new);
717 }
718 }
719 db_finalize(&q);
720 append_diff_javascript(diffType);
721 }
722
723 /*
724 ** Render a web-page diff of the changes in the working check-out to
725 ** an external reference.
726 */
727 static void ckout_external_base_diff(int vid, const char *zExBase){
728 int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
729 DiffConfig DCfg,*pCfg; /* Diff details */
730 const char *zW; /* The "w" query parameter */
731 Stmt q;
732
733 diffType = preferred_diff_type();
734 pCfg = construct_diff_flags(diffType, &DCfg);
735 db_prepare(&q,
736 "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid
737 );
738 if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
739 DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
740 }else{
741 DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
742 }
743 @ <div class="sectionmenu info-changes-menu">
744 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
745 if( diffType!=1 ){
746 @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
747 @ Unified&nbsp;Diff</a>
748 }
749 if( diffType!=2 ){
750 @ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\
751 @ Side-by-Side&nbsp;Diff</a>
752 }
753 if( diffType!=0 ){
754 if( *zW ){
755 @ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\
756 @ Show&nbsp;Whitespace&nbsp;Changes</a>
757 }else{
758 @ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\
759 @ Ignore&nbsp;Whitespace</a>
760 }
761 }
762 @ </div>
763 while( db_step(&q)==SQLITE_ROW ){
764 const char *zFile; /* Name of file in the repository */
765 char *zLhs; /* Full name of left-hand side file */
766 char *zRhs; /* Full name of right-hand side file */
767 Blob rhs; /* Full text of RHS */
768 Blob lhs; /* Full text of LHS */
769
770 zFile = db_column_text(&q,0);
771 zLhs = mprintf("%s/%s", zExBase, zFile);
772 zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
773 if( file_size(zLhs, ExtFILE)<0 ){
774 @ <div class='file-change-line'><span>
775 @ Missing from external baseline: %h(zFile)
776 @ </span></div>
777 }else{
778 blob_read_from_file(&lhs, zLhs, ExtFILE);
779 blob_read_from_file(&rhs, zRhs, ExtFILE);
780 if( blob_size(&lhs)!=blob_size(&rhs)
781 || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
782 ){
783 @ <div class='file-change-line'><span>
784 @ Changes to %h(zFile)
785 @ </span></div>
786 if( pCfg ){
787 char *zFullFN;
788 char *zHexFN;
789 int nFullFN;
790 zFullFN = file_canonical_name_dup(zLhs);
791 nFullFN = (int)strlen(zFullFN);
792 zHexFN = fossil_malloc( nFullFN*2 + 5 );
793 zHexFN[0] = 'x';
794 encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN);
795 zHexFN[1+nFullFN*2] = 0;
796 fossil_free(zFullFN);
797 pCfg->zLeftHash = zHexFN;
798 text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
799 pCfg->zLeftHash = 0;
800 fossil_free(zHexFN);
801 }
802 }
803 blob_reset(&lhs);
804 blob_reset(&rhs);
805 }
806 fossil_free(zLhs);
807 fossil_free(zRhs);
808 }
809 db_finalize(&q);
810 append_diff_javascript(diffType);
811 }
812
813 /*
814 ** WEBPAGE: ckout
815 **
816 ** Show information about the current checkout. This page only functions
817 ** if the web server is run on a loopback interface (in other words, was
818 ** started using "fossil ui" or similar) from within an open check-out.
819 **
820 ** If the "exbase=PATH" query parameter is provided, then the diff shown
821 ** uses the files in PATH as the baseline. This is the same as using
822 ** the "--from PATH" argument to the "fossil diff" command-line. In fact,
823 ** when using "fossil ui --from PATH", the --from argument becomes the value
824 ** of the exbase query parameter for the start page.
825 **
826 ** Other query parameters related to diffs are also accepted.
827 */
828 void ckout_page(void){
829 int vid;
830 const char *zHome; /* Home directory */
831 int nHome;
832 const char *zExBase;
833 char *zHostname;
834 char *zCwd;
835
836 if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
837 cgi_redirectf("%R/home");
838 return;
839 }
840 file_chdir(g.zLocalRoot, 0);
841 vid = db_lget_int("checkout", 0);
842 db_unprotect(PROTECT_ALL);
843 vfile_check_signature(vid, CKSIG_ENOTFILE);
844 db_protect_pop();
845 style_set_current_feature("vinfo");
846 zHostname = fossil_hostname();
847 zCwd = file_getcwd(0,0);
848 zHome = fossil_getenv("HOME");
849 if( zHome ){
850 nHome = (int)strlen(zHome);
851 if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
852 zCwd = mprintf("~%s", zCwd+nHome);
853 }
854 }else{
855 nHome = 0;
856 }
857 if( zHostname ){
858 style_header("Checkout Status: %h on %h", zCwd, zHostname);
859 }else{
860 style_header("Checkout Status: %h", zCwd);
861 }
862 render_checkin_context(vid, 0, 0, 0);
863 @ <hr>
864 zExBase = P("exbase");
865 if( zExBase && zExBase[0] ){
866 char *zCBase = file_canonical_name_dup(zExBase);
867 if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
868 @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
869 }else{
870 @ <p>Using external baseline: %h(zCBase)</p>
871 }
872 ckout_external_base_diff(vid, zCBase);
873 fossil_free(zCBase);
874 }else{
875 ckout_normal_diff(vid);
876 }
877 style_finish_page();
878 }
879
880 /*
881 ** WEBPAGE: vinfo
@@ -2077,10 +2197,16 @@
2197 ** WEBPAGE: jchunk hidden
2198 ** URL: /jchunk/HASH?from=N&to=M
2199 **
2200 ** Return lines of text from a file as a JSON array - one entry in the
2201 ** array for each line of text.
2202 **
2203 ** The HASH is normally a sha1 or sha3 hash that identifies an artifact
2204 ** in the BLOB table of the database. However, if HASH starts with an "x"
2205 ** and is followed by valid hexadecimal, and if we are running in a
2206 ** "fossil ui" situation (locally and with privilege), then decode the hex
2207 ** into a filename and read the file content from that name.
2208 **
2209 ** **Warning:** This is an internal-use-only interface that is subject to
2210 ** change at any moment. External application should not use this interface
2211 ** since the application will break when this interface changes, and this
2212 ** interface will undoubtedly change.
@@ -2092,10 +2218,11 @@
2218 ** ajax_route_error().
2219 */
2220 void jchunk_page(void){
2221 int rid = 0;
2222 const char *zName = PD("name", "");
2223 int nName = (int)(strlen(zName)&0x7fffffff);
2224 int iFrom = atoi(PD("from","0"));
2225 int iTo = atoi(PD("to","0"));
2226 int ln;
2227 int go = 1;
2228 const char *zSep;
@@ -2112,36 +2239,57 @@
2239 cgi_check_for_malice();
2240 if( !g.perm.Read ){
2241 ajax_route_error(403, "Access requires Read permissions.");
2242 return;
2243 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2244 if( iFrom<1 || iTo<iFrom ){
2245 ajax_route_error(500, "Invalid line range from=%d, to=%d.",
2246 iFrom, iTo);
2247 return;
2248 }
2249 if( zName[0]=='x'
2250 && ((nName-1)&1)==0
2251 && validate16(&zName[1],nName-1)
2252 && g.perm.Admin
2253 && db_open_local(0)
2254 && cgi_is_loopback(g.zIpAddr)
2255 ){
2256 /* Treat the HASH as a hex-encoded filename */
2257 int n = (nName-1)/2;
2258 char *zFN = fossil_malloc(n+1);
2259 decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
2260 zFN[n] = 0;
2261 if( file_size(zFN, ExtFILE)<0 ){
2262 blob_zero(&content);
2263 }else{
2264 blob_read_from_file(&content, zFN, ExtFILE);
2265 }
2266 fossil_free(zFN);
2267 }else{
2268 /* Treat the HASH as an artifact hash matching BLOB.UUID */
2269 #if 1
2270 /* Re-enable this block once this code is integrated somewhere into
2271 the UI. */
2272 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
2273 if( rid==0 ){
2274 ajax_route_error(404, "Unknown artifact: %h", zName);
2275 return;
2276 }
2277 #else
2278 /* This impl is only to simplify "manual" testing via the JS
2279 console. */
2280 rid = symbolic_name_to_rid(zName, "*");
2281 if( rid==0 ){
2282 ajax_route_error(404, "Unknown artifact: %h", zName);
2283 return;
2284 }else if( rid<0 ){
2285 ajax_route_error(418, "Ambiguous artifact name: %h", zName);
2286 return;
2287 }
2288 #endif
2289 content_get(rid, &content);
2290 }
2291 g.isConst = 1;
2292 cgi_set_content_type("application/json");
2293 ln = 0;
2294 while( go && ln<iFrom ){
2295 go = blob_line(&content, &line);
2296
+10
--- src/main.c
+++ src/main.c
@@ -3178,10 +3178,11 @@
31783178
** --errorlog FILE Append HTTP error messages to FILE
31793179
** --extroot DIR Document root for the /ext extension mechanism
31803180
** --files GLOBLIST Comma-separated list of glob patterns for static files
31813181
** --fossilcmd PATH The pathname of the "fossil" executable on the remote
31823182
** system when REPOSITORY is remote.
3183
+** --from PATH Use PATH as the diff baseline for the /ckout page
31833184
** --localauth Enable automatic login for requests from localhost
31843185
** --localhost Listen on 127.0.0.1 only (always true for "ui")
31853186
** --https Indicates that the input is coming through a reverse
31863187
** proxy that has already translated HTTPS into HTTP.
31873188
** --jsmode MODE Determine how JavaScript is delivered with pages.
@@ -3250,10 +3251,11 @@
32503251
const char *zInitPage = 0; /* Start on this page. --page option */
32513252
int findServerArg = 2; /* argv index for find_server_repository() */
32523253
char *zRemote = 0; /* Remote host on which to run "fossil ui" */
32533254
const char *zJsMode; /* The --jsmode parameter */
32543255
const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3256
+ const char *zFrom; /* Value for --from */
32553257
32563258
32573259
#if USE_SEE
32583260
db_setup_for_saved_encryption_key();
32593261
#endif
@@ -3286,13 +3288,21 @@
32863288
g.useLocalauth = find_option("localauth", 0, 0)!=0;
32873289
Th_InitTraceLog();
32883290
zPort = find_option("port", "P", 1);
32893291
isUiCmd = g.argv[1][0]=='u';
32903292
if( isUiCmd ){
3293
+ zFrom = find_option("from", 0, 1);
3294
+ if( zFrom && zFrom==file_tail(zFrom) ){
3295
+ fossil_fatal("the argument to --from must be a pathname for"
3296
+ " the \"ui\" command");
3297
+ }
32913298
zInitPage = find_option("page", "p", 1);
32923299
if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
32933300
zFossilCmd = find_option("fossilcmd", 0, 1);
3301
+ if( zFrom && zInitPage==0 ){
3302
+ zInitPage = mprintf("ckout?exbase=%T", zFrom);
3303
+ }
32943304
}
32953305
zNotFound = find_option("notfound", 0, 1);
32963306
allowRepoList = find_option("repolist",0,0)!=0;
32973307
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
32983308
zAltBase = find_option("baseurl", 0, 1);
32993309
--- src/main.c
+++ src/main.c
@@ -3178,10 +3178,11 @@
3178 ** --errorlog FILE Append HTTP error messages to FILE
3179 ** --extroot DIR Document root for the /ext extension mechanism
3180 ** --files GLOBLIST Comma-separated list of glob patterns for static files
3181 ** --fossilcmd PATH The pathname of the "fossil" executable on the remote
3182 ** system when REPOSITORY is remote.
 
3183 ** --localauth Enable automatic login for requests from localhost
3184 ** --localhost Listen on 127.0.0.1 only (always true for "ui")
3185 ** --https Indicates that the input is coming through a reverse
3186 ** proxy that has already translated HTTPS into HTTP.
3187 ** --jsmode MODE Determine how JavaScript is delivered with pages.
@@ -3250,10 +3251,11 @@
3250 const char *zInitPage = 0; /* Start on this page. --page option */
3251 int findServerArg = 2; /* argv index for find_server_repository() */
3252 char *zRemote = 0; /* Remote host on which to run "fossil ui" */
3253 const char *zJsMode; /* The --jsmode parameter */
3254 const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
 
3255
3256
3257 #if USE_SEE
3258 db_setup_for_saved_encryption_key();
3259 #endif
@@ -3286,13 +3288,21 @@
3286 g.useLocalauth = find_option("localauth", 0, 0)!=0;
3287 Th_InitTraceLog();
3288 zPort = find_option("port", "P", 1);
3289 isUiCmd = g.argv[1][0]=='u';
3290 if( isUiCmd ){
 
 
 
 
 
3291 zInitPage = find_option("page", "p", 1);
3292 if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3293 zFossilCmd = find_option("fossilcmd", 0, 1);
 
 
 
3294 }
3295 zNotFound = find_option("notfound", 0, 1);
3296 allowRepoList = find_option("repolist",0,0)!=0;
3297 if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
3298 zAltBase = find_option("baseurl", 0, 1);
3299
--- src/main.c
+++ src/main.c
@@ -3178,10 +3178,11 @@
3178 ** --errorlog FILE Append HTTP error messages to FILE
3179 ** --extroot DIR Document root for the /ext extension mechanism
3180 ** --files GLOBLIST Comma-separated list of glob patterns for static files
3181 ** --fossilcmd PATH The pathname of the "fossil" executable on the remote
3182 ** system when REPOSITORY is remote.
3183 ** --from PATH Use PATH as the diff baseline for the /ckout page
3184 ** --localauth Enable automatic login for requests from localhost
3185 ** --localhost Listen on 127.0.0.1 only (always true for "ui")
3186 ** --https Indicates that the input is coming through a reverse
3187 ** proxy that has already translated HTTPS into HTTP.
3188 ** --jsmode MODE Determine how JavaScript is delivered with pages.
@@ -3250,10 +3251,11 @@
3251 const char *zInitPage = 0; /* Start on this page. --page option */
3252 int findServerArg = 2; /* argv index for find_server_repository() */
3253 char *zRemote = 0; /* Remote host on which to run "fossil ui" */
3254 const char *zJsMode; /* The --jsmode parameter */
3255 const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3256 const char *zFrom; /* Value for --from */
3257
3258
3259 #if USE_SEE
3260 db_setup_for_saved_encryption_key();
3261 #endif
@@ -3286,13 +3288,21 @@
3288 g.useLocalauth = find_option("localauth", 0, 0)!=0;
3289 Th_InitTraceLog();
3290 zPort = find_option("port", "P", 1);
3291 isUiCmd = g.argv[1][0]=='u';
3292 if( isUiCmd ){
3293 zFrom = find_option("from", 0, 1);
3294 if( zFrom && zFrom==file_tail(zFrom) ){
3295 fossil_fatal("the argument to --from must be a pathname for"
3296 " the \"ui\" command");
3297 }
3298 zInitPage = find_option("page", "p", 1);
3299 if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3300 zFossilCmd = find_option("fossilcmd", 0, 1);
3301 if( zFrom && zInitPage==0 ){
3302 zInitPage = mprintf("ckout?exbase=%T", zFrom);
3303 }
3304 }
3305 zNotFound = find_option("notfound", 0, 1);
3306 allowRepoList = find_option("repolist",0,0)!=0;
3307 if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
3308 zAltBase = find_option("baseurl", 0, 1);
3309
+12 -8
--- src/merge.c
+++ src/merge.c
@@ -687,10 +687,11 @@
687687
** -K|--keep-merge-files On merge conflict, retain the temporary files
688688
** used for merging, named *-baseline, *-original,
689689
** and *-merge.
690690
** -n|--dry-run Do not actually change files on disk
691691
** --nosync Do not auto-sync prior to merging
692
+** --noundo Do not record changes in the undo log
692693
** -v|--verbose Show additional details of the merge
693694
*/
694695
void merge_cmd(void){
695696
int vid; /* Current version "V" */
696697
int mid; /* Version we are merging from "M" */
@@ -712,10 +713,11 @@
712713
int nOverwrite = 0; /* Number of unmanaged files overwritten */
713714
char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
714715
const char *zVersion; /* The VERSION argument */
715716
int bMultiMerge = 0; /* True if there are two or more VERSION arguments */
716717
int nMerge = 0; /* Number of prior merges processed */
718
+ int useUndo = 1; /* True to record changes in the undo log */
717719
Stmt q; /* SQL statment used for merge processing */
718720
719721
720722
/* Notation:
721723
**
@@ -760,10 +762,12 @@
760762
** * The --dry-run option is also useful in combination with --debug.
761763
*/
762764
debugFlag = find_option("debug",0,0)!=0;
763765
if( debugFlag && verboseFlag ) debugFlag = 2;
764766
showVfileFlag = find_option("show-vfile",0,0)!=0;
767
+ useUndo = find_option("noundo",0,0)==0;
768
+ if( dryRunFlag ) useUndo = 0;
765769
766770
verify_all_options();
767771
db_must_be_within_tree();
768772
if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
769773
vid = db_lget_int("checkout", 0);
@@ -926,11 +930,11 @@
926930
integrateFlag ? "integrate:" : "merge-from:");
927931
print_checkin_description(pid, 12, "baseline:");
928932
}
929933
vfile_check_signature(vid, CKSIG_ENOTFILE);
930934
if( nMerge==0 ) db_begin_transaction();
931
- if( !dryRunFlag ) undo_begin();
935
+ if( useUndo ) undo_begin();
932936
if( load_vfile_from_rid(mid) && !forceMissingFlag ){
933937
fossil_fatal("missing content, unable to merge");
934938
}
935939
if( load_vfile_from_rid(pid) && !forceMissingFlag ){
936940
fossil_fatal("missing content, unable to merge");
@@ -1183,12 +1187,12 @@
11831187
int ridm = db_column_int(&q, 1);
11841188
const char *zName = db_column_text(&q, 2);
11851189
int islinkm = db_column_int(&q, 3);
11861190
/* Copy content from idm over into idv. Overwrite idv. */
11871191
fossil_print("UPDATE %s\n", zName);
1192
+ if( useUndo ) undo_save(zName);
11881193
if( !dryRunFlag ){
1189
- undo_save(zName);
11901194
db_multi_exec(
11911195
"UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
11921196
" mhash=CASE WHEN rid<>%d"
11931197
" THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
11941198
" WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
@@ -1265,11 +1269,11 @@
12651269
}else{
12661270
i64 sz;
12671271
const char *zErrMsg = 0;
12681272
int nc = 0;
12691273
1270
- if( !dryRunFlag ) undo_save(zName);
1274
+ if( useUndo ) undo_save(zName);
12711275
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
12721276
sz = file_size(zFullPath, ExtFILE);
12731277
content_get(ridp, &p);
12741278
content_get(ridm, &m);
12751279
if( isBinary ){
@@ -1352,11 +1356,11 @@
13521356
zErrMsg = "local edits lost";
13531357
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
13541358
sz = file_size(zFullPath, ExtFILE);
13551359
fossil_free(zFullPath);
13561360
}
1357
- if( !dryRunFlag ) undo_save(zName);
1361
+ if( useUndo ) undo_save(zName);
13581362
db_multi_exec(
13591363
"UPDATE vfile SET deleted=1 WHERE id=%d", idv
13601364
);
13611365
if( !dryRunFlag ){
13621366
char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
@@ -1399,12 +1403,12 @@
13991403
int idv = db_column_int(&q, 0);
14001404
const char *zOldName = db_column_text(&q, 1);
14011405
const char *zNewName = db_column_text(&q, 2);
14021406
int isExe = db_column_int(&q, 3);
14031407
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
1404
- if( !dryRunFlag ) undo_save(zOldName);
1405
- if( !dryRunFlag ) undo_save(zNewName);
1408
+ if( useUndo ) undo_save(zOldName);
1409
+ if( useUndo ) undo_save(zNewName);
14061410
db_multi_exec(
14071411
"UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
14081412
zOldName
14091413
);
14101414
db_multi_exec(
@@ -1498,12 +1502,12 @@
14981502
"VALUES('ADDED',%Q,%d,%Q)",
14991503
/* fnm */ zName,
15001504
/* ridm */ db_column_int(&q,2),
15011505
/* fnr */ zName
15021506
);
1507
+ if( useUndo ) undo_save(zName);
15031508
if( !dryRunFlag ){
1504
- undo_save(zName);
15051509
vfile_to_disk(0, idm, 0, 0);
15061510
}
15071511
}
15081512
db_finalize(&q);
15091513
@@ -1558,9 +1562,9 @@
15581562
}
15591563
if( bMultiMerge && nConflict==0 ){
15601564
nMerge++;
15611565
goto merge_next_child;
15621566
}
1563
- if( !dryRunFlag ) undo_finish();
1567
+ if( useUndo ) undo_finish();
15641568
15651569
db_end_transaction(dryRunFlag);
15661570
}
15671571
--- src/merge.c
+++ src/merge.c
@@ -687,10 +687,11 @@
687 ** -K|--keep-merge-files On merge conflict, retain the temporary files
688 ** used for merging, named *-baseline, *-original,
689 ** and *-merge.
690 ** -n|--dry-run Do not actually change files on disk
691 ** --nosync Do not auto-sync prior to merging
 
692 ** -v|--verbose Show additional details of the merge
693 */
694 void merge_cmd(void){
695 int vid; /* Current version "V" */
696 int mid; /* Version we are merging from "M" */
@@ -712,10 +713,11 @@
712 int nOverwrite = 0; /* Number of unmanaged files overwritten */
713 char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
714 const char *zVersion; /* The VERSION argument */
715 int bMultiMerge = 0; /* True if there are two or more VERSION arguments */
716 int nMerge = 0; /* Number of prior merges processed */
 
717 Stmt q; /* SQL statment used for merge processing */
718
719
720 /* Notation:
721 **
@@ -760,10 +762,12 @@
760 ** * The --dry-run option is also useful in combination with --debug.
761 */
762 debugFlag = find_option("debug",0,0)!=0;
763 if( debugFlag && verboseFlag ) debugFlag = 2;
764 showVfileFlag = find_option("show-vfile",0,0)!=0;
 
 
765
766 verify_all_options();
767 db_must_be_within_tree();
768 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
769 vid = db_lget_int("checkout", 0);
@@ -926,11 +930,11 @@
926 integrateFlag ? "integrate:" : "merge-from:");
927 print_checkin_description(pid, 12, "baseline:");
928 }
929 vfile_check_signature(vid, CKSIG_ENOTFILE);
930 if( nMerge==0 ) db_begin_transaction();
931 if( !dryRunFlag ) undo_begin();
932 if( load_vfile_from_rid(mid) && !forceMissingFlag ){
933 fossil_fatal("missing content, unable to merge");
934 }
935 if( load_vfile_from_rid(pid) && !forceMissingFlag ){
936 fossil_fatal("missing content, unable to merge");
@@ -1183,12 +1187,12 @@
1183 int ridm = db_column_int(&q, 1);
1184 const char *zName = db_column_text(&q, 2);
1185 int islinkm = db_column_int(&q, 3);
1186 /* Copy content from idm over into idv. Overwrite idv. */
1187 fossil_print("UPDATE %s\n", zName);
 
1188 if( !dryRunFlag ){
1189 undo_save(zName);
1190 db_multi_exec(
1191 "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
1192 " mhash=CASE WHEN rid<>%d"
1193 " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
1194 " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
@@ -1265,11 +1269,11 @@
1265 }else{
1266 i64 sz;
1267 const char *zErrMsg = 0;
1268 int nc = 0;
1269
1270 if( !dryRunFlag ) undo_save(zName);
1271 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1272 sz = file_size(zFullPath, ExtFILE);
1273 content_get(ridp, &p);
1274 content_get(ridm, &m);
1275 if( isBinary ){
@@ -1352,11 +1356,11 @@
1352 zErrMsg = "local edits lost";
1353 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1354 sz = file_size(zFullPath, ExtFILE);
1355 fossil_free(zFullPath);
1356 }
1357 if( !dryRunFlag ) undo_save(zName);
1358 db_multi_exec(
1359 "UPDATE vfile SET deleted=1 WHERE id=%d", idv
1360 );
1361 if( !dryRunFlag ){
1362 char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
@@ -1399,12 +1403,12 @@
1399 int idv = db_column_int(&q, 0);
1400 const char *zOldName = db_column_text(&q, 1);
1401 const char *zNewName = db_column_text(&q, 2);
1402 int isExe = db_column_int(&q, 3);
1403 fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
1404 if( !dryRunFlag ) undo_save(zOldName);
1405 if( !dryRunFlag ) undo_save(zNewName);
1406 db_multi_exec(
1407 "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
1408 zOldName
1409 );
1410 db_multi_exec(
@@ -1498,12 +1502,12 @@
1498 "VALUES('ADDED',%Q,%d,%Q)",
1499 /* fnm */ zName,
1500 /* ridm */ db_column_int(&q,2),
1501 /* fnr */ zName
1502 );
 
1503 if( !dryRunFlag ){
1504 undo_save(zName);
1505 vfile_to_disk(0, idm, 0, 0);
1506 }
1507 }
1508 db_finalize(&q);
1509
@@ -1558,9 +1562,9 @@
1558 }
1559 if( bMultiMerge && nConflict==0 ){
1560 nMerge++;
1561 goto merge_next_child;
1562 }
1563 if( !dryRunFlag ) undo_finish();
1564
1565 db_end_transaction(dryRunFlag);
1566 }
1567
--- src/merge.c
+++ src/merge.c
@@ -687,10 +687,11 @@
687 ** -K|--keep-merge-files On merge conflict, retain the temporary files
688 ** used for merging, named *-baseline, *-original,
689 ** and *-merge.
690 ** -n|--dry-run Do not actually change files on disk
691 ** --nosync Do not auto-sync prior to merging
692 ** --noundo Do not record changes in the undo log
693 ** -v|--verbose Show additional details of the merge
694 */
695 void merge_cmd(void){
696 int vid; /* Current version "V" */
697 int mid; /* Version we are merging from "M" */
@@ -712,10 +713,11 @@
713 int nOverwrite = 0; /* Number of unmanaged files overwritten */
714 char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
715 const char *zVersion; /* The VERSION argument */
716 int bMultiMerge = 0; /* True if there are two or more VERSION arguments */
717 int nMerge = 0; /* Number of prior merges processed */
718 int useUndo = 1; /* True to record changes in the undo log */
719 Stmt q; /* SQL statment used for merge processing */
720
721
722 /* Notation:
723 **
@@ -760,10 +762,12 @@
762 ** * The --dry-run option is also useful in combination with --debug.
763 */
764 debugFlag = find_option("debug",0,0)!=0;
765 if( debugFlag && verboseFlag ) debugFlag = 2;
766 showVfileFlag = find_option("show-vfile",0,0)!=0;
767 useUndo = find_option("noundo",0,0)==0;
768 if( dryRunFlag ) useUndo = 0;
769
770 verify_all_options();
771 db_must_be_within_tree();
772 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
773 vid = db_lget_int("checkout", 0);
@@ -926,11 +930,11 @@
930 integrateFlag ? "integrate:" : "merge-from:");
931 print_checkin_description(pid, 12, "baseline:");
932 }
933 vfile_check_signature(vid, CKSIG_ENOTFILE);
934 if( nMerge==0 ) db_begin_transaction();
935 if( useUndo ) undo_begin();
936 if( load_vfile_from_rid(mid) && !forceMissingFlag ){
937 fossil_fatal("missing content, unable to merge");
938 }
939 if( load_vfile_from_rid(pid) && !forceMissingFlag ){
940 fossil_fatal("missing content, unable to merge");
@@ -1183,12 +1187,12 @@
1187 int ridm = db_column_int(&q, 1);
1188 const char *zName = db_column_text(&q, 2);
1189 int islinkm = db_column_int(&q, 3);
1190 /* Copy content from idm over into idv. Overwrite idv. */
1191 fossil_print("UPDATE %s\n", zName);
1192 if( useUndo ) undo_save(zName);
1193 if( !dryRunFlag ){
 
1194 db_multi_exec(
1195 "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
1196 " mhash=CASE WHEN rid<>%d"
1197 " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
1198 " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
@@ -1265,11 +1269,11 @@
1269 }else{
1270 i64 sz;
1271 const char *zErrMsg = 0;
1272 int nc = 0;
1273
1274 if( useUndo ) undo_save(zName);
1275 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1276 sz = file_size(zFullPath, ExtFILE);
1277 content_get(ridp, &p);
1278 content_get(ridm, &m);
1279 if( isBinary ){
@@ -1352,11 +1356,11 @@
1356 zErrMsg = "local edits lost";
1357 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1358 sz = file_size(zFullPath, ExtFILE);
1359 fossil_free(zFullPath);
1360 }
1361 if( useUndo ) undo_save(zName);
1362 db_multi_exec(
1363 "UPDATE vfile SET deleted=1 WHERE id=%d", idv
1364 );
1365 if( !dryRunFlag ){
1366 char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
@@ -1399,12 +1403,12 @@
1403 int idv = db_column_int(&q, 0);
1404 const char *zOldName = db_column_text(&q, 1);
1405 const char *zNewName = db_column_text(&q, 2);
1406 int isExe = db_column_int(&q, 3);
1407 fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
1408 if( useUndo ) undo_save(zOldName);
1409 if( useUndo ) undo_save(zNewName);
1410 db_multi_exec(
1411 "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
1412 zOldName
1413 );
1414 db_multi_exec(
@@ -1498,12 +1502,12 @@
1502 "VALUES('ADDED',%Q,%d,%Q)",
1503 /* fnm */ zName,
1504 /* ridm */ db_column_int(&q,2),
1505 /* fnr */ zName
1506 );
1507 if( useUndo ) undo_save(zName);
1508 if( !dryRunFlag ){
 
1509 vfile_to_disk(0, idm, 0, 0);
1510 }
1511 }
1512 db_finalize(&q);
1513
@@ -1558,9 +1562,9 @@
1562 }
1563 if( bMultiMerge && nConflict==0 ){
1564 nMerge++;
1565 goto merge_next_child;
1566 }
1567 if( useUndo ) undo_finish();
1568
1569 db_end_transaction(dryRunFlag);
1570 }
1571
+42 -3
--- src/patch.c
+++ src/patch.c
@@ -387,11 +387,11 @@
387387
if( unsaved_changes(0) ){
388388
if( (mFlags & PATCH_FORCE)==0 ){
389389
fossil_fatal("Cannot apply patch: there are unsaved changes "
390390
"in the current check-out");
391391
}else{
392
- blob_appendf(&cmd, "%$ revert", g.nameOfExe);
392
+ blob_appendf(&cmd, "%$ revert --noundo", g.nameOfExe);
393393
if( mFlags & PATCH_DRYRUN ){
394394
fossil_print("%s\n", blob_str(&cmd));
395395
}else{
396396
int rc = fossil_system(blob_str(&cmd));
397397
if( rc ){
@@ -429,23 +429,27 @@
429429
}
430430
}
431431
}
432432
blob_reset(&cmd);
433433
if( db_table_exists("patch","patchmerge") ){
434
+ int nMerge = 0;
434435
db_prepare(&q,
435436
"SELECT type, mhash, upper(type) FROM patch.patchmerge"
436437
" WHERE type IN ('merge','cherrypick','backout','integrate')"
437438
" AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
438439
);
439440
while( db_step(&q)==SQLITE_ROW ){
440441
const char *zType = db_column_text(&q,0);
441442
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
442443
if( strcmp(zType,"merge")==0 ){
443
- blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));
444
+ blob_appendf(&cmd, " merge --noundo --nosync %s\n",
445
+ db_column_text(&q,1));
444446
}else{
445
- blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));
447
+ blob_appendf(&cmd, " merge --%s --noundo --nosync %s\n",
448
+ zType, db_column_text(&q,1));
446449
}
450
+ nMerge++;
447451
if( mFlags & PATCH_VERBOSE ){
448452
fossil_print("%-10s %s\n", db_column_text(&q,2),
449453
db_column_text(&q,0));
450454
}
451455
}
@@ -458,10 +462,45 @@
458462
fossil_fatal("unable to do merges:\n%s",
459463
blob_str(&cmd));
460464
}
461465
}
462466
blob_reset(&cmd);
467
+
468
+ /* 2024-12-16 https://fossil-scm.org/home/forumpost/51a37054
469
+ ** If one or more merge operations occurred in the patch and there are
470
+ ** files that are marked as "chnged' in the local VFILE but which
471
+ ** are not mentioned as having been modified in the patch, then
472
+ ** revert those files.
473
+ */
474
+ if( nMerge ){
475
+ int vid = db_lget_int("checkout", 0);
476
+ int nRevert = 0;
477
+ blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
478
+ blob_appendf(&cmd, " revert --noundo ");
479
+ db_prepare(&q,
480
+ "SELECT pathname FROM vfile WHERE vid=%d AND chnged "
481
+ "EXCEPT SELECT pathname FROM chng",
482
+ vid
483
+ );
484
+ while( db_step(&q)==SQLITE_ROW ){
485
+ blob_append_escaped_arg(&cmd, db_column_text(&q,0), 1);
486
+ nRevert++;
487
+ }
488
+ db_finalize(&q);
489
+ if( nRevert ){
490
+ if( mFlags & PATCH_DRYRUN ){
491
+ fossil_print("%s", blob_str(&cmd));
492
+ }else{
493
+ int rc = fossil_unsafe_system(blob_str(&cmd));
494
+ if( rc ){
495
+ fossil_fatal("unable to do reverts:\n%s",
496
+ blob_str(&cmd));
497
+ }
498
+ }
499
+ }
500
+ blob_reset(&cmd);
501
+ }
463502
}
464503
465504
/* Deletions */
466505
db_prepare(&q, "SELECT pathname FROM patch.chng"
467506
" WHERE origname IS NULL AND delta IS NULL");
468507
--- src/patch.c
+++ src/patch.c
@@ -387,11 +387,11 @@
387 if( unsaved_changes(0) ){
388 if( (mFlags & PATCH_FORCE)==0 ){
389 fossil_fatal("Cannot apply patch: there are unsaved changes "
390 "in the current check-out");
391 }else{
392 blob_appendf(&cmd, "%$ revert", g.nameOfExe);
393 if( mFlags & PATCH_DRYRUN ){
394 fossil_print("%s\n", blob_str(&cmd));
395 }else{
396 int rc = fossil_system(blob_str(&cmd));
397 if( rc ){
@@ -429,23 +429,27 @@
429 }
430 }
431 }
432 blob_reset(&cmd);
433 if( db_table_exists("patch","patchmerge") ){
 
434 db_prepare(&q,
435 "SELECT type, mhash, upper(type) FROM patch.patchmerge"
436 " WHERE type IN ('merge','cherrypick','backout','integrate')"
437 " AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
438 );
439 while( db_step(&q)==SQLITE_ROW ){
440 const char *zType = db_column_text(&q,0);
441 blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
442 if( strcmp(zType,"merge")==0 ){
443 blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));
 
444 }else{
445 blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));
 
446 }
 
447 if( mFlags & PATCH_VERBOSE ){
448 fossil_print("%-10s %s\n", db_column_text(&q,2),
449 db_column_text(&q,0));
450 }
451 }
@@ -458,10 +462,45 @@
458 fossil_fatal("unable to do merges:\n%s",
459 blob_str(&cmd));
460 }
461 }
462 blob_reset(&cmd);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463 }
464
465 /* Deletions */
466 db_prepare(&q, "SELECT pathname FROM patch.chng"
467 " WHERE origname IS NULL AND delta IS NULL");
468
--- src/patch.c
+++ src/patch.c
@@ -387,11 +387,11 @@
387 if( unsaved_changes(0) ){
388 if( (mFlags & PATCH_FORCE)==0 ){
389 fossil_fatal("Cannot apply patch: there are unsaved changes "
390 "in the current check-out");
391 }else{
392 blob_appendf(&cmd, "%$ revert --noundo", g.nameOfExe);
393 if( mFlags & PATCH_DRYRUN ){
394 fossil_print("%s\n", blob_str(&cmd));
395 }else{
396 int rc = fossil_system(blob_str(&cmd));
397 if( rc ){
@@ -429,23 +429,27 @@
429 }
430 }
431 }
432 blob_reset(&cmd);
433 if( db_table_exists("patch","patchmerge") ){
434 int nMerge = 0;
435 db_prepare(&q,
436 "SELECT type, mhash, upper(type) FROM patch.patchmerge"
437 " WHERE type IN ('merge','cherrypick','backout','integrate')"
438 " AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
439 );
440 while( db_step(&q)==SQLITE_ROW ){
441 const char *zType = db_column_text(&q,0);
442 blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
443 if( strcmp(zType,"merge")==0 ){
444 blob_appendf(&cmd, " merge --noundo --nosync %s\n",
445 db_column_text(&q,1));
446 }else{
447 blob_appendf(&cmd, " merge --%s --noundo --nosync %s\n",
448 zType, db_column_text(&q,1));
449 }
450 nMerge++;
451 if( mFlags & PATCH_VERBOSE ){
452 fossil_print("%-10s %s\n", db_column_text(&q,2),
453 db_column_text(&q,0));
454 }
455 }
@@ -458,10 +462,45 @@
462 fossil_fatal("unable to do merges:\n%s",
463 blob_str(&cmd));
464 }
465 }
466 blob_reset(&cmd);
467
468 /* 2024-12-16 https://fossil-scm.org/home/forumpost/51a37054
469 ** If one or more merge operations occurred in the patch and there are
470 ** files that are marked as "chnged' in the local VFILE but which
471 ** are not mentioned as having been modified in the patch, then
472 ** revert those files.
473 */
474 if( nMerge ){
475 int vid = db_lget_int("checkout", 0);
476 int nRevert = 0;
477 blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
478 blob_appendf(&cmd, " revert --noundo ");
479 db_prepare(&q,
480 "SELECT pathname FROM vfile WHERE vid=%d AND chnged "
481 "EXCEPT SELECT pathname FROM chng",
482 vid
483 );
484 while( db_step(&q)==SQLITE_ROW ){
485 blob_append_escaped_arg(&cmd, db_column_text(&q,0), 1);
486 nRevert++;
487 }
488 db_finalize(&q);
489 if( nRevert ){
490 if( mFlags & PATCH_DRYRUN ){
491 fossil_print("%s", blob_str(&cmd));
492 }else{
493 int rc = fossil_unsafe_system(blob_str(&cmd));
494 if( rc ){
495 fossil_fatal("unable to do reverts:\n%s",
496 blob_str(&cmd));
497 }
498 }
499 }
500 blob_reset(&cmd);
501 }
502 }
503
504 /* Deletions */
505 db_prepare(&q, "SELECT pathname FROM patch.chng"
506 " WHERE origname IS NULL AND delta IS NULL");
507
+11 -4
--- src/update.c
+++ src/update.c
@@ -854,10 +854,11 @@
854854
**
855855
** If a file is reverted accidentally, it can be restored using
856856
** the "fossil undo" command.
857857
**
858858
** Options:
859
+** --noundo Do not record changes in the undo/redo log.
859860
** -r|--revision VERSION Revert given FILE(s) back to given
860861
** VERSION
861862
**
862863
** See also: [[redo]], [[undo]], [[checkout]], [[update]]
863864
*/
@@ -867,17 +868,19 @@
867868
ManifestFile *pCoFile; /* File within current check-out manifest */
868869
ManifestFile *pRvFile; /* File within revert version manifest */
869870
const char *zFile; /* Filename relative to check-out root */
870871
const char *zRevision; /* Selected revert version, NULL if current */
871872
Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */
873
+ int useUndo = 1; /* True to record changes in UNDO */
872874
int i;
873875
Stmt q;
874876
int revertAll = 0;
875877
int revisionOptNotSupported = 0;
876878
877879
undo_capture_command_line();
878880
zRevision = find_option("revision", "r", 1);
881
+ useUndo = find_option("noundo", 0, 0)==0;
879882
verify_all_options();
880883
881884
if( g.argc<2 ){
882885
usage("?OPTIONS? [FILE] ...");
883886
}
@@ -890,11 +893,15 @@
890893
/* Get manifests of revert version and (if different) current check-out. */
891894
pRvManifest = historical_manifest(zRevision);
892895
pCoManifest = zRevision ? historical_manifest(0) : 0;
893896
894897
db_begin_transaction();
895
- undo_begin();
898
+ if( useUndo ){
899
+ undo_begin();
900
+ }else{
901
+ undo_reset();
902
+ }
896903
db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");
897904
898905
if( g.argc>2 ){
899906
for(i=2; i<g.argc; i++){
900907
Blob fname;
@@ -987,11 +994,11 @@
987994
if( !pRvFile ){
988995
if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
989996
zFile, zFile)==0 ){
990997
fossil_print("UNMANAGE %s\n", zFile);
991998
}else{
992
- undo_save(zFile);
999
+ if( useUndo ) undo_save(zFile);
9931000
file_delete(zFull);
9941001
fossil_print("DELETE %s\n", zFile);
9951002
}
9961003
db_multi_exec(
9971004
"UPDATE OR REPLACE vfile"
@@ -1014,11 +1021,11 @@
10141021
}
10151022
10161023
/* Get contents of reverted-to file. */
10171024
content_get(fast_uuid_to_rid(pRvFile->zUuid), &record);
10181025
1019
- undo_save(zFile);
1026
+ if( useUndo ) undo_save(zFile);
10201027
if( file_size(zFull, RepoFILE)>=0
10211028
&& (rvPerm==PERM_LNK || file_islink(0))
10221029
){
10231030
file_delete(zFull);
10241031
}
@@ -1040,12 +1047,12 @@
10401047
}
10411048
blob_reset(&record);
10421049
free(zFull);
10431050
}
10441051
db_finalize(&q);
1045
- undo_finish();
1052
+ if( useUndo) undo_finish();
10461053
db_end_transaction(0);
10471054
10481055
/* Deallocate parsed manifest structures. */
10491056
manifest_destroy(pRvManifest);
10501057
manifest_destroy(pCoManifest);
10511058
}
10521059
--- src/update.c
+++ src/update.c
@@ -854,10 +854,11 @@
854 **
855 ** If a file is reverted accidentally, it can be restored using
856 ** the "fossil undo" command.
857 **
858 ** Options:
 
859 ** -r|--revision VERSION Revert given FILE(s) back to given
860 ** VERSION
861 **
862 ** See also: [[redo]], [[undo]], [[checkout]], [[update]]
863 */
@@ -867,17 +868,19 @@
867 ManifestFile *pCoFile; /* File within current check-out manifest */
868 ManifestFile *pRvFile; /* File within revert version manifest */
869 const char *zFile; /* Filename relative to check-out root */
870 const char *zRevision; /* Selected revert version, NULL if current */
871 Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */
 
872 int i;
873 Stmt q;
874 int revertAll = 0;
875 int revisionOptNotSupported = 0;
876
877 undo_capture_command_line();
878 zRevision = find_option("revision", "r", 1);
 
879 verify_all_options();
880
881 if( g.argc<2 ){
882 usage("?OPTIONS? [FILE] ...");
883 }
@@ -890,11 +893,15 @@
890 /* Get manifests of revert version and (if different) current check-out. */
891 pRvManifest = historical_manifest(zRevision);
892 pCoManifest = zRevision ? historical_manifest(0) : 0;
893
894 db_begin_transaction();
895 undo_begin();
 
 
 
 
896 db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");
897
898 if( g.argc>2 ){
899 for(i=2; i<g.argc; i++){
900 Blob fname;
@@ -987,11 +994,11 @@
987 if( !pRvFile ){
988 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
989 zFile, zFile)==0 ){
990 fossil_print("UNMANAGE %s\n", zFile);
991 }else{
992 undo_save(zFile);
993 file_delete(zFull);
994 fossil_print("DELETE %s\n", zFile);
995 }
996 db_multi_exec(
997 "UPDATE OR REPLACE vfile"
@@ -1014,11 +1021,11 @@
1014 }
1015
1016 /* Get contents of reverted-to file. */
1017 content_get(fast_uuid_to_rid(pRvFile->zUuid), &record);
1018
1019 undo_save(zFile);
1020 if( file_size(zFull, RepoFILE)>=0
1021 && (rvPerm==PERM_LNK || file_islink(0))
1022 ){
1023 file_delete(zFull);
1024 }
@@ -1040,12 +1047,12 @@
1040 }
1041 blob_reset(&record);
1042 free(zFull);
1043 }
1044 db_finalize(&q);
1045 undo_finish();
1046 db_end_transaction(0);
1047
1048 /* Deallocate parsed manifest structures. */
1049 manifest_destroy(pRvManifest);
1050 manifest_destroy(pCoManifest);
1051 }
1052
--- src/update.c
+++ src/update.c
@@ -854,10 +854,11 @@
854 **
855 ** If a file is reverted accidentally, it can be restored using
856 ** the "fossil undo" command.
857 **
858 ** Options:
859 ** --noundo Do not record changes in the undo/redo log.
860 ** -r|--revision VERSION Revert given FILE(s) back to given
861 ** VERSION
862 **
863 ** See also: [[redo]], [[undo]], [[checkout]], [[update]]
864 */
@@ -867,17 +868,19 @@
868 ManifestFile *pCoFile; /* File within current check-out manifest */
869 ManifestFile *pRvFile; /* File within revert version manifest */
870 const char *zFile; /* Filename relative to check-out root */
871 const char *zRevision; /* Selected revert version, NULL if current */
872 Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */
873 int useUndo = 1; /* True to record changes in UNDO */
874 int i;
875 Stmt q;
876 int revertAll = 0;
877 int revisionOptNotSupported = 0;
878
879 undo_capture_command_line();
880 zRevision = find_option("revision", "r", 1);
881 useUndo = find_option("noundo", 0, 0)==0;
882 verify_all_options();
883
884 if( g.argc<2 ){
885 usage("?OPTIONS? [FILE] ...");
886 }
@@ -890,11 +893,15 @@
893 /* Get manifests of revert version and (if different) current check-out. */
894 pRvManifest = historical_manifest(zRevision);
895 pCoManifest = zRevision ? historical_manifest(0) : 0;
896
897 db_begin_transaction();
898 if( useUndo ){
899 undo_begin();
900 }else{
901 undo_reset();
902 }
903 db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");
904
905 if( g.argc>2 ){
906 for(i=2; i<g.argc; i++){
907 Blob fname;
@@ -987,11 +994,11 @@
994 if( !pRvFile ){
995 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
996 zFile, zFile)==0 ){
997 fossil_print("UNMANAGE %s\n", zFile);
998 }else{
999 if( useUndo ) undo_save(zFile);
1000 file_delete(zFull);
1001 fossil_print("DELETE %s\n", zFile);
1002 }
1003 db_multi_exec(
1004 "UPDATE OR REPLACE vfile"
@@ -1014,11 +1021,11 @@
1021 }
1022
1023 /* Get contents of reverted-to file. */
1024 content_get(fast_uuid_to_rid(pRvFile->zUuid), &record);
1025
1026 if( useUndo ) undo_save(zFile);
1027 if( file_size(zFull, RepoFILE)>=0
1028 && (rvPerm==PERM_LNK || file_islink(0))
1029 ){
1030 file_delete(zFull);
1031 }
@@ -1040,12 +1047,12 @@
1047 }
1048 blob_reset(&record);
1049 free(zFull);
1050 }
1051 db_finalize(&q);
1052 if( useUndo) undo_finish();
1053 db_end_transaction(0);
1054
1055 /* Deallocate parsed manifest structures. */
1056 manifest_destroy(pRvManifest);
1057 manifest_destroy(pCoManifest);
1058 }
1059
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,13 +1,18 @@
11
<title>Change Log</title>
22
33
<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
44
5
+ * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that
6
+ it optionally accepts a directory name as its argument, and uses files
7
+ under that directory as the baseline for the diff.
58
* Added the [/help?cmd=/ckout|/ckout web page] to provide information
69
about pending changes in a working check-out
7
- * The [/help?cmd=ui|fossil ui] command defaults to using the /ckout
8
- page as its start page.
10
+ * The [/help?cmd=ui|fossil ui] command defaults to using the
11
+ [/help?cmd=/ckout|/ckout page] as its start page. Or, if the
12
+ "--from PATH" option is present, the default start page becomes
13
+ "/ckout?exbase=PATH".
914
* Added the [/help?cmd=merge-info|fossil merge-info] command and especially
1015
the --tk option to that command, to provide analysis of the most recent
1116
merge or update operation.
1217
* Issue a warning if a user tries to commit on a check-in where the
1318
branch has been changed.
1419
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,13 +1,18 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
4
 
 
 
5 * Added the [/help?cmd=/ckout|/ckout web page] to provide information
6 about pending changes in a working check-out
7 * The [/help?cmd=ui|fossil ui] command defaults to using the /ckout
8 page as its start page.
 
 
9 * Added the [/help?cmd=merge-info|fossil merge-info] command and especially
10 the --tk option to that command, to provide analysis of the most recent
11 merge or update operation.
12 * Issue a warning if a user tries to commit on a check-in where the
13 branch has been changed.
14
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,13 +1,18 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
4
5 * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that
6 it optionally accepts a directory name as its argument, and uses files
7 under that directory as the baseline for the diff.
8 * Added the [/help?cmd=/ckout|/ckout web page] to provide information
9 about pending changes in a working check-out
10 * The [/help?cmd=ui|fossil ui] command defaults to using the
11 [/help?cmd=/ckout|/ckout page] as its start page. Or, if the
12 "--from PATH" option is present, the default start page becomes
13 "/ckout?exbase=PATH".
14 * Added the [/help?cmd=merge-info|fossil merge-info] command and especially
15 the --tk option to that command, to provide analysis of the most recent
16 merge or update operation.
17 * Issue a warning if a user tries to commit on a check-in where the
18 branch has been changed.
19

Keyboard Shortcuts

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