Fossil SCM

Sync with trunk.

florian 2024-12-17 06:26 timeline-keyboard-navigation merge
Commit 215fc593d0327b15d93c5015820aeb029c93ba13b61b1714de9923e52703797d
+1 -2
--- src/add.c
+++ src/add.c
@@ -753,12 +753,11 @@
753753
**
754754
** * All files in the repository but missing from the check-out (that is,
755755
** all files that show as MISSING with the "status" command) are
756756
** removed as if by the "[[rm]]" command.
757757
**
758
-** The command does not "[[commit]]". You must run the "[[commit]]" separately
759
-** as a separate step.
758
+** Note that this command does not "commit", as that is a separate step.
760759
**
761760
** Files and directories whose names begin with "." are ignored unless
762761
** the --dotfiles option is used.
763762
**
764763
** The --ignore option overrides the "ignore-glob" setting, as do the
765764
--- src/add.c
+++ src/add.c
@@ -753,12 +753,11 @@
753 **
754 ** * All files in the repository but missing from the check-out (that is,
755 ** all files that show as MISSING with the "status" command) are
756 ** removed as if by the "[[rm]]" command.
757 **
758 ** The command does not "[[commit]]". You must run the "[[commit]]" separately
759 ** as a separate step.
760 **
761 ** Files and directories whose names begin with "." are ignored unless
762 ** the --dotfiles option is used.
763 **
764 ** The --ignore option overrides the "ignore-glob" setting, as do the
765
--- src/add.c
+++ src/add.c
@@ -753,12 +753,11 @@
753 **
754 ** * All files in the repository but missing from the check-out (that is,
755 ** all files that show as MISSING with the "status" command) are
756 ** removed as if by the "[[rm]]" command.
757 **
758 ** Note that this command does not "commit", as that is a separate step.
 
759 **
760 ** Files and directories whose names begin with "." are ignored unless
761 ** the --dotfiles option is used.
762 **
763 ** The --ignore option overrides the "ignore-glob" setting, as do the
764
+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
--- src/default.css
+++ src/default.css
@@ -762,10 +762,11 @@
762762
border-bottom: 3px solid gold;
763763
}
764764
body.tkt div.content ol.tkt-changes > li:target > ol {
765765
border-left: 1px solid gold;
766766
}
767
+body.cpage-ckout .file-change-line,
767768
body.cpage-info .file-change-line,
768769
body.cpage-vdiff .file-change-line {
769770
margin-top: 16px;
770771
margin-bottom: 16px;
771772
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
772773
--- src/default.css
+++ src/default.css
@@ -762,10 +762,11 @@
762 border-bottom: 3px solid gold;
763 }
764 body.tkt div.content ol.tkt-changes > li:target > ol {
765 border-left: 1px solid gold;
766 }
 
767 body.cpage-info .file-change-line,
768 body.cpage-vdiff .file-change-line {
769 margin-top: 16px;
770 margin-bottom: 16px;
771 margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
772
--- src/default.css
+++ src/default.css
@@ -762,10 +762,11 @@
762 border-bottom: 3px solid gold;
763 }
764 body.tkt div.content ol.tkt-changes > li:target > ol {
765 border-left: 1px solid gold;
766 }
767 body.cpage-ckout .file-change-line,
768 body.cpage-info .file-change-line,
769 body.cpage-vdiff .file-change-line {
770 margin-top: 16px;
771 margin-bottom: 16px;
772 margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
773
--- src/default.css
+++ src/default.css
@@ -762,10 +762,11 @@
762762
border-bottom: 3px solid gold;
763763
}
764764
body.tkt div.content ol.tkt-changes > li:target > ol {
765765
border-left: 1px solid gold;
766766
}
767
+body.cpage-ckout .file-change-line,
767768
body.cpage-info .file-change-line,
768769
body.cpage-vdiff .file-change-line {
769770
margin-top: 16px;
770771
margin-bottom: 16px;
771772
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
772773
--- src/default.css
+++ src/default.css
@@ -762,10 +762,11 @@
762 border-bottom: 3px solid gold;
763 }
764 body.tkt div.content ol.tkt-changes > li:target > ol {
765 border-left: 1px solid gold;
766 }
 
767 body.cpage-info .file-change-line,
768 body.cpage-vdiff .file-change-line {
769 margin-top: 16px;
770 margin-bottom: 16px;
771 margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
772
--- src/default.css
+++ src/default.css
@@ -762,10 +762,11 @@
762 border-bottom: 3px solid gold;
763 }
764 body.tkt div.content ol.tkt-changes > li:target > ol {
765 border-left: 1px solid gold;
766 }
767 body.cpage-ckout .file-change-line,
768 body.cpage-info .file-change-line,
769 body.cpage-vdiff .file-change-line {
770 margin-top: 16px;
771 margin-bottom: 16px;
772 margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
773
+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
@@ -784,26 +784,27 @@
784784
blob_reset(&file);
785785
return rc;
786786
}
787787
788788
/*
789
-** Run a diff between the version zFrom and files on disk. zFrom might
790
-** be NULL which means to simply show the difference between the edited
791
-** files on disk and the check-out on which they are based.
789
+** Run a diff between the version zFrom and files on disk in the current
790
+** working checkout. zFrom might be NULL which means to simply show the
791
+** difference between the edited files on disk and the check-out on which
792
+** they are based.
792793
**
793794
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
794795
** command zDiffCmd to do the diffing.
795796
**
796797
** When using an external diff program, zBinGlob contains the GLOB patterns
797798
** for file names to treat as binary. If fIncludeBinary is zero, these files
798799
** will be skipped in addition to files that may contain binary content.
799800
*/
800
-void diff_against_disk(
801
+void diff_version_to_checkout(
801802
const char *zFrom, /* Version to difference from */
802803
DiffConfig *pCfg, /* Flags controlling diff output */
803804
FileDirList *pFileDir, /* Which files to diff */
804
- Blob *pOut /* Blob to output diff instead of stdout */
805
+ Blob *pOut /* Blob to output diff instead of stdout */
805806
){
806807
int vid;
807808
Blob sql;
808809
Stmt q;
809810
int asNewFile; /* Treat non-existant files as empty files */
@@ -928,20 +929,20 @@
928929
db_finalize(&q);
929930
db_end_transaction(1); /* ROLLBACK */
930931
}
931932
932933
/*
933
-** Run a diff between the undo buffer and files on disk.
934
+** Run a diff from the undo buffer to files on disk.
934935
**
935936
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
936937
** command zDiffCmd to do the diffing.
937938
**
938939
** When using an external diff program, zBinGlob contains the GLOB patterns
939940
** for file names to treat as binary. If fIncludeBinary is zero, these files
940941
** will be skipped in addition to files that may contain binary content.
941942
*/
942
-static void diff_against_undo(
943
+static void diff_undo_to_checkout(
943944
DiffConfig *pCfg, /* Flags controlling diff output */
944945
FileDirList *pFileDir /* List of files and directories to diff */
945946
){
946947
Stmt q;
947948
Blob content;
@@ -1088,10 +1089,67 @@
10881089
}
10891090
}
10901091
manifest_destroy(pFrom);
10911092
manifest_destroy(pTo);
10921093
}
1094
+
1095
+/*
1096
+** Compute the difference from an external tree of files to the current
1097
+** working checkout with its edits.
1098
+**
1099
+** To put it another way: Every managed file in the current working
1100
+** checkout is compared to the file with same name under zExternBase. The
1101
+** zExternBase files are on the left and the files in the current working
1102
+** directory are on the right.
1103
+*/
1104
+void diff_externbase_to_checkout(
1105
+ const char *zExternBase, /* Remote tree to use as the baseline */
1106
+ DiffConfig *pCfg, /* Diff settings */
1107
+ FileDirList *pFileDir /* Only look at these files */
1108
+){
1109
+ int vid;
1110
+ Stmt q;
1111
+
1112
+ vid = db_lget_int("checkout",0);
1113
+ if( file_isdir(zExternBase, ExtFILE)!=1 ){
1114
+ fossil_fatal("\"%s\" is not a directory", zExternBase);
1115
+ }
1116
+ db_prepare(&q,
1117
+ "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
1118
+ vid
1119
+ );
1120
+ while( db_step(&q)==SQLITE_ROW ){
1121
+ const char *zFile; /* Name of file in the repository */
1122
+ char *zLhs; /* Full name of left-hand side file */
1123
+ char *zRhs; /* Full name of right-hand side file */
1124
+ Blob rhs; /* Full text of RHS */
1125
+ Blob lhs; /* Full text of LHS */
1126
+
1127
+ zFile = db_column_text(&q,0);
1128
+ if( !file_dir_match(pFileDir, zFile) ) continue;
1129
+ zLhs = mprintf("%s/%s", zExternBase, zFile);
1130
+ zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
1131
+ if( file_size(zLhs, ExtFILE)<0 ){
1132
+ blob_zero(&lhs);
1133
+ }else{
1134
+ blob_read_from_file(&lhs, zLhs, ExtFILE);
1135
+ }
1136
+ blob_read_from_file(&rhs, zRhs, ExtFILE);
1137
+ if( blob_size(&lhs)!=blob_size(&rhs)
1138
+ || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
1139
+ ){
1140
+ diff_print_index(zFile, pCfg, 0);
1141
+ diff_file_mem(&lhs, &rhs, zFile, pCfg);
1142
+ }
1143
+ blob_reset(&lhs);
1144
+ blob_reset(&rhs);
1145
+ fossil_free(zLhs);
1146
+ fossil_free(zRhs);
1147
+ }
1148
+ db_finalize(&q);
1149
+}
1150
+
10931151
10941152
/*
10951153
** Return the name of the external diff command, or return NULL if
10961154
** no external diff command is defined.
10971155
*/
@@ -1224,10 +1282,14 @@
12241282
** option specifies the check-in from which the second version of the file
12251283
** or files is taken. If there is no "--to" option then the (possibly edited)
12261284
** files in the current check-out are used. The "--checkin VERSION" option
12271285
** shows the changes made by check-in VERSION relative to its primary parent.
12281286
** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
1287
+**
1288
+** With the "--from VERSION" option, if VERSION is actually a directory name
1289
+** (not a tag or check-in hash) then the files under that directory are used
1290
+** as the baseline for the diff.
12291291
**
12301292
** The "-i" command-line option forces the use of Fossil's own internal
12311293
** diff logic rather than any external diff program that might be configured
12321294
** using the "setting" command. If no external diff program is configured,
12331295
** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1256,11 +1318,13 @@
12561318
** with negative N meaning show all content
12571319
** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
12581320
** --diff-binary BOOL Include binary files with external commands
12591321
** --exec-abs-paths Force absolute path names on external commands
12601322
** --exec-rel-paths Force relative path names on external commands
1261
-** -r|--from VERSION Select VERSION as source for the diff
1323
+** -r|--from VERSION Use VERSION as the baseline for the diff, or
1324
+** if VERSION is a directory name, use files in
1325
+** that directory as the baseline.
12621326
** -w|--ignore-all-space Ignore white space when comparing lines
12631327
** -i|--internal Use internal diff logic
12641328
** --invert Invert the diff
12651329
** --json Output formatted as JSON
12661330
** -n|--linenum Show line numbers
@@ -1270,11 +1334,11 @@
12701334
** --strip-trailing-cr Strip trailing CR
12711335
** --tcl Tcl-formatted output used internally by --tk
12721336
** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
12731337
** --tk Launch a Tcl/Tk GUI for display
12741338
** --to VERSION Select VERSION as target for the diff
1275
-** --undo Diff against the "undo" buffer
1339
+** --undo Use the undo buffer as the baseline
12761340
** --unified Unified diff
12771341
** -v|--verbose Output complete text of added or deleted files
12781342
** -h|--versions Show compared versions in the diff header
12791343
** --webpage Format output as a stand-alone HTML webpage
12801344
** -W|--width N Width of lines in side-by-side diff
@@ -1287,10 +1351,11 @@
12871351
const char *zCheckin; /* Check-in version number */
12881352
const char *zBranch; /* Branch to diff */
12891353
int againstUndo = 0; /* Diff against files in the undo buffer */
12901354
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
12911355
DiffConfig DCfg; /* Diff configuration object */
1356
+ int bFromIsDir = 0; /* True if zFrom is a directory name */
12921357
12931358
if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
12941359
diff_tk("diff", 2);
12951360
return;
12961361
}
@@ -1298,11 +1363,11 @@
12981363
zFrom = find_option("from", "r", 1);
12991364
zTo = find_option("to", 0, 1);
13001365
zCheckin = find_option("checkin", "ci", 1);
13011366
zBranch = find_option("branch", 0, 1);
13021367
againstUndo = find_option("undo",0,0)!=0;
1303
- if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1368
+ if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
13041369
fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
13051370
" or --branch");
13061371
}
13071372
if( zBranch ){
13081373
if( zTo || zFrom || zCheckin ){
@@ -1309,15 +1374,13 @@
13091374
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
13101375
}
13111376
zTo = zBranch;
13121377
zFrom = mprintf("root:%s", zBranch);
13131378
}
1314
- if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
1379
+ if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
13151380
fossil_fatal("cannot use --checkin together with --from or --to");
13161381
}
1317
- diff_options(&DCfg, isGDiff, 0);
1318
- determine_exec_relative_option(1);
13191382
if( 0==zCheckin ){
13201383
if( zTo==0 || againstUndo ){
13211384
db_must_be_within_tree();
13221385
}else if( zFrom==0 ){
13231386
fossil_fatal("must use --from if --to is present");
@@ -1325,10 +1388,21 @@
13251388
db_find_and_open_repository(0, 0);
13261389
}
13271390
}else{
13281391
db_find_and_open_repository(0, 0);
13291392
}
1393
+ determine_exec_relative_option(1);
1394
+ if( zFrom!=file_tail(zFrom)
1395
+ && file_isdir(zFrom, ExtFILE)==1
1396
+ && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
1397
+ ){
1398
+ bFromIsDir = 1;
1399
+ if( zTo ){
1400
+ fossil_fatal("cannot use --to together with \"--from PATH\"");
1401
+ }
1402
+ }
1403
+ diff_options(&DCfg, isGDiff, 0);
13301404
verify_all_options();
13311405
g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
13321406
if( g.argc>=3 ){
13331407
int i;
13341408
Blob fname;
@@ -1357,18 +1431,20 @@
13571431
if( zFrom==0 ){
13581432
fossil_fatal("check-in %s has no parent", zTo);
13591433
}
13601434
}
13611435
diff_begin(&DCfg);
1362
- if( againstUndo ){
1436
+ if( bFromIsDir ){
1437
+ diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
1438
+ }else if( againstUndo ){
13631439
if( db_lget_int("undo_available",0)==0 ){
13641440
fossil_print("No undo or redo is available\n");
13651441
return;
13661442
}
1367
- diff_against_undo(&DCfg, pFileDir);
1443
+ diff_undo_to_checkout(&DCfg, pFileDir);
13681444
}else if( zTo==0 ){
1369
- diff_against_disk(zFrom, &DCfg, pFileDir, 0);
1445
+ diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
13701446
}else{
13711447
diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
13721448
}
13731449
if( pFileDir ){
13741450
int i;
13751451
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -784,26 +784,27 @@
784 blob_reset(&file);
785 return rc;
786 }
787
788 /*
789 ** Run a diff between the version zFrom and files on disk. zFrom might
790 ** be NULL which means to simply show the difference between the edited
791 ** files on disk and the check-out on which they are based.
 
792 **
793 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
794 ** command zDiffCmd to do the diffing.
795 **
796 ** When using an external diff program, zBinGlob contains the GLOB patterns
797 ** for file names to treat as binary. If fIncludeBinary is zero, these files
798 ** will be skipped in addition to files that may contain binary content.
799 */
800 void diff_against_disk(
801 const char *zFrom, /* Version to difference from */
802 DiffConfig *pCfg, /* Flags controlling diff output */
803 FileDirList *pFileDir, /* Which files to diff */
804 Blob *pOut /* Blob to output diff instead of stdout */
805 ){
806 int vid;
807 Blob sql;
808 Stmt q;
809 int asNewFile; /* Treat non-existant files as empty files */
@@ -928,20 +929,20 @@
928 db_finalize(&q);
929 db_end_transaction(1); /* ROLLBACK */
930 }
931
932 /*
933 ** Run a diff between the undo buffer and files on disk.
934 **
935 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
936 ** command zDiffCmd to do the diffing.
937 **
938 ** When using an external diff program, zBinGlob contains the GLOB patterns
939 ** for file names to treat as binary. If fIncludeBinary is zero, these files
940 ** will be skipped in addition to files that may contain binary content.
941 */
942 static void diff_against_undo(
943 DiffConfig *pCfg, /* Flags controlling diff output */
944 FileDirList *pFileDir /* List of files and directories to diff */
945 ){
946 Stmt q;
947 Blob content;
@@ -1088,10 +1089,67 @@
1088 }
1089 }
1090 manifest_destroy(pFrom);
1091 manifest_destroy(pTo);
1092 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1093
1094 /*
1095 ** Return the name of the external diff command, or return NULL if
1096 ** no external diff command is defined.
1097 */
@@ -1224,10 +1282,14 @@
1224 ** option specifies the check-in from which the second version of the file
1225 ** or files is taken. If there is no "--to" option then the (possibly edited)
1226 ** files in the current check-out are used. The "--checkin VERSION" option
1227 ** shows the changes made by check-in VERSION relative to its primary parent.
1228 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
 
 
 
 
1229 **
1230 ** The "-i" command-line option forces the use of Fossil's own internal
1231 ** diff logic rather than any external diff program that might be configured
1232 ** using the "setting" command. If no external diff program is configured,
1233 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1256,11 +1318,13 @@
1256 ** with negative N meaning show all content
1257 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
1258 ** --diff-binary BOOL Include binary files with external commands
1259 ** --exec-abs-paths Force absolute path names on external commands
1260 ** --exec-rel-paths Force relative path names on external commands
1261 ** -r|--from VERSION Select VERSION as source for the diff
 
 
1262 ** -w|--ignore-all-space Ignore white space when comparing lines
1263 ** -i|--internal Use internal diff logic
1264 ** --invert Invert the diff
1265 ** --json Output formatted as JSON
1266 ** -n|--linenum Show line numbers
@@ -1270,11 +1334,11 @@
1270 ** --strip-trailing-cr Strip trailing CR
1271 ** --tcl Tcl-formatted output used internally by --tk
1272 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1273 ** --tk Launch a Tcl/Tk GUI for display
1274 ** --to VERSION Select VERSION as target for the diff
1275 ** --undo Diff against the "undo" buffer
1276 ** --unified Unified diff
1277 ** -v|--verbose Output complete text of added or deleted files
1278 ** -h|--versions Show compared versions in the diff header
1279 ** --webpage Format output as a stand-alone HTML webpage
1280 ** -W|--width N Width of lines in side-by-side diff
@@ -1287,10 +1351,11 @@
1287 const char *zCheckin; /* Check-in version number */
1288 const char *zBranch; /* Branch to diff */
1289 int againstUndo = 0; /* Diff against files in the undo buffer */
1290 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1291 DiffConfig DCfg; /* Diff configuration object */
 
1292
1293 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1294 diff_tk("diff", 2);
1295 return;
1296 }
@@ -1298,11 +1363,11 @@
1298 zFrom = find_option("from", "r", 1);
1299 zTo = find_option("to", 0, 1);
1300 zCheckin = find_option("checkin", "ci", 1);
1301 zBranch = find_option("branch", 0, 1);
1302 againstUndo = find_option("undo",0,0)!=0;
1303 if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1304 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
1305 " or --branch");
1306 }
1307 if( zBranch ){
1308 if( zTo || zFrom || zCheckin ){
@@ -1309,15 +1374,13 @@
1309 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1310 }
1311 zTo = zBranch;
1312 zFrom = mprintf("root:%s", zBranch);
1313 }
1314 if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
1315 fossil_fatal("cannot use --checkin together with --from or --to");
1316 }
1317 diff_options(&DCfg, isGDiff, 0);
1318 determine_exec_relative_option(1);
1319 if( 0==zCheckin ){
1320 if( zTo==0 || againstUndo ){
1321 db_must_be_within_tree();
1322 }else if( zFrom==0 ){
1323 fossil_fatal("must use --from if --to is present");
@@ -1325,10 +1388,21 @@
1325 db_find_and_open_repository(0, 0);
1326 }
1327 }else{
1328 db_find_and_open_repository(0, 0);
1329 }
 
 
 
 
 
 
 
 
 
 
 
1330 verify_all_options();
1331 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
1332 if( g.argc>=3 ){
1333 int i;
1334 Blob fname;
@@ -1357,18 +1431,20 @@
1357 if( zFrom==0 ){
1358 fossil_fatal("check-in %s has no parent", zTo);
1359 }
1360 }
1361 diff_begin(&DCfg);
1362 if( againstUndo ){
 
 
1363 if( db_lget_int("undo_available",0)==0 ){
1364 fossil_print("No undo or redo is available\n");
1365 return;
1366 }
1367 diff_against_undo(&DCfg, pFileDir);
1368 }else if( zTo==0 ){
1369 diff_against_disk(zFrom, &DCfg, pFileDir, 0);
1370 }else{
1371 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
1372 }
1373 if( pFileDir ){
1374 int i;
1375
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -784,26 +784,27 @@
784 blob_reset(&file);
785 return rc;
786 }
787
788 /*
789 ** Run a diff between the version zFrom and files on disk in the current
790 ** working checkout. zFrom might be NULL which means to simply show the
791 ** difference between the edited files on disk and the check-out on which
792 ** they are based.
793 **
794 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
795 ** command zDiffCmd to do the diffing.
796 **
797 ** When using an external diff program, zBinGlob contains the GLOB patterns
798 ** for file names to treat as binary. If fIncludeBinary is zero, these files
799 ** will be skipped in addition to files that may contain binary content.
800 */
801 void diff_version_to_checkout(
802 const char *zFrom, /* Version to difference from */
803 DiffConfig *pCfg, /* Flags controlling diff output */
804 FileDirList *pFileDir, /* Which files to diff */
805 Blob *pOut /* Blob to output diff instead of stdout */
806 ){
807 int vid;
808 Blob sql;
809 Stmt q;
810 int asNewFile; /* Treat non-existant files as empty files */
@@ -928,20 +929,20 @@
929 db_finalize(&q);
930 db_end_transaction(1); /* ROLLBACK */
931 }
932
933 /*
934 ** Run a diff from the undo buffer to files on disk.
935 **
936 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
937 ** command zDiffCmd to do the diffing.
938 **
939 ** When using an external diff program, zBinGlob contains the GLOB patterns
940 ** for file names to treat as binary. If fIncludeBinary is zero, these files
941 ** will be skipped in addition to files that may contain binary content.
942 */
943 static void diff_undo_to_checkout(
944 DiffConfig *pCfg, /* Flags controlling diff output */
945 FileDirList *pFileDir /* List of files and directories to diff */
946 ){
947 Stmt q;
948 Blob content;
@@ -1088,10 +1089,67 @@
1089 }
1090 }
1091 manifest_destroy(pFrom);
1092 manifest_destroy(pTo);
1093 }
1094
1095 /*
1096 ** Compute the difference from an external tree of files to the current
1097 ** working checkout with its edits.
1098 **
1099 ** To put it another way: Every managed file in the current working
1100 ** checkout is compared to the file with same name under zExternBase. The
1101 ** zExternBase files are on the left and the files in the current working
1102 ** directory are on the right.
1103 */
1104 void diff_externbase_to_checkout(
1105 const char *zExternBase, /* Remote tree to use as the baseline */
1106 DiffConfig *pCfg, /* Diff settings */
1107 FileDirList *pFileDir /* Only look at these files */
1108 ){
1109 int vid;
1110 Stmt q;
1111
1112 vid = db_lget_int("checkout",0);
1113 if( file_isdir(zExternBase, ExtFILE)!=1 ){
1114 fossil_fatal("\"%s\" is not a directory", zExternBase);
1115 }
1116 db_prepare(&q,
1117 "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
1118 vid
1119 );
1120 while( db_step(&q)==SQLITE_ROW ){
1121 const char *zFile; /* Name of file in the repository */
1122 char *zLhs; /* Full name of left-hand side file */
1123 char *zRhs; /* Full name of right-hand side file */
1124 Blob rhs; /* Full text of RHS */
1125 Blob lhs; /* Full text of LHS */
1126
1127 zFile = db_column_text(&q,0);
1128 if( !file_dir_match(pFileDir, zFile) ) continue;
1129 zLhs = mprintf("%s/%s", zExternBase, zFile);
1130 zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
1131 if( file_size(zLhs, ExtFILE)<0 ){
1132 blob_zero(&lhs);
1133 }else{
1134 blob_read_from_file(&lhs, zLhs, ExtFILE);
1135 }
1136 blob_read_from_file(&rhs, zRhs, ExtFILE);
1137 if( blob_size(&lhs)!=blob_size(&rhs)
1138 || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
1139 ){
1140 diff_print_index(zFile, pCfg, 0);
1141 diff_file_mem(&lhs, &rhs, zFile, pCfg);
1142 }
1143 blob_reset(&lhs);
1144 blob_reset(&rhs);
1145 fossil_free(zLhs);
1146 fossil_free(zRhs);
1147 }
1148 db_finalize(&q);
1149 }
1150
1151
1152 /*
1153 ** Return the name of the external diff command, or return NULL if
1154 ** no external diff command is defined.
1155 */
@@ -1224,10 +1282,14 @@
1282 ** option specifies the check-in from which the second version of the file
1283 ** or files is taken. If there is no "--to" option then the (possibly edited)
1284 ** files in the current check-out are used. The "--checkin VERSION" option
1285 ** shows the changes made by check-in VERSION relative to its primary parent.
1286 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
1287 **
1288 ** With the "--from VERSION" option, if VERSION is actually a directory name
1289 ** (not a tag or check-in hash) then the files under that directory are used
1290 ** as the baseline for the diff.
1291 **
1292 ** The "-i" command-line option forces the use of Fossil's own internal
1293 ** diff logic rather than any external diff program that might be configured
1294 ** using the "setting" command. If no external diff program is configured,
1295 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
@@ -1256,11 +1318,13 @@
1318 ** with negative N meaning show all content
1319 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
1320 ** --diff-binary BOOL Include binary files with external commands
1321 ** --exec-abs-paths Force absolute path names on external commands
1322 ** --exec-rel-paths Force relative path names on external commands
1323 ** -r|--from VERSION Use VERSION as the baseline for the diff, or
1324 ** if VERSION is a directory name, use files in
1325 ** that directory as the baseline.
1326 ** -w|--ignore-all-space Ignore white space when comparing lines
1327 ** -i|--internal Use internal diff logic
1328 ** --invert Invert the diff
1329 ** --json Output formatted as JSON
1330 ** -n|--linenum Show line numbers
@@ -1270,11 +1334,11 @@
1334 ** --strip-trailing-cr Strip trailing CR
1335 ** --tcl Tcl-formatted output used internally by --tk
1336 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
1337 ** --tk Launch a Tcl/Tk GUI for display
1338 ** --to VERSION Select VERSION as target for the diff
1339 ** --undo Use the undo buffer as the baseline
1340 ** --unified Unified diff
1341 ** -v|--verbose Output complete text of added or deleted files
1342 ** -h|--versions Show compared versions in the diff header
1343 ** --webpage Format output as a stand-alone HTML webpage
1344 ** -W|--width N Width of lines in side-by-side diff
@@ -1287,10 +1351,11 @@
1351 const char *zCheckin; /* Check-in version number */
1352 const char *zBranch; /* Branch to diff */
1353 int againstUndo = 0; /* Diff against files in the undo buffer */
1354 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1355 DiffConfig DCfg; /* Diff configuration object */
1356 int bFromIsDir = 0; /* True if zFrom is a directory name */
1357
1358 if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
1359 diff_tk("diff", 2);
1360 return;
1361 }
@@ -1298,11 +1363,11 @@
1363 zFrom = find_option("from", "r", 1);
1364 zTo = find_option("to", 0, 1);
1365 zCheckin = find_option("checkin", "ci", 1);
1366 zBranch = find_option("branch", 0, 1);
1367 againstUndo = find_option("undo",0,0)!=0;
1368 if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
1369 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
1370 " or --branch");
1371 }
1372 if( zBranch ){
1373 if( zTo || zFrom || zCheckin ){
@@ -1309,15 +1374,13 @@
1374 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
1375 }
1376 zTo = zBranch;
1377 zFrom = mprintf("root:%s", zBranch);
1378 }
1379 if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
1380 fossil_fatal("cannot use --checkin together with --from or --to");
1381 }
 
 
1382 if( 0==zCheckin ){
1383 if( zTo==0 || againstUndo ){
1384 db_must_be_within_tree();
1385 }else if( zFrom==0 ){
1386 fossil_fatal("must use --from if --to is present");
@@ -1325,10 +1388,21 @@
1388 db_find_and_open_repository(0, 0);
1389 }
1390 }else{
1391 db_find_and_open_repository(0, 0);
1392 }
1393 determine_exec_relative_option(1);
1394 if( zFrom!=file_tail(zFrom)
1395 && file_isdir(zFrom, ExtFILE)==1
1396 && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
1397 ){
1398 bFromIsDir = 1;
1399 if( zTo ){
1400 fossil_fatal("cannot use --to together with \"--from PATH\"");
1401 }
1402 }
1403 diff_options(&DCfg, isGDiff, 0);
1404 verify_all_options();
1405 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
1406 if( g.argc>=3 ){
1407 int i;
1408 Blob fname;
@@ -1357,18 +1431,20 @@
1431 if( zFrom==0 ){
1432 fossil_fatal("check-in %s has no parent", zTo);
1433 }
1434 }
1435 diff_begin(&DCfg);
1436 if( bFromIsDir ){
1437 diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
1438 }else if( againstUndo ){
1439 if( db_lget_int("undo_available",0)==0 ){
1440 fossil_print("No undo or redo is available\n");
1441 return;
1442 }
1443 diff_undo_to_checkout(&DCfg, pFileDir);
1444 }else if( zTo==0 ){
1445 diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
1446 }else{
1447 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
1448 }
1449 if( pFileDir ){
1450 int i;
1451
+72 -34
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -1,44 +1,93 @@
11
/**
22
diff-related JS APIs for fossil.
33
*/
44
"use strict";
5
+/* Locate the UI element (if any) into which we can inject some diff-related
6
+ UI controls. */
7
+window.fossil.onPageLoad(function(){
8
+ const potentialParents = window.fossil.page.diffControlContainers = [
9
+ /* CSS selectors for possible parents for injected diff-related UI
10
+ controls. */
11
+ /* Put the most likely pages at the end, as array.pop() is more
12
+ efficient than array.shift() (see loop below). */
13
+ /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
14
+ /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
15
+ /* /fdiff */ 'body.fdiff form div.submenu',
16
+ /* /vdiff */ 'body.vdiff form div.submenu',
17
+ /* /info, /vinfo, /ckout */ 'body.vinfo div.sectionmenu.info-changes-menu'
18
+ ];
19
+ window.fossil.page.diffControlContainer = undefined;
20
+ while( potentialParents.length ){
21
+ if( (window.fossil.page.diffControlContainer
22
+ = document.querySelector(potentialParents.pop())) ){
23
+ break;
24
+ }
25
+ }
26
+});
27
+
528
window.fossil.onPageLoad(function(){
629
/**
730
Adds toggle checkboxes to each file entry in the diff views for
831
/info and similar pages.
932
*/
33
+ if( !window.fossil.page.diffControlContainer ){
34
+ return;
35
+ }
1036
const D = window.fossil.dom;
11
- 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. */
1244
const addToggle = function(diffElem){
1345
const sib = diffElem.previousElementSibling,
14
- btnOne = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
46
+ ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
1547
if(!sib) return;
16
- const lblToggle = D.append(D.label(null, " Toggle "), btnOne);
48
+ const lblToggle = D.label();
49
+ D.append(lblToggle, ckbox, D.text(" show/hide "));
1750
const wrapper = D.append(D.span(), lblToggle);
18
- const btnAll = D.button("all");
19
- btnAll.$cb = btnOne;
20
- allToggles.push(btnOne);
21
- D.append(sib, D.append(wrapper, lblToggle, D.text(" "), btnAll));
22
- btnOne.addEventListener('change', function(){
51
+ allToggles.push(ckbox);
52
+ ++checkedCount;
53
+ D.append(sib, D.append(wrapper, lblToggle));
54
+ ckbox.addEventListener('change', function(){
2355
diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
24
- }, false);
25
- btnAll.addEventListener('click', function(){
26
- /* Toggle all entries to match this line's new state. Note that
27
- we use click() instead of cb.checked=... so that the
28
- on-change event handler fires. */
29
- const checked = !this.$cb.checked;
30
- allToggles.forEach( (cb)=>{
31
- if(cb.checked!==checked) cb.click();
32
- });
56
+ if(btnAll){
57
+ checkedCount += (this.checked ? 1 : -1);
58
+ btnAll.innerText = (checkedCount < allToggles.length)
59
+ ? "Show diffs" : "Hide diffs";
60
+ }
3361
}, false);
3462
};
3563
if( !document.querySelector('body.fdiff') ){
3664
/* Don't show the diff toggle button for /fdiff because it only
3765
has a single file to show (and also a different DOM layout). */
3866
document.querySelectorAll('table.diff').forEach(addToggle);
3967
}
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
+ }, false);
88
+ }
4089
});
4190
4291
window.fossil.onPageLoad(function(){
4392
const F = window.fossil, D = F.dom;
4493
const Diff = F.diff = {
@@ -651,26 +700,15 @@
651700
/* Look for a parent element to hold the sbs-sync-scroll toggle
652701
checkbox. This differs per page. If we don't find one, simply
653702
elide that toggle and use whatever preference the user last
654703
specified (defaulting to on). */
655704
let cbSync /* scroll-sync checkbox */;
656
- let eToggleParent /* element to put the sync-scroll checkbox in */;
657
- const potentialParents = [ /* possible parents for the checkbox */
658
- /* Put the most likely pages at the end, as array.pop() is more
659
- efficient than array.shift() (see loop below). */
660
- /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
661
- /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
662
- /* /fdiff */ 'body.fdiff form div.submenu',
663
- /* /vdiff */ 'body.vdiff form div.submenu',
664
- /* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu'
665
- ];
666
- while( potentialParents.length ){
667
- if( (eToggleParent = document.querySelector(potentialParents.pop())) ){
668
- break;
669
- }
670
- }
671
- const keySbsScroll = 'sync-diff-scroll' /* F.storage key */;
705
+ let eToggleParent = /* element to put the sync-scroll checkbox in */
706
+ document.querySelector('table.diff.splitdiff')
707
+ ? window.fossil.page.diffControlContainer
708
+ : undefined;
709
+ const keySbsScroll = 'sync-diff-scroll' /* F.storage key for persistent user preference */;
672710
if( eToggleParent ){
673711
/* Add a checkbox to toggle sbs scroll sync. Remember that in
674712
order to be UI-consistent in the /vdiff page we have to ensure
675713
that the checkbox is to the LEFT of of its label. We store the
676714
sync-scroll preference in F.storage (not a cookie) so that it
@@ -677,11 +715,11 @@
677715
persists across page loads and different apps. */
678716
cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true));
679717
D.append(eToggleParent, D.append(
680718
D.addClass(D.create('span'), 'input-with-label'),
681719
D.append(D.create('label'),
682
- cbSync, "Sync side-by-side scrolling")
720
+ cbSync, "Scroll Sync")
683721
));
684722
cbSync.addEventListener('change', function(e){
685723
F.storage.set(keySbsScroll, e.target.checked);
686724
});
687725
}
688726
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -1,44 +1,93 @@
1 /**
2 diff-related JS APIs for fossil.
3 */
4 "use strict";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5 window.fossil.onPageLoad(function(){
6 /**
7 Adds toggle checkboxes to each file entry in the diff views for
8 /info and similar pages.
9 */
 
 
 
10 const D = window.fossil.dom;
11 const allToggles = [/*collection of all diff-toggle checkboxes */];
 
 
 
 
 
 
12 const addToggle = function(diffElem){
13 const sib = diffElem.previousElementSibling,
14 btnOne = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
15 if(!sib) return;
16 const lblToggle = D.append(D.label(null, " Toggle "), btnOne);
 
17 const wrapper = D.append(D.span(), lblToggle);
18 const btnAll = D.button("all");
19 btnAll.$cb = btnOne;
20 allToggles.push(btnOne);
21 D.append(sib, D.append(wrapper, lblToggle, D.text(" "), btnAll));
22 btnOne.addEventListener('change', function(){
23 diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
24 }, false);
25 btnAll.addEventListener('click', function(){
26 /* Toggle all entries to match this line's new state. Note that
27 we use click() instead of cb.checked=... so that the
28 on-change event handler fires. */
29 const checked = !this.$cb.checked;
30 allToggles.forEach( (cb)=>{
31 if(cb.checked!==checked) cb.click();
32 });
33 }, false);
34 };
35 if( !document.querySelector('body.fdiff') ){
36 /* Don't show the diff toggle button for /fdiff because it only
37 has a single file to show (and also a different DOM layout). */
38 document.querySelectorAll('table.diff').forEach(addToggle);
39 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40 });
41
42 window.fossil.onPageLoad(function(){
43 const F = window.fossil, D = F.dom;
44 const Diff = F.diff = {
@@ -651,26 +700,15 @@
651 /* Look for a parent element to hold the sbs-sync-scroll toggle
652 checkbox. This differs per page. If we don't find one, simply
653 elide that toggle and use whatever preference the user last
654 specified (defaulting to on). */
655 let cbSync /* scroll-sync checkbox */;
656 let eToggleParent /* element to put the sync-scroll checkbox in */;
657 const potentialParents = [ /* possible parents for the checkbox */
658 /* Put the most likely pages at the end, as array.pop() is more
659 efficient than array.shift() (see loop below). */
660 /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
661 /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
662 /* /fdiff */ 'body.fdiff form div.submenu',
663 /* /vdiff */ 'body.vdiff form div.submenu',
664 /* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu'
665 ];
666 while( potentialParents.length ){
667 if( (eToggleParent = document.querySelector(potentialParents.pop())) ){
668 break;
669 }
670 }
671 const keySbsScroll = 'sync-diff-scroll' /* F.storage key */;
672 if( eToggleParent ){
673 /* Add a checkbox to toggle sbs scroll sync. Remember that in
674 order to be UI-consistent in the /vdiff page we have to ensure
675 that the checkbox is to the LEFT of of its label. We store the
676 sync-scroll preference in F.storage (not a cookie) so that it
@@ -677,11 +715,11 @@
677 persists across page loads and different apps. */
678 cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true));
679 D.append(eToggleParent, D.append(
680 D.addClass(D.create('span'), 'input-with-label'),
681 D.append(D.create('label'),
682 cbSync, "Sync side-by-side scrolling")
683 ));
684 cbSync.addEventListener('change', function(e){
685 F.storage.set(keySbsScroll, e.target.checked);
686 });
687 }
688
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -1,44 +1,93 @@
1 /**
2 diff-related JS APIs for fossil.
3 */
4 "use strict";
5 /* Locate the UI element (if any) into which we can inject some diff-related
6 UI controls. */
7 window.fossil.onPageLoad(function(){
8 const potentialParents = window.fossil.page.diffControlContainers = [
9 /* CSS selectors for possible parents for injected diff-related UI
10 controls. */
11 /* Put the most likely pages at the end, as array.pop() is more
12 efficient than array.shift() (see loop below). */
13 /* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
14 /* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
15 /* /fdiff */ 'body.fdiff form div.submenu',
16 /* /vdiff */ 'body.vdiff form div.submenu',
17 /* /info, /vinfo, /ckout */ 'body.vinfo div.sectionmenu.info-changes-menu'
18 ];
19 window.fossil.page.diffControlContainer = undefined;
20 while( potentialParents.length ){
21 if( (window.fossil.page.diffControlContainer
22 = document.querySelector(potentialParents.pop())) ){
23 break;
24 }
25 }
26 });
27
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 }, false);
88 }
89 });
90
91 window.fossil.onPageLoad(function(){
92 const F = window.fossil, D = F.dom;
93 const Diff = F.diff = {
@@ -651,26 +700,15 @@
700 /* Look for a parent element to hold the sbs-sync-scroll toggle
701 checkbox. This differs per page. If we don't find one, simply
702 elide that toggle and use whatever preference the user last
703 specified (defaulting to on). */
704 let cbSync /* scroll-sync checkbox */;
705 let eToggleParent = /* element to put the sync-scroll checkbox in */
706 document.querySelector('table.diff.splitdiff')
707 ? window.fossil.page.diffControlContainer
708 : undefined;
709 const keySbsScroll = 'sync-diff-scroll' /* F.storage key for persistent user preference */;
 
 
 
 
 
 
 
 
 
 
 
710 if( eToggleParent ){
711 /* Add a checkbox to toggle sbs scroll sync. Remember that in
712 order to be UI-consistent in the /vdiff page we have to ensure
713 that the checkbox is to the LEFT of of its label. We store the
714 sync-scroll preference in F.storage (not a cookie) so that it
@@ -677,11 +715,11 @@
715 persists across page loads and different apps. */
716 cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true));
717 D.append(eToggleParent, D.append(
718 D.addClass(D.create('span'), 'input-with-label'),
719 D.append(D.create('label'),
720 cbSync, "Scroll Sync")
721 ));
722 cbSync.addEventListener('change', function(e){
723 F.storage.set(keySbsScroll, e.target.checked);
724 });
725 }
726
+249 -82
--- src/info.c
+++ src/info.c
@@ -609,53 +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 */
619
+ const char *zW; /* The "w" query parameter */
620
+ int nChng; /* Number of changes */
627621
Stmt q;
628622
629
- if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
630
- cgi_redirect("%R/home");
631
- return;
632
- }
633623
diffType = preferred_diff_type();
634624
pCfg = construct_diff_flags(diffType, &DCfg);
635
- vid = db_lget_int("checkout", 0);
636
- db_unprotect(PROTECT_ALL);
637
- vfile_check_signature(vid, CKSIG_ENOTFILE);
638
- db_protect_pop();
639
- style_set_current_feature("vinfo");
640
- zHostname = fossil_hostname();
641
- zCwd = file_getcwd(0,0);
642
- zHome = fossil_getenv("HOME");
643
- if( zHome ){
644
- int nHome = (int)strlen(zHome);
645
- if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
646
- zCwd = mprintf("~%s", zCwd+nHome);
647
- }
648
- }
649
- if( zHostname ){
650
- style_header("Checkout Status: %h on %h", zCwd, zHostname);
651
- }else{
652
- style_header("Checkout Status: %h", zCwd);
653
- }
654
- render_checkin_context(vid, 0, 0, 0);
655
- if( pCfg==0 ){
656
- style_finish_page();
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>
657629
return;
658630
}
659631
db_prepare(&q,
660632
/* 0 1 2 3 4 5 6 */
661633
"SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
@@ -663,15 +635,32 @@
663635
" WHERE vid=%d"
664636
" AND (deleted OR chnged OR rid==0)"
665637
" ORDER BY pathname /*scan*/",
666638
vid
667639
);
668
- if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){
669
- pCfg->diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
640
+ if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
641
+ DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
670642
}else{
671
- pCfg->diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
643
+ DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
672644
}
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
+ }
650
+ if( diffType!=2 ){
651
+ @ %z(chref("button","%R?diff=2%s",zW))Side-by-Side&nbsp;Diff</a>
652
+ }
653
+ if( diffType!=0 ){
654
+ if( *zW ){
655
+ @ %z(chref("button","%R?diff=%d",diffType))\
656
+ @ Show&nbsp;Whitespace&nbsp;Changes</a>
657
+ }else{
658
+ @ %z(chref("button","%R?diff=%d&w",diffType))Ignore&nbsp;Whitespace</a>
659
+ }
660
+ }
661
+ @ </div>
673662
while( db_step(&q)==SQLITE_ROW ){
674663
const char *zTreename = db_column_text(&q,0);
675664
int isDeleted = db_column_int(&q, 1);
676665
int isChnged = db_column_int(&q,2);
677666
int isNew = db_column_int(&q,3);
@@ -678,37 +667,39 @@
678667
int srcid = db_column_int(&q, 4);
679668
int isLink = db_column_int(&q, 5);
680669
const char *zUuid = db_column_text(&q, 6);
681670
int showDiff = 1;
682671
683
- pCfg->diffFlags &= (~DIFF_FILE_MASK);
672
+ DCfg.diffFlags &= (~DIFF_FILE_MASK);
673
+ @ <div class='file-change-line'><span>
684674
if( isDeleted ){
685
- @ <p>DELETED %h(zTreename)</p>
686
- pCfg->diffFlags |= DIFF_FILE_DELETED;
675
+ @ DELETED %h(zTreename)
676
+ DCfg.diffFlags |= DIFF_FILE_DELETED;
687677
showDiff = 0;
688678
}else if( file_access(zTreename, F_OK) ){
689
- @ <p>MISSING %h(zTreename)</p>
679
+ @ MISSING %h(zTreename)
690680
showDiff = 0;
691681
}else if( isNew ){
692
- @ <p>ADDED %h(zTreename)</p>
693
- pCfg->diffFlags |= DIFF_FILE_ADDED;
682
+ @ ADDED %h(zTreename)
683
+ DCfg.diffFlags |= DIFF_FILE_ADDED;
694684
srcid = 0;
695685
showDiff = 0;
696686
}else if( isChnged==3 ){
697
- @ <p>ADDED_BY_MERGE %h(zTreename)</p>
698
- pCfg->diffFlags |= DIFF_FILE_ADDED;
687
+ @ ADDED_BY_MERGE %h(zTreename)
688
+ DCfg.diffFlags |= DIFF_FILE_ADDED;
699689
srcid = 0;
700690
showDiff = 0;
701691
}else if( isChnged==5 ){
702
- @ <p>ADDED_BY_INTEGRATE %h(zTreename)</p>
703
- pCfg->diffFlags |= DIFF_FILE_ADDED;
692
+ @ ADDED_BY_INTEGRATE %h(zTreename)
693
+ DCfg.diffFlags |= DIFF_FILE_ADDED;
704694
srcid = 0;
705695
showDiff = 0;
706696
}else{
707
- @ <p>CHANGED %h(zTreename)</p>
697
+ @ CHANGED %h(zTreename)
708698
}
709
- if( showDiff ){
699
+ @ </span></div>
700
+ if( showDiff && pCfg ){
710701
Blob old, new;
711702
if( !isLink != !file_islink(zTreename) ){
712703
@ %s(DIFF_CANNOT_COMPUTE_SYMLINK)
713704
continue;
714705
}
@@ -725,10 +716,166 @@
725716
blob_reset(&new);
726717
}
727718
}
728719
db_finalize(&q);
729720
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
+ }
730877
style_finish_page();
731878
}
732879
733880
/*
734881
** WEBPAGE: vinfo
@@ -753,11 +900,10 @@
753900
const char *zParent; /* Hash of the parent check-in (if any) */
754901
const char *zRe; /* regex parameter */
755902
ReCompiled *pRe = 0; /* regex */
756903
const char *zW; /* URL param for ignoring whitespace */
757904
const char *zPage = "vinfo"; /* Page that shows diffs */
758
- const char *zPageHide = "ci"; /* Page that hides diffs */
759905
const char *zBrName; /* Branch name */
760906
DiffConfig DCfg,*pCfg; /* Type of diff */
761907
762908
login_check_credentials();
763909
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1023,21 +1169,17 @@
10231169
@ <div class="sectionmenu info-changes-menu">
10241170
/* ^^^ .info-changes-menu is used by diff scroll sync */
10251171
pCfg = construct_diff_flags(diffType, &DCfg);
10261172
DCfg.pRe = pRe;
10271173
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
1028
- if( diffType!=0 ){
1029
- @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
1030
- @ Hide&nbsp;Diffs</a>
1031
- }
10321174
if( diffType!=1 ){
10331175
@ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
1034
- @ Unified&nbsp;Diffs</a>
1176
+ @ Unified&nbsp;Diff</a>
10351177
}
10361178
if( diffType!=2 ){
10371179
@ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
1038
- @ Side-by-Side&nbsp;Diffs</a>
1180
+ @ Side-by-Side&nbsp;Diff</a>
10391181
}
10401182
if( diffType!=0 ){
10411183
if( *zW ){
10421184
@ %z(chref("button","%R/%s/%T",zPage,zName))
10431185
@ Show&nbsp;Whitespace&nbsp;Changes</a>
@@ -1393,13 +1535,10 @@
13931535
cgi_check_for_malice();
13941536
style_set_current_feature("vdiff");
13951537
if( zBranch==0 ){
13961538
style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
13971539
}
1398
- if( diffType!=0 ){
1399
- style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b%b", &qp, &qpGlob);
1400
- }
14011540
if( diffType!=2 ){
14021541
style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp,
14031542
&qpGlob);
14041543
}
14051544
if( diffType!=1 ) {
@@ -2058,10 +2197,16 @@
20582197
** WEBPAGE: jchunk hidden
20592198
** URL: /jchunk/HASH?from=N&to=M
20602199
**
20612200
** Return lines of text from a file as a JSON array - one entry in the
20622201
** 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.
20632208
**
20642209
** **Warning:** This is an internal-use-only interface that is subject to
20652210
** change at any moment. External application should not use this interface
20662211
** since the application will break when this interface changes, and this
20672212
** interface will undoubtedly change.
@@ -2073,10 +2218,11 @@
20732218
** ajax_route_error().
20742219
*/
20752220
void jchunk_page(void){
20762221
int rid = 0;
20772222
const char *zName = PD("name", "");
2223
+ int nName = (int)(strlen(zName)&0x7fffffff);
20782224
int iFrom = atoi(PD("from","0"));
20792225
int iTo = atoi(PD("to","0"));
20802226
int ln;
20812227
int go = 1;
20822228
const char *zSep;
@@ -2093,36 +2239,57 @@
20932239
cgi_check_for_malice();
20942240
if( !g.perm.Read ){
20952241
ajax_route_error(403, "Access requires Read permissions.");
20962242
return;
20972243
}
2098
-#if 1
2099
- /* Re-enable this block once this code is integrated somewhere into
2100
- the UI. */
2101
- rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
2102
- if( rid==0 ){
2103
- ajax_route_error(404, "Unknown artifact: %h", zName);
2104
- return;
2105
- }
2106
-#else
2107
- /* This impl is only to simplify "manual" testing via the JS
2108
- console. */
2109
- rid = symbolic_name_to_rid(zName, "*");
2110
- if( rid==0 ){
2111
- ajax_route_error(404, "Unknown artifact: %h", zName);
2112
- return;
2113
- }else if( rid<0 ){
2114
- ajax_route_error(418, "Ambiguous artifact name: %h", zName);
2115
- return;
2116
- }
2117
-#endif
21182244
if( iFrom<1 || iTo<iFrom ){
21192245
ajax_route_error(500, "Invalid line range from=%d, to=%d.",
21202246
iFrom, iTo);
21212247
return;
21222248
}
2123
- 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
+ }
21242291
g.isConst = 1;
21252292
cgi_set_content_type("application/json");
21262293
ln = 0;
21272294
while( go && ln<iFrom ){
21282295
go = blob_line(&content, &line);
21292296
--- src/info.c
+++ src/info.c
@@ -609,53 +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 Stmt q;
628
629 if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
630 cgi_redirect("%R/home");
631 return;
632 }
633 diffType = preferred_diff_type();
634 pCfg = construct_diff_flags(diffType, &DCfg);
635 vid = db_lget_int("checkout", 0);
636 db_unprotect(PROTECT_ALL);
637 vfile_check_signature(vid, CKSIG_ENOTFILE);
638 db_protect_pop();
639 style_set_current_feature("vinfo");
640 zHostname = fossil_hostname();
641 zCwd = file_getcwd(0,0);
642 zHome = fossil_getenv("HOME");
643 if( zHome ){
644 int nHome = (int)strlen(zHome);
645 if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
646 zCwd = mprintf("~%s", zCwd+nHome);
647 }
648 }
649 if( zHostname ){
650 style_header("Checkout Status: %h on %h", zCwd, zHostname);
651 }else{
652 style_header("Checkout Status: %h", zCwd);
653 }
654 render_checkin_context(vid, 0, 0, 0);
655 if( pCfg==0 ){
656 style_finish_page();
657 return;
658 }
659 db_prepare(&q,
660 /* 0 1 2 3 4 5 6 */
661 "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
@@ -663,15 +635,32 @@
663 " WHERE vid=%d"
664 " AND (deleted OR chnged OR rid==0)"
665 " ORDER BY pathname /*scan*/",
666 vid
667 );
668 if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){
669 pCfg->diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
670 }else{
671 pCfg->diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
672 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673 while( db_step(&q)==SQLITE_ROW ){
674 const char *zTreename = db_column_text(&q,0);
675 int isDeleted = db_column_int(&q, 1);
676 int isChnged = db_column_int(&q,2);
677 int isNew = db_column_int(&q,3);
@@ -678,37 +667,39 @@
678 int srcid = db_column_int(&q, 4);
679 int isLink = db_column_int(&q, 5);
680 const char *zUuid = db_column_text(&q, 6);
681 int showDiff = 1;
682
683 pCfg->diffFlags &= (~DIFF_FILE_MASK);
 
684 if( isDeleted ){
685 @ <p>DELETED %h(zTreename)</p>
686 pCfg->diffFlags |= DIFF_FILE_DELETED;
687 showDiff = 0;
688 }else if( file_access(zTreename, F_OK) ){
689 @ <p>MISSING %h(zTreename)</p>
690 showDiff = 0;
691 }else if( isNew ){
692 @ <p>ADDED %h(zTreename)</p>
693 pCfg->diffFlags |= DIFF_FILE_ADDED;
694 srcid = 0;
695 showDiff = 0;
696 }else if( isChnged==3 ){
697 @ <p>ADDED_BY_MERGE %h(zTreename)</p>
698 pCfg->diffFlags |= DIFF_FILE_ADDED;
699 srcid = 0;
700 showDiff = 0;
701 }else if( isChnged==5 ){
702 @ <p>ADDED_BY_INTEGRATE %h(zTreename)</p>
703 pCfg->diffFlags |= DIFF_FILE_ADDED;
704 srcid = 0;
705 showDiff = 0;
706 }else{
707 @ <p>CHANGED %h(zTreename)</p>
708 }
709 if( showDiff ){
 
710 Blob old, new;
711 if( !isLink != !file_islink(zTreename) ){
712 @ %s(DIFF_CANNOT_COMPUTE_SYMLINK)
713 continue;
714 }
@@ -725,10 +716,166 @@
725 blob_reset(&new);
726 }
727 }
728 db_finalize(&q);
729 append_diff_javascript(diffType);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730 style_finish_page();
731 }
732
733 /*
734 ** WEBPAGE: vinfo
@@ -753,11 +900,10 @@
753 const char *zParent; /* Hash of the parent check-in (if any) */
754 const char *zRe; /* regex parameter */
755 ReCompiled *pRe = 0; /* regex */
756 const char *zW; /* URL param for ignoring whitespace */
757 const char *zPage = "vinfo"; /* Page that shows diffs */
758 const char *zPageHide = "ci"; /* Page that hides diffs */
759 const char *zBrName; /* Branch name */
760 DiffConfig DCfg,*pCfg; /* Type of diff */
761
762 login_check_credentials();
763 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1023,21 +1169,17 @@
1023 @ <div class="sectionmenu info-changes-menu">
1024 /* ^^^ .info-changes-menu is used by diff scroll sync */
1025 pCfg = construct_diff_flags(diffType, &DCfg);
1026 DCfg.pRe = pRe;
1027 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
1028 if( diffType!=0 ){
1029 @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
1030 @ Hide&nbsp;Diffs</a>
1031 }
1032 if( diffType!=1 ){
1033 @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
1034 @ Unified&nbsp;Diffs</a>
1035 }
1036 if( diffType!=2 ){
1037 @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
1038 @ Side-by-Side&nbsp;Diffs</a>
1039 }
1040 if( diffType!=0 ){
1041 if( *zW ){
1042 @ %z(chref("button","%R/%s/%T",zPage,zName))
1043 @ Show&nbsp;Whitespace&nbsp;Changes</a>
@@ -1393,13 +1535,10 @@
1393 cgi_check_for_malice();
1394 style_set_current_feature("vdiff");
1395 if( zBranch==0 ){
1396 style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
1397 }
1398 if( diffType!=0 ){
1399 style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b%b", &qp, &qpGlob);
1400 }
1401 if( diffType!=2 ){
1402 style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp,
1403 &qpGlob);
1404 }
1405 if( diffType!=1 ) {
@@ -2058,10 +2197,16 @@
2058 ** WEBPAGE: jchunk hidden
2059 ** URL: /jchunk/HASH?from=N&to=M
2060 **
2061 ** Return lines of text from a file as a JSON array - one entry in the
2062 ** array for each line of text.
 
 
 
 
 
 
2063 **
2064 ** **Warning:** This is an internal-use-only interface that is subject to
2065 ** change at any moment. External application should not use this interface
2066 ** since the application will break when this interface changes, and this
2067 ** interface will undoubtedly change.
@@ -2073,10 +2218,11 @@
2073 ** ajax_route_error().
2074 */
2075 void jchunk_page(void){
2076 int rid = 0;
2077 const char *zName = PD("name", "");
 
2078 int iFrom = atoi(PD("from","0"));
2079 int iTo = atoi(PD("to","0"));
2080 int ln;
2081 int go = 1;
2082 const char *zSep;
@@ -2093,36 +2239,57 @@
2093 cgi_check_for_malice();
2094 if( !g.perm.Read ){
2095 ajax_route_error(403, "Access requires Read permissions.");
2096 return;
2097 }
2098 #if 1
2099 /* Re-enable this block once this code is integrated somewhere into
2100 the UI. */
2101 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
2102 if( rid==0 ){
2103 ajax_route_error(404, "Unknown artifact: %h", zName);
2104 return;
2105 }
2106 #else
2107 /* This impl is only to simplify "manual" testing via the JS
2108 console. */
2109 rid = symbolic_name_to_rid(zName, "*");
2110 if( rid==0 ){
2111 ajax_route_error(404, "Unknown artifact: %h", zName);
2112 return;
2113 }else if( rid<0 ){
2114 ajax_route_error(418, "Ambiguous artifact name: %h", zName);
2115 return;
2116 }
2117 #endif
2118 if( iFrom<1 || iTo<iFrom ){
2119 ajax_route_error(500, "Invalid line range from=%d, to=%d.",
2120 iFrom, iTo);
2121 return;
2122 }
2123 content_get(rid, &content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2124 g.isConst = 1;
2125 cgi_set_content_type("application/json");
2126 ln = 0;
2127 while( go && ln<iFrom ){
2128 go = blob_line(&content, &line);
2129
--- src/info.c
+++ src/info.c
@@ -609,53 +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"
@@ -663,15 +635,32 @@
635 " WHERE vid=%d"
636 " AND (deleted OR chnged OR rid==0)"
637 " ORDER BY pathname /*scan*/",
638 vid
639 );
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 }
650 if( diffType!=2 ){
651 @ %z(chref("button","%R?diff=2%s",zW))Side-by-Side&nbsp;Diff</a>
652 }
653 if( diffType!=0 ){
654 if( *zW ){
655 @ %z(chref("button","%R?diff=%d",diffType))\
656 @ Show&nbsp;Whitespace&nbsp;Changes</a>
657 }else{
658 @ %z(chref("button","%R?diff=%d&w",diffType))Ignore&nbsp;Whitespace</a>
659 }
660 }
661 @ </div>
662 while( db_step(&q)==SQLITE_ROW ){
663 const char *zTreename = db_column_text(&q,0);
664 int isDeleted = db_column_int(&q, 1);
665 int isChnged = db_column_int(&q,2);
666 int isNew = db_column_int(&q,3);
@@ -678,37 +667,39 @@
667 int srcid = db_column_int(&q, 4);
668 int isLink = db_column_int(&q, 5);
669 const char *zUuid = db_column_text(&q, 6);
670 int showDiff = 1;
671
672 DCfg.diffFlags &= (~DIFF_FILE_MASK);
673 @ <div class='file-change-line'><span>
674 if( isDeleted ){
675 @ DELETED %h(zTreename)
676 DCfg.diffFlags |= DIFF_FILE_DELETED;
677 showDiff = 0;
678 }else if( file_access(zTreename, F_OK) ){
679 @ MISSING %h(zTreename)
680 showDiff = 0;
681 }else if( isNew ){
682 @ ADDED %h(zTreename)
683 DCfg.diffFlags |= DIFF_FILE_ADDED;
684 srcid = 0;
685 showDiff = 0;
686 }else if( isChnged==3 ){
687 @ ADDED_BY_MERGE %h(zTreename)
688 DCfg.diffFlags |= DIFF_FILE_ADDED;
689 srcid = 0;
690 showDiff = 0;
691 }else if( isChnged==5 ){
692 @ ADDED_BY_INTEGRATE %h(zTreename)
693 DCfg.diffFlags |= DIFF_FILE_ADDED;
694 srcid = 0;
695 showDiff = 0;
696 }else{
697 @ CHANGED %h(zTreename)
698 }
699 @ </span></div>
700 if( showDiff && pCfg ){
701 Blob old, new;
702 if( !isLink != !file_islink(zTreename) ){
703 @ %s(DIFF_CANNOT_COMPUTE_SYMLINK)
704 continue;
705 }
@@ -725,10 +716,166 @@
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
@@ -753,11 +900,10 @@
900 const char *zParent; /* Hash of the parent check-in (if any) */
901 const char *zRe; /* regex parameter */
902 ReCompiled *pRe = 0; /* regex */
903 const char *zW; /* URL param for ignoring whitespace */
904 const char *zPage = "vinfo"; /* Page that shows diffs */
 
905 const char *zBrName; /* Branch name */
906 DiffConfig DCfg,*pCfg; /* Type of diff */
907
908 login_check_credentials();
909 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1023,21 +1169,17 @@
1169 @ <div class="sectionmenu info-changes-menu">
1170 /* ^^^ .info-changes-menu is used by diff scroll sync */
1171 pCfg = construct_diff_flags(diffType, &DCfg);
1172 DCfg.pRe = pRe;
1173 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
 
 
 
 
1174 if( diffType!=1 ){
1175 @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
1176 @ Unified&nbsp;Diff</a>
1177 }
1178 if( diffType!=2 ){
1179 @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
1180 @ Side-by-Side&nbsp;Diff</a>
1181 }
1182 if( diffType!=0 ){
1183 if( *zW ){
1184 @ %z(chref("button","%R/%s/%T",zPage,zName))
1185 @ Show&nbsp;Whitespace&nbsp;Changes</a>
@@ -1393,13 +1535,10 @@
1535 cgi_check_for_malice();
1536 style_set_current_feature("vdiff");
1537 if( zBranch==0 ){
1538 style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
1539 }
 
 
 
1540 if( diffType!=2 ){
1541 style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp,
1542 &qpGlob);
1543 }
1544 if( diffType!=1 ) {
@@ -2058,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.
@@ -2073,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;
@@ -2093,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