Fossil SCM
Sync with trunk.
Commit
5fbb14f73aaf0c7e02cf12f1a1d1cdfb30e44b13b76910c7e4796d2ef5d92122
Parent
4d7277762fab0b2…
12 files changed
+2
-2
+5
-1
+92
-16
+92
-16
+24
-13
+24
-13
+207
-59
+10
+12
-8
+42
-3
+11
-4
+7
-2
+2
-2
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1563,17 +1563,17 @@ | ||
| 1563 | 1563 | break; |
| 1564 | 1564 | } |
| 1565 | 1565 | diffFiles[i].nName = strlen(diffFiles[i].zName); |
| 1566 | 1566 | diffFiles[i].nUsed = 0; |
| 1567 | 1567 | } |
| 1568 | - diff_against_disk(0, &DCfg, diffFiles, &prompt); | |
| 1568 | + diff_version_to_checkout(0, &DCfg, diffFiles, &prompt); | |
| 1569 | 1569 | for( i=0; diffFiles[i].zName; ++i ){ |
| 1570 | 1570 | fossil_free(diffFiles[i].zName); |
| 1571 | 1571 | } |
| 1572 | 1572 | fossil_free(diffFiles); |
| 1573 | 1573 | }else{ |
| 1574 | - diff_against_disk(0, &DCfg, 0, &prompt); | |
| 1574 | + diff_version_to_checkout(0, &DCfg, 0, &prompt); | |
| 1575 | 1575 | } |
| 1576 | 1576 | } |
| 1577 | 1577 | prompt_for_user_comment(pComment, &prompt); |
| 1578 | 1578 | blob_reset(&prompt); |
| 1579 | 1579 | } |
| 1580 | 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_against_disk(0, &DCfg, diffFiles, &prompt); |
| 1569 | for( i=0; diffFiles[i].zName; ++i ){ |
| 1570 | fossil_free(diffFiles[i].zName); |
| 1571 | } |
| 1572 | fossil_free(diffFiles); |
| 1573 | }else{ |
| 1574 | diff_against_disk(0, &DCfg, 0, &prompt); |
| 1575 | } |
| 1576 | } |
| 1577 | prompt_for_user_comment(pComment, &prompt); |
| 1578 | blob_reset(&prompt); |
| 1579 | } |
| 1580 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1563,17 +1563,17 @@ | |
| 1563 | break; |
| 1564 | } |
| 1565 | diffFiles[i].nName = strlen(diffFiles[i].zName); |
| 1566 | diffFiles[i].nUsed = 0; |
| 1567 | } |
| 1568 | diff_version_to_checkout(0, &DCfg, diffFiles, &prompt); |
| 1569 | for( i=0; diffFiles[i].zName; ++i ){ |
| 1570 | fossil_free(diffFiles[i].zName); |
| 1571 | } |
| 1572 | fossil_free(diffFiles); |
| 1573 | }else{ |
| 1574 | diff_version_to_checkout(0, &DCfg, 0, &prompt); |
| 1575 | } |
| 1576 | } |
| 1577 | prompt_for_user_comment(pComment, &prompt); |
| 1578 | blob_reset(&prompt); |
| 1579 | } |
| 1580 |
+5
-1
| --- src/diff.tcl | ||
| +++ src/diff.tcl | ||
| @@ -110,11 +110,15 @@ | ||
| 110 | 110 | |
| 111 | 111 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 112 | 112 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 113 | 113 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| 114 | 114 | 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 | + } | |
| 116 | 120 | set fB {current check-out} |
| 117 | 121 | if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]} |
| 118 | 122 | if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]} |
| 119 | 123 | if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"} |
| 120 | 124 | if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]} |
| 121 | 125 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -110,11 +110,15 @@ | |
| 110 | |
| 111 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 112 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 113 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| 114 | set checkinIndex [lsearch -glob $fossilcmd *-checkin] |
| 115 | set fA {base check-in} |
| 116 | set fB {current check-out} |
| 117 | if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]} |
| 118 | if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]} |
| 119 | if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"} |
| 120 | if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]} |
| 121 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -110,11 +110,15 @@ | |
| 110 | |
| 111 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 112 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 113 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| 114 | set checkinIndex [lsearch -glob $fossilcmd *-checkin] |
| 115 | if {[string match *?--external-baseline* $fossilcmd]} { |
| 116 | set fA {external baseline} |
| 117 | } else { |
| 118 | set fA {base check-in} |
| 119 | } |
| 120 | set fB {current check-out} |
| 121 | if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]} |
| 122 | if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]} |
| 123 | if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"} |
| 124 | if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]} |
| 125 |
+92
-16
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -764,26 +764,27 @@ | ||
| 764 | 764 | blob_reset(&file); |
| 765 | 765 | return rc; |
| 766 | 766 | } |
| 767 | 767 | |
| 768 | 768 | /* |
| 769 | -** Run a diff between the version zFrom and files on disk. zFrom might | |
| 770 | -** be NULL which means to simply show the difference between the edited | |
| 771 | -** files on disk and the check-out on which they are based. | |
| 769 | +** Run a diff between the version zFrom and files on disk in the current | |
| 770 | +** working checkout. zFrom might be NULL which means to simply show the | |
| 771 | +** difference between the edited files on disk and the check-out on which | |
| 772 | +** they are based. | |
| 772 | 773 | ** |
| 773 | 774 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 774 | 775 | ** command zDiffCmd to do the diffing. |
| 775 | 776 | ** |
| 776 | 777 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 777 | 778 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 778 | 779 | ** will be skipped in addition to files that may contain binary content. |
| 779 | 780 | */ |
| 780 | -void diff_against_disk( | |
| 781 | +void diff_version_to_checkout( | |
| 781 | 782 | const char *zFrom, /* Version to difference from */ |
| 782 | 783 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 783 | 784 | FileDirList *pFileDir, /* Which files to diff */ |
| 784 | - Blob *pOut /* Blob to output diff instead of stdout */ | |
| 785 | + Blob *pOut /* Blob to output diff instead of stdout */ | |
| 785 | 786 | ){ |
| 786 | 787 | int vid; |
| 787 | 788 | Blob sql; |
| 788 | 789 | Stmt q; |
| 789 | 790 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -908,20 +909,20 @@ | ||
| 908 | 909 | db_finalize(&q); |
| 909 | 910 | db_end_transaction(1); /* ROLLBACK */ |
| 910 | 911 | } |
| 911 | 912 | |
| 912 | 913 | /* |
| 913 | -** Run a diff between the undo buffer and files on disk. | |
| 914 | +** Run a diff from the undo buffer to files on disk. | |
| 914 | 915 | ** |
| 915 | 916 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 916 | 917 | ** command zDiffCmd to do the diffing. |
| 917 | 918 | ** |
| 918 | 919 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 919 | 920 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 920 | 921 | ** will be skipped in addition to files that may contain binary content. |
| 921 | 922 | */ |
| 922 | -static void diff_against_undo( | |
| 923 | +static void diff_undo_to_checkout( | |
| 923 | 924 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 924 | 925 | FileDirList *pFileDir /* List of files and directories to diff */ |
| 925 | 926 | ){ |
| 926 | 927 | Stmt q; |
| 927 | 928 | Blob content; |
| @@ -1068,10 +1069,67 @@ | ||
| 1068 | 1069 | } |
| 1069 | 1070 | } |
| 1070 | 1071 | manifest_destroy(pFrom); |
| 1071 | 1072 | manifest_destroy(pTo); |
| 1072 | 1073 | } |
| 1074 | + | |
| 1075 | +/* | |
| 1076 | +** Compute the difference from an external tree of files to the current | |
| 1077 | +** working checkout with its edits. | |
| 1078 | +** | |
| 1079 | +** To put it another way: Every managed file in the current working | |
| 1080 | +** checkout is compared to the file with same name under zExternBase. The | |
| 1081 | +** zExternBase files are on the left and the files in the current working | |
| 1082 | +** directory are on the right. | |
| 1083 | +*/ | |
| 1084 | +void diff_externbase_to_checkout( | |
| 1085 | + const char *zExternBase, /* Remote tree to use as the baseline */ | |
| 1086 | + DiffConfig *pCfg, /* Diff settings */ | |
| 1087 | + FileDirList *pFileDir /* Only look at these files */ | |
| 1088 | +){ | |
| 1089 | + int vid; | |
| 1090 | + Stmt q; | |
| 1091 | + | |
| 1092 | + vid = db_lget_int("checkout",0); | |
| 1093 | + if( file_isdir(zExternBase, ExtFILE)!=1 ){ | |
| 1094 | + fossil_fatal("\"%s\" is not a directory", zExternBase); | |
| 1095 | + } | |
| 1096 | + db_prepare(&q, | |
| 1097 | + "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", | |
| 1098 | + vid | |
| 1099 | + ); | |
| 1100 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1101 | + const char *zFile; /* Name of file in the repository */ | |
| 1102 | + char *zLhs; /* Full name of left-hand side file */ | |
| 1103 | + char *zRhs; /* Full name of right-hand side file */ | |
| 1104 | + Blob rhs; /* Full text of RHS */ | |
| 1105 | + Blob lhs; /* Full text of LHS */ | |
| 1106 | + | |
| 1107 | + zFile = db_column_text(&q,0); | |
| 1108 | + if( !file_dir_match(pFileDir, zFile) ) continue; | |
| 1109 | + zLhs = mprintf("%s/%s", zExternBase, zFile); | |
| 1110 | + zRhs = mprintf("%s%s", g.zLocalRoot, zFile); | |
| 1111 | + if( file_size(zLhs, ExtFILE)<0 ){ | |
| 1112 | + blob_zero(&lhs); | |
| 1113 | + }else{ | |
| 1114 | + blob_read_from_file(&lhs, zLhs, ExtFILE); | |
| 1115 | + } | |
| 1116 | + blob_read_from_file(&rhs, zRhs, ExtFILE); | |
| 1117 | + if( blob_size(&lhs)!=blob_size(&rhs) | |
| 1118 | + || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 | |
| 1119 | + ){ | |
| 1120 | + diff_print_index(zFile, pCfg, 0); | |
| 1121 | + diff_file_mem(&lhs, &rhs, zFile, pCfg); | |
| 1122 | + } | |
| 1123 | + blob_reset(&lhs); | |
| 1124 | + blob_reset(&rhs); | |
| 1125 | + fossil_free(zLhs); | |
| 1126 | + fossil_free(zRhs); | |
| 1127 | + } | |
| 1128 | + db_finalize(&q); | |
| 1129 | +} | |
| 1130 | + | |
| 1073 | 1131 | |
| 1074 | 1132 | /* |
| 1075 | 1133 | ** Return the name of the external diff command, or return NULL if |
| 1076 | 1134 | ** no external diff command is defined. |
| 1077 | 1135 | */ |
| @@ -1204,10 +1262,14 @@ | ||
| 1204 | 1262 | ** option specifies the check-in from which the second version of the file |
| 1205 | 1263 | ** or files is taken. If there is no "--to" option then the (possibly edited) |
| 1206 | 1264 | ** files in the current check-out are used. The "--checkin VERSION" option |
| 1207 | 1265 | ** shows the changes made by check-in VERSION relative to its primary parent. |
| 1208 | 1266 | ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. |
| 1267 | +** | |
| 1268 | +** With the "--from VERSION" option, if VERSION is actually a directory name | |
| 1269 | +** (not a tag or check-in hash) then the files under that directory are used | |
| 1270 | +** as the baseline for the diff. | |
| 1209 | 1271 | ** |
| 1210 | 1272 | ** The "-i" command-line option forces the use of Fossil's own internal |
| 1211 | 1273 | ** diff logic rather than any external diff program that might be configured |
| 1212 | 1274 | ** using the "setting" command. If no external diff program is configured, |
| 1213 | 1275 | ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into |
| @@ -1236,11 +1298,13 @@ | ||
| 1236 | 1298 | ** with negative N meaning show all content |
| 1237 | 1299 | ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML |
| 1238 | 1300 | ** --diff-binary BOOL Include binary files with external commands |
| 1239 | 1301 | ** --exec-abs-paths Force absolute path names on external commands |
| 1240 | 1302 | ** --exec-rel-paths Force relative path names on external commands |
| 1241 | -** -r|--from VERSION Select VERSION as source for the diff | |
| 1303 | +** -r|--from VERSION Use VERSION as the baseline for the diff, or | |
| 1304 | +** if VERSION is a directory name, use files in | |
| 1305 | +** that directory as the baseline. | |
| 1242 | 1306 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 1243 | 1307 | ** -i|--internal Use internal diff logic |
| 1244 | 1308 | ** --invert Invert the diff |
| 1245 | 1309 | ** --json Output formatted as JSON |
| 1246 | 1310 | ** -n|--linenum Show line numbers |
| @@ -1250,11 +1314,11 @@ | ||
| 1250 | 1314 | ** --strip-trailing-cr Strip trailing CR |
| 1251 | 1315 | ** --tcl Tcl-formatted output used internally by --tk |
| 1252 | 1316 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1253 | 1317 | ** --tk Launch a Tcl/Tk GUI for display |
| 1254 | 1318 | ** --to VERSION Select VERSION as target for the diff |
| 1255 | -** --undo Diff against the "undo" buffer | |
| 1319 | +** --undo Use the undo buffer as the baseline | |
| 1256 | 1320 | ** --unified Unified diff |
| 1257 | 1321 | ** -v|--verbose Output complete text of added or deleted files |
| 1258 | 1322 | ** -h|--versions Show compared versions in the diff header |
| 1259 | 1323 | ** --webpage Format output as a stand-alone HTML webpage |
| 1260 | 1324 | ** -W|--width N Width of lines in side-by-side diff |
| @@ -1267,10 +1331,11 @@ | ||
| 1267 | 1331 | const char *zCheckin; /* Check-in version number */ |
| 1268 | 1332 | const char *zBranch; /* Branch to diff */ |
| 1269 | 1333 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1270 | 1334 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1271 | 1335 | DiffConfig DCfg; /* Diff configuration object */ |
| 1336 | + int bFromIsDir = 0; /* True if zFrom is a directory name */ | |
| 1272 | 1337 | |
| 1273 | 1338 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1274 | 1339 | diff_tk("diff", 2); |
| 1275 | 1340 | return; |
| 1276 | 1341 | } |
| @@ -1278,11 +1343,11 @@ | ||
| 1278 | 1343 | zFrom = find_option("from", "r", 1); |
| 1279 | 1344 | zTo = find_option("to", 0, 1); |
| 1280 | 1345 | zCheckin = find_option("checkin", "ci", 1); |
| 1281 | 1346 | zBranch = find_option("branch", 0, 1); |
| 1282 | 1347 | againstUndo = find_option("undo",0,0)!=0; |
| 1283 | - if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ | |
| 1348 | + if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ | |
| 1284 | 1349 | fossil_fatal("cannot use --undo together with --from, --to, --checkin," |
| 1285 | 1350 | " or --branch"); |
| 1286 | 1351 | } |
| 1287 | 1352 | if( zBranch ){ |
| 1288 | 1353 | if( zTo || zFrom || zCheckin ){ |
| @@ -1289,15 +1354,13 @@ | ||
| 1289 | 1354 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1290 | 1355 | } |
| 1291 | 1356 | zTo = zBranch; |
| 1292 | 1357 | zFrom = mprintf("root:%s", zBranch); |
| 1293 | 1358 | } |
| 1294 | - if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ | |
| 1359 | + if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ | |
| 1295 | 1360 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1296 | 1361 | } |
| 1297 | - diff_options(&DCfg, isGDiff, 0); | |
| 1298 | - determine_exec_relative_option(1); | |
| 1299 | 1362 | if( 0==zCheckin ){ |
| 1300 | 1363 | if( zTo==0 || againstUndo ){ |
| 1301 | 1364 | db_must_be_within_tree(); |
| 1302 | 1365 | }else if( zFrom==0 ){ |
| 1303 | 1366 | fossil_fatal("must use --from if --to is present"); |
| @@ -1305,10 +1368,21 @@ | ||
| 1305 | 1368 | db_find_and_open_repository(0, 0); |
| 1306 | 1369 | } |
| 1307 | 1370 | }else{ |
| 1308 | 1371 | db_find_and_open_repository(0, 0); |
| 1309 | 1372 | } |
| 1373 | + determine_exec_relative_option(1); | |
| 1374 | + if( zFrom!=file_tail(zFrom) | |
| 1375 | + && file_isdir(zFrom, ExtFILE)==1 | |
| 1376 | + && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) | |
| 1377 | + ){ | |
| 1378 | + bFromIsDir = 1; | |
| 1379 | + if( zTo ){ | |
| 1380 | + fossil_fatal("cannot use --to together with \"--from PATH\""); | |
| 1381 | + } | |
| 1382 | + } | |
| 1383 | + diff_options(&DCfg, isGDiff, 0); | |
| 1310 | 1384 | verify_all_options(); |
| 1311 | 1385 | g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; |
| 1312 | 1386 | if( g.argc>=3 ){ |
| 1313 | 1387 | int i; |
| 1314 | 1388 | Blob fname; |
| @@ -1337,18 +1411,20 @@ | ||
| 1337 | 1411 | if( zFrom==0 ){ |
| 1338 | 1412 | fossil_fatal("check-in %s has no parent", zTo); |
| 1339 | 1413 | } |
| 1340 | 1414 | } |
| 1341 | 1415 | diff_begin(&DCfg); |
| 1342 | - if( againstUndo ){ | |
| 1416 | + if( bFromIsDir ){ | |
| 1417 | + diff_externbase_to_checkout(zFrom, &DCfg, pFileDir); | |
| 1418 | + }else if( againstUndo ){ | |
| 1343 | 1419 | if( db_lget_int("undo_available",0)==0 ){ |
| 1344 | 1420 | fossil_print("No undo or redo is available\n"); |
| 1345 | 1421 | return; |
| 1346 | 1422 | } |
| 1347 | - diff_against_undo(&DCfg, pFileDir); | |
| 1423 | + diff_undo_to_checkout(&DCfg, pFileDir); | |
| 1348 | 1424 | }else if( zTo==0 ){ |
| 1349 | - diff_against_disk(zFrom, &DCfg, pFileDir, 0); | |
| 1425 | + diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0); | |
| 1350 | 1426 | }else{ |
| 1351 | 1427 | diff_two_versions(zFrom, zTo, &DCfg, pFileDir); |
| 1352 | 1428 | } |
| 1353 | 1429 | if( pFileDir ){ |
| 1354 | 1430 | int i; |
| 1355 | 1431 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -764,26 +764,27 @@ | |
| 764 | blob_reset(&file); |
| 765 | return rc; |
| 766 | } |
| 767 | |
| 768 | /* |
| 769 | ** Run a diff between the version zFrom and files on disk. zFrom might |
| 770 | ** be NULL which means to simply show the difference between the edited |
| 771 | ** files on disk and the check-out on which they are based. |
| 772 | ** |
| 773 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 774 | ** command zDiffCmd to do the diffing. |
| 775 | ** |
| 776 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 777 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 778 | ** will be skipped in addition to files that may contain binary content. |
| 779 | */ |
| 780 | void diff_against_disk( |
| 781 | const char *zFrom, /* Version to difference from */ |
| 782 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 783 | FileDirList *pFileDir, /* Which files to diff */ |
| 784 | Blob *pOut /* Blob to output diff instead of stdout */ |
| 785 | ){ |
| 786 | int vid; |
| 787 | Blob sql; |
| 788 | Stmt q; |
| 789 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -908,20 +909,20 @@ | |
| 908 | db_finalize(&q); |
| 909 | db_end_transaction(1); /* ROLLBACK */ |
| 910 | } |
| 911 | |
| 912 | /* |
| 913 | ** Run a diff between the undo buffer and files on disk. |
| 914 | ** |
| 915 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 916 | ** command zDiffCmd to do the diffing. |
| 917 | ** |
| 918 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 919 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 920 | ** will be skipped in addition to files that may contain binary content. |
| 921 | */ |
| 922 | static void diff_against_undo( |
| 923 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 924 | FileDirList *pFileDir /* List of files and directories to diff */ |
| 925 | ){ |
| 926 | Stmt q; |
| 927 | Blob content; |
| @@ -1068,10 +1069,67 @@ | |
| 1068 | } |
| 1069 | } |
| 1070 | manifest_destroy(pFrom); |
| 1071 | manifest_destroy(pTo); |
| 1072 | } |
| 1073 | |
| 1074 | /* |
| 1075 | ** Return the name of the external diff command, or return NULL if |
| 1076 | ** no external diff command is defined. |
| 1077 | */ |
| @@ -1204,10 +1262,14 @@ | |
| 1204 | ** option specifies the check-in from which the second version of the file |
| 1205 | ** or files is taken. If there is no "--to" option then the (possibly edited) |
| 1206 | ** files in the current check-out are used. The "--checkin VERSION" option |
| 1207 | ** shows the changes made by check-in VERSION relative to its primary parent. |
| 1208 | ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. |
| 1209 | ** |
| 1210 | ** The "-i" command-line option forces the use of Fossil's own internal |
| 1211 | ** diff logic rather than any external diff program that might be configured |
| 1212 | ** using the "setting" command. If no external diff program is configured, |
| 1213 | ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into |
| @@ -1236,11 +1298,13 @@ | |
| 1236 | ** with negative N meaning show all content |
| 1237 | ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML |
| 1238 | ** --diff-binary BOOL Include binary files with external commands |
| 1239 | ** --exec-abs-paths Force absolute path names on external commands |
| 1240 | ** --exec-rel-paths Force relative path names on external commands |
| 1241 | ** -r|--from VERSION Select VERSION as source for the diff |
| 1242 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 1243 | ** -i|--internal Use internal diff logic |
| 1244 | ** --invert Invert the diff |
| 1245 | ** --json Output formatted as JSON |
| 1246 | ** -n|--linenum Show line numbers |
| @@ -1250,11 +1314,11 @@ | |
| 1250 | ** --strip-trailing-cr Strip trailing CR |
| 1251 | ** --tcl Tcl-formatted output used internally by --tk |
| 1252 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1253 | ** --tk Launch a Tcl/Tk GUI for display |
| 1254 | ** --to VERSION Select VERSION as target for the diff |
| 1255 | ** --undo Diff against the "undo" buffer |
| 1256 | ** --unified Unified diff |
| 1257 | ** -v|--verbose Output complete text of added or deleted files |
| 1258 | ** -h|--versions Show compared versions in the diff header |
| 1259 | ** --webpage Format output as a stand-alone HTML webpage |
| 1260 | ** -W|--width N Width of lines in side-by-side diff |
| @@ -1267,10 +1331,11 @@ | |
| 1267 | const char *zCheckin; /* Check-in version number */ |
| 1268 | const char *zBranch; /* Branch to diff */ |
| 1269 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1270 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1271 | DiffConfig DCfg; /* Diff configuration object */ |
| 1272 | |
| 1273 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1274 | diff_tk("diff", 2); |
| 1275 | return; |
| 1276 | } |
| @@ -1278,11 +1343,11 @@ | |
| 1278 | zFrom = find_option("from", "r", 1); |
| 1279 | zTo = find_option("to", 0, 1); |
| 1280 | zCheckin = find_option("checkin", "ci", 1); |
| 1281 | zBranch = find_option("branch", 0, 1); |
| 1282 | againstUndo = find_option("undo",0,0)!=0; |
| 1283 | if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ |
| 1284 | fossil_fatal("cannot use --undo together with --from, --to, --checkin," |
| 1285 | " or --branch"); |
| 1286 | } |
| 1287 | if( zBranch ){ |
| 1288 | if( zTo || zFrom || zCheckin ){ |
| @@ -1289,15 +1354,13 @@ | |
| 1289 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1290 | } |
| 1291 | zTo = zBranch; |
| 1292 | zFrom = mprintf("root:%s", zBranch); |
| 1293 | } |
| 1294 | if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ |
| 1295 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1296 | } |
| 1297 | diff_options(&DCfg, isGDiff, 0); |
| 1298 | determine_exec_relative_option(1); |
| 1299 | if( 0==zCheckin ){ |
| 1300 | if( zTo==0 || againstUndo ){ |
| 1301 | db_must_be_within_tree(); |
| 1302 | }else if( zFrom==0 ){ |
| 1303 | fossil_fatal("must use --from if --to is present"); |
| @@ -1305,10 +1368,21 @@ | |
| 1305 | db_find_and_open_repository(0, 0); |
| 1306 | } |
| 1307 | }else{ |
| 1308 | db_find_and_open_repository(0, 0); |
| 1309 | } |
| 1310 | verify_all_options(); |
| 1311 | g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; |
| 1312 | if( g.argc>=3 ){ |
| 1313 | int i; |
| 1314 | Blob fname; |
| @@ -1337,18 +1411,20 @@ | |
| 1337 | if( zFrom==0 ){ |
| 1338 | fossil_fatal("check-in %s has no parent", zTo); |
| 1339 | } |
| 1340 | } |
| 1341 | diff_begin(&DCfg); |
| 1342 | if( againstUndo ){ |
| 1343 | if( db_lget_int("undo_available",0)==0 ){ |
| 1344 | fossil_print("No undo or redo is available\n"); |
| 1345 | return; |
| 1346 | } |
| 1347 | diff_against_undo(&DCfg, pFileDir); |
| 1348 | }else if( zTo==0 ){ |
| 1349 | diff_against_disk(zFrom, &DCfg, pFileDir, 0); |
| 1350 | }else{ |
| 1351 | diff_two_versions(zFrom, zTo, &DCfg, pFileDir); |
| 1352 | } |
| 1353 | if( pFileDir ){ |
| 1354 | int i; |
| 1355 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -764,26 +764,27 @@ | |
| 764 | blob_reset(&file); |
| 765 | return rc; |
| 766 | } |
| 767 | |
| 768 | /* |
| 769 | ** Run a diff between the version zFrom and files on disk in the current |
| 770 | ** working checkout. zFrom might be NULL which means to simply show the |
| 771 | ** difference between the edited files on disk and the check-out on which |
| 772 | ** they are based. |
| 773 | ** |
| 774 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 775 | ** command zDiffCmd to do the diffing. |
| 776 | ** |
| 777 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 778 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 779 | ** will be skipped in addition to files that may contain binary content. |
| 780 | */ |
| 781 | void diff_version_to_checkout( |
| 782 | const char *zFrom, /* Version to difference from */ |
| 783 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 784 | FileDirList *pFileDir, /* Which files to diff */ |
| 785 | Blob *pOut /* Blob to output diff instead of stdout */ |
| 786 | ){ |
| 787 | int vid; |
| 788 | Blob sql; |
| 789 | Stmt q; |
| 790 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -908,20 +909,20 @@ | |
| 909 | db_finalize(&q); |
| 910 | db_end_transaction(1); /* ROLLBACK */ |
| 911 | } |
| 912 | |
| 913 | /* |
| 914 | ** Run a diff from the undo buffer to files on disk. |
| 915 | ** |
| 916 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 917 | ** command zDiffCmd to do the diffing. |
| 918 | ** |
| 919 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 920 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 921 | ** will be skipped in addition to files that may contain binary content. |
| 922 | */ |
| 923 | static void diff_undo_to_checkout( |
| 924 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 925 | FileDirList *pFileDir /* List of files and directories to diff */ |
| 926 | ){ |
| 927 | Stmt q; |
| 928 | Blob content; |
| @@ -1068,10 +1069,67 @@ | |
| 1069 | } |
| 1070 | } |
| 1071 | manifest_destroy(pFrom); |
| 1072 | manifest_destroy(pTo); |
| 1073 | } |
| 1074 | |
| 1075 | /* |
| 1076 | ** Compute the difference from an external tree of files to the current |
| 1077 | ** working checkout with its edits. |
| 1078 | ** |
| 1079 | ** To put it another way: Every managed file in the current working |
| 1080 | ** checkout is compared to the file with same name under zExternBase. The |
| 1081 | ** zExternBase files are on the left and the files in the current working |
| 1082 | ** directory are on the right. |
| 1083 | */ |
| 1084 | void diff_externbase_to_checkout( |
| 1085 | const char *zExternBase, /* Remote tree to use as the baseline */ |
| 1086 | DiffConfig *pCfg, /* Diff settings */ |
| 1087 | FileDirList *pFileDir /* Only look at these files */ |
| 1088 | ){ |
| 1089 | int vid; |
| 1090 | Stmt q; |
| 1091 | |
| 1092 | vid = db_lget_int("checkout",0); |
| 1093 | if( file_isdir(zExternBase, ExtFILE)!=1 ){ |
| 1094 | fossil_fatal("\"%s\" is not a directory", zExternBase); |
| 1095 | } |
| 1096 | db_prepare(&q, |
| 1097 | "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", |
| 1098 | vid |
| 1099 | ); |
| 1100 | while( db_step(&q)==SQLITE_ROW ){ |
| 1101 | const char *zFile; /* Name of file in the repository */ |
| 1102 | char *zLhs; /* Full name of left-hand side file */ |
| 1103 | char *zRhs; /* Full name of right-hand side file */ |
| 1104 | Blob rhs; /* Full text of RHS */ |
| 1105 | Blob lhs; /* Full text of LHS */ |
| 1106 | |
| 1107 | zFile = db_column_text(&q,0); |
| 1108 | if( !file_dir_match(pFileDir, zFile) ) continue; |
| 1109 | zLhs = mprintf("%s/%s", zExternBase, zFile); |
| 1110 | zRhs = mprintf("%s%s", g.zLocalRoot, zFile); |
| 1111 | if( file_size(zLhs, ExtFILE)<0 ){ |
| 1112 | blob_zero(&lhs); |
| 1113 | }else{ |
| 1114 | blob_read_from_file(&lhs, zLhs, ExtFILE); |
| 1115 | } |
| 1116 | blob_read_from_file(&rhs, zRhs, ExtFILE); |
| 1117 | if( blob_size(&lhs)!=blob_size(&rhs) |
| 1118 | || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 |
| 1119 | ){ |
| 1120 | diff_print_index(zFile, pCfg, 0); |
| 1121 | diff_file_mem(&lhs, &rhs, zFile, pCfg); |
| 1122 | } |
| 1123 | blob_reset(&lhs); |
| 1124 | blob_reset(&rhs); |
| 1125 | fossil_free(zLhs); |
| 1126 | fossil_free(zRhs); |
| 1127 | } |
| 1128 | db_finalize(&q); |
| 1129 | } |
| 1130 | |
| 1131 | |
| 1132 | /* |
| 1133 | ** Return the name of the external diff command, or return NULL if |
| 1134 | ** no external diff command is defined. |
| 1135 | */ |
| @@ -1204,10 +1262,14 @@ | |
| 1262 | ** option specifies the check-in from which the second version of the file |
| 1263 | ** or files is taken. If there is no "--to" option then the (possibly edited) |
| 1264 | ** files in the current check-out are used. The "--checkin VERSION" option |
| 1265 | ** shows the changes made by check-in VERSION relative to its primary parent. |
| 1266 | ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. |
| 1267 | ** |
| 1268 | ** With the "--from VERSION" option, if VERSION is actually a directory name |
| 1269 | ** (not a tag or check-in hash) then the files under that directory are used |
| 1270 | ** as the baseline for the diff. |
| 1271 | ** |
| 1272 | ** The "-i" command-line option forces the use of Fossil's own internal |
| 1273 | ** diff logic rather than any external diff program that might be configured |
| 1274 | ** using the "setting" command. If no external diff program is configured, |
| 1275 | ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into |
| @@ -1236,11 +1298,13 @@ | |
| 1298 | ** with negative N meaning show all content |
| 1299 | ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML |
| 1300 | ** --diff-binary BOOL Include binary files with external commands |
| 1301 | ** --exec-abs-paths Force absolute path names on external commands |
| 1302 | ** --exec-rel-paths Force relative path names on external commands |
| 1303 | ** -r|--from VERSION Use VERSION as the baseline for the diff, or |
| 1304 | ** if VERSION is a directory name, use files in |
| 1305 | ** that directory as the baseline. |
| 1306 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 1307 | ** -i|--internal Use internal diff logic |
| 1308 | ** --invert Invert the diff |
| 1309 | ** --json Output formatted as JSON |
| 1310 | ** -n|--linenum Show line numbers |
| @@ -1250,11 +1314,11 @@ | |
| 1314 | ** --strip-trailing-cr Strip trailing CR |
| 1315 | ** --tcl Tcl-formatted output used internally by --tk |
| 1316 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1317 | ** --tk Launch a Tcl/Tk GUI for display |
| 1318 | ** --to VERSION Select VERSION as target for the diff |
| 1319 | ** --undo Use the undo buffer as the baseline |
| 1320 | ** --unified Unified diff |
| 1321 | ** -v|--verbose Output complete text of added or deleted files |
| 1322 | ** -h|--versions Show compared versions in the diff header |
| 1323 | ** --webpage Format output as a stand-alone HTML webpage |
| 1324 | ** -W|--width N Width of lines in side-by-side diff |
| @@ -1267,10 +1331,11 @@ | |
| 1331 | const char *zCheckin; /* Check-in version number */ |
| 1332 | const char *zBranch; /* Branch to diff */ |
| 1333 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1334 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1335 | DiffConfig DCfg; /* Diff configuration object */ |
| 1336 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1337 | |
| 1338 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1339 | diff_tk("diff", 2); |
| 1340 | return; |
| 1341 | } |
| @@ -1278,11 +1343,11 @@ | |
| 1343 | zFrom = find_option("from", "r", 1); |
| 1344 | zTo = find_option("to", 0, 1); |
| 1345 | zCheckin = find_option("checkin", "ci", 1); |
| 1346 | zBranch = find_option("branch", 0, 1); |
| 1347 | againstUndo = find_option("undo",0,0)!=0; |
| 1348 | if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ |
| 1349 | fossil_fatal("cannot use --undo together with --from, --to, --checkin," |
| 1350 | " or --branch"); |
| 1351 | } |
| 1352 | if( zBranch ){ |
| 1353 | if( zTo || zFrom || zCheckin ){ |
| @@ -1289,15 +1354,13 @@ | |
| 1354 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1355 | } |
| 1356 | zTo = zBranch; |
| 1357 | zFrom = mprintf("root:%s", zBranch); |
| 1358 | } |
| 1359 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1360 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1361 | } |
| 1362 | if( 0==zCheckin ){ |
| 1363 | if( zTo==0 || againstUndo ){ |
| 1364 | db_must_be_within_tree(); |
| 1365 | }else if( zFrom==0 ){ |
| 1366 | fossil_fatal("must use --from if --to is present"); |
| @@ -1305,10 +1368,21 @@ | |
| 1368 | db_find_and_open_repository(0, 0); |
| 1369 | } |
| 1370 | }else{ |
| 1371 | db_find_and_open_repository(0, 0); |
| 1372 | } |
| 1373 | determine_exec_relative_option(1); |
| 1374 | if( zFrom!=file_tail(zFrom) |
| 1375 | && file_isdir(zFrom, ExtFILE)==1 |
| 1376 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| 1377 | ){ |
| 1378 | bFromIsDir = 1; |
| 1379 | if( zTo ){ |
| 1380 | fossil_fatal("cannot use --to together with \"--from PATH\""); |
| 1381 | } |
| 1382 | } |
| 1383 | diff_options(&DCfg, isGDiff, 0); |
| 1384 | verify_all_options(); |
| 1385 | g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; |
| 1386 | if( g.argc>=3 ){ |
| 1387 | int i; |
| 1388 | Blob fname; |
| @@ -1337,18 +1411,20 @@ | |
| 1411 | if( zFrom==0 ){ |
| 1412 | fossil_fatal("check-in %s has no parent", zTo); |
| 1413 | } |
| 1414 | } |
| 1415 | diff_begin(&DCfg); |
| 1416 | if( bFromIsDir ){ |
| 1417 | diff_externbase_to_checkout(zFrom, &DCfg, pFileDir); |
| 1418 | }else if( againstUndo ){ |
| 1419 | if( db_lget_int("undo_available",0)==0 ){ |
| 1420 | fossil_print("No undo or redo is available\n"); |
| 1421 | return; |
| 1422 | } |
| 1423 | diff_undo_to_checkout(&DCfg, pFileDir); |
| 1424 | }else if( zTo==0 ){ |
| 1425 | diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0); |
| 1426 | }else{ |
| 1427 | diff_two_versions(zFrom, zTo, &DCfg, pFileDir); |
| 1428 | } |
| 1429 | if( pFileDir ){ |
| 1430 | int i; |
| 1431 |
+92
-16
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -764,26 +764,27 @@ | ||
| 764 | 764 | blob_reset(&file); |
| 765 | 765 | return rc; |
| 766 | 766 | } |
| 767 | 767 | |
| 768 | 768 | /* |
| 769 | -** Run a diff between the version zFrom and files on disk. zFrom might | |
| 770 | -** be NULL which means to simply show the difference between the edited | |
| 771 | -** files on disk and the check-out on which they are based. | |
| 769 | +** Run a diff between the version zFrom and files on disk in the current | |
| 770 | +** working checkout. zFrom might be NULL which means to simply show the | |
| 771 | +** difference between the edited files on disk and the check-out on which | |
| 772 | +** they are based. | |
| 772 | 773 | ** |
| 773 | 774 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 774 | 775 | ** command zDiffCmd to do the diffing. |
| 775 | 776 | ** |
| 776 | 777 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 777 | 778 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 778 | 779 | ** will be skipped in addition to files that may contain binary content. |
| 779 | 780 | */ |
| 780 | -void diff_against_disk( | |
| 781 | +void diff_version_to_checkout( | |
| 781 | 782 | const char *zFrom, /* Version to difference from */ |
| 782 | 783 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 783 | 784 | FileDirList *pFileDir, /* Which files to diff */ |
| 784 | - Blob *pOut /* Blob to output diff instead of stdout */ | |
| 785 | + Blob *pOut /* Blob to output diff instead of stdout */ | |
| 785 | 786 | ){ |
| 786 | 787 | int vid; |
| 787 | 788 | Blob sql; |
| 788 | 789 | Stmt q; |
| 789 | 790 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -908,20 +909,20 @@ | ||
| 908 | 909 | db_finalize(&q); |
| 909 | 910 | db_end_transaction(1); /* ROLLBACK */ |
| 910 | 911 | } |
| 911 | 912 | |
| 912 | 913 | /* |
| 913 | -** Run a diff between the undo buffer and files on disk. | |
| 914 | +** Run a diff from the undo buffer to files on disk. | |
| 914 | 915 | ** |
| 915 | 916 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 916 | 917 | ** command zDiffCmd to do the diffing. |
| 917 | 918 | ** |
| 918 | 919 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 919 | 920 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 920 | 921 | ** will be skipped in addition to files that may contain binary content. |
| 921 | 922 | */ |
| 922 | -static void diff_against_undo( | |
| 923 | +static void diff_undo_to_checkout( | |
| 923 | 924 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 924 | 925 | FileDirList *pFileDir /* List of files and directories to diff */ |
| 925 | 926 | ){ |
| 926 | 927 | Stmt q; |
| 927 | 928 | Blob content; |
| @@ -1068,10 +1069,67 @@ | ||
| 1068 | 1069 | } |
| 1069 | 1070 | } |
| 1070 | 1071 | manifest_destroy(pFrom); |
| 1071 | 1072 | manifest_destroy(pTo); |
| 1072 | 1073 | } |
| 1074 | + | |
| 1075 | +/* | |
| 1076 | +** Compute the difference from an external tree of files to the current | |
| 1077 | +** working checkout with its edits. | |
| 1078 | +** | |
| 1079 | +** To put it another way: Every managed file in the current working | |
| 1080 | +** checkout is compared to the file with same name under zExternBase. The | |
| 1081 | +** zExternBase files are on the left and the files in the current working | |
| 1082 | +** directory are on the right. | |
| 1083 | +*/ | |
| 1084 | +void diff_externbase_to_checkout( | |
| 1085 | + const char *zExternBase, /* Remote tree to use as the baseline */ | |
| 1086 | + DiffConfig *pCfg, /* Diff settings */ | |
| 1087 | + FileDirList *pFileDir /* Only look at these files */ | |
| 1088 | +){ | |
| 1089 | + int vid; | |
| 1090 | + Stmt q; | |
| 1091 | + | |
| 1092 | + vid = db_lget_int("checkout",0); | |
| 1093 | + if( file_isdir(zExternBase, ExtFILE)!=1 ){ | |
| 1094 | + fossil_fatal("\"%s\" is not a directory", zExternBase); | |
| 1095 | + } | |
| 1096 | + db_prepare(&q, | |
| 1097 | + "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", | |
| 1098 | + vid | |
| 1099 | + ); | |
| 1100 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1101 | + const char *zFile; /* Name of file in the repository */ | |
| 1102 | + char *zLhs; /* Full name of left-hand side file */ | |
| 1103 | + char *zRhs; /* Full name of right-hand side file */ | |
| 1104 | + Blob rhs; /* Full text of RHS */ | |
| 1105 | + Blob lhs; /* Full text of LHS */ | |
| 1106 | + | |
| 1107 | + zFile = db_column_text(&q,0); | |
| 1108 | + if( !file_dir_match(pFileDir, zFile) ) continue; | |
| 1109 | + zLhs = mprintf("%s/%s", zExternBase, zFile); | |
| 1110 | + zRhs = mprintf("%s%s", g.zLocalRoot, zFile); | |
| 1111 | + if( file_size(zLhs, ExtFILE)<0 ){ | |
| 1112 | + blob_zero(&lhs); | |
| 1113 | + }else{ | |
| 1114 | + blob_read_from_file(&lhs, zLhs, ExtFILE); | |
| 1115 | + } | |
| 1116 | + blob_read_from_file(&rhs, zRhs, ExtFILE); | |
| 1117 | + if( blob_size(&lhs)!=blob_size(&rhs) | |
| 1118 | + || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 | |
| 1119 | + ){ | |
| 1120 | + diff_print_index(zFile, pCfg, 0); | |
| 1121 | + diff_file_mem(&lhs, &rhs, zFile, pCfg); | |
| 1122 | + } | |
| 1123 | + blob_reset(&lhs); | |
| 1124 | + blob_reset(&rhs); | |
| 1125 | + fossil_free(zLhs); | |
| 1126 | + fossil_free(zRhs); | |
| 1127 | + } | |
| 1128 | + db_finalize(&q); | |
| 1129 | +} | |
| 1130 | + | |
| 1073 | 1131 | |
| 1074 | 1132 | /* |
| 1075 | 1133 | ** Return the name of the external diff command, or return NULL if |
| 1076 | 1134 | ** no external diff command is defined. |
| 1077 | 1135 | */ |
| @@ -1204,10 +1262,14 @@ | ||
| 1204 | 1262 | ** option specifies the check-in from which the second version of the file |
| 1205 | 1263 | ** or files is taken. If there is no "--to" option then the (possibly edited) |
| 1206 | 1264 | ** files in the current check-out are used. The "--checkin VERSION" option |
| 1207 | 1265 | ** shows the changes made by check-in VERSION relative to its primary parent. |
| 1208 | 1266 | ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. |
| 1267 | +** | |
| 1268 | +** With the "--from VERSION" option, if VERSION is actually a directory name | |
| 1269 | +** (not a tag or check-in hash) then the files under that directory are used | |
| 1270 | +** as the baseline for the diff. | |
| 1209 | 1271 | ** |
| 1210 | 1272 | ** The "-i" command-line option forces the use of Fossil's own internal |
| 1211 | 1273 | ** diff logic rather than any external diff program that might be configured |
| 1212 | 1274 | ** using the "setting" command. If no external diff program is configured, |
| 1213 | 1275 | ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into |
| @@ -1236,11 +1298,13 @@ | ||
| 1236 | 1298 | ** with negative N meaning show all content |
| 1237 | 1299 | ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML |
| 1238 | 1300 | ** --diff-binary BOOL Include binary files with external commands |
| 1239 | 1301 | ** --exec-abs-paths Force absolute path names on external commands |
| 1240 | 1302 | ** --exec-rel-paths Force relative path names on external commands |
| 1241 | -** -r|--from VERSION Select VERSION as source for the diff | |
| 1303 | +** -r|--from VERSION Use VERSION as the baseline for the diff, or | |
| 1304 | +** if VERSION is a directory name, use files in | |
| 1305 | +** that directory as the baseline. | |
| 1242 | 1306 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 1243 | 1307 | ** -i|--internal Use internal diff logic |
| 1244 | 1308 | ** --invert Invert the diff |
| 1245 | 1309 | ** --json Output formatted as JSON |
| 1246 | 1310 | ** -n|--linenum Show line numbers |
| @@ -1250,11 +1314,11 @@ | ||
| 1250 | 1314 | ** --strip-trailing-cr Strip trailing CR |
| 1251 | 1315 | ** --tcl Tcl-formatted output used internally by --tk |
| 1252 | 1316 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1253 | 1317 | ** --tk Launch a Tcl/Tk GUI for display |
| 1254 | 1318 | ** --to VERSION Select VERSION as target for the diff |
| 1255 | -** --undo Diff against the "undo" buffer | |
| 1319 | +** --undo Use the undo buffer as the baseline | |
| 1256 | 1320 | ** --unified Unified diff |
| 1257 | 1321 | ** -v|--verbose Output complete text of added or deleted files |
| 1258 | 1322 | ** -h|--versions Show compared versions in the diff header |
| 1259 | 1323 | ** --webpage Format output as a stand-alone HTML webpage |
| 1260 | 1324 | ** -W|--width N Width of lines in side-by-side diff |
| @@ -1267,10 +1331,11 @@ | ||
| 1267 | 1331 | const char *zCheckin; /* Check-in version number */ |
| 1268 | 1332 | const char *zBranch; /* Branch to diff */ |
| 1269 | 1333 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1270 | 1334 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1271 | 1335 | DiffConfig DCfg; /* Diff configuration object */ |
| 1336 | + int bFromIsDir = 0; /* True if zFrom is a directory name */ | |
| 1272 | 1337 | |
| 1273 | 1338 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1274 | 1339 | diff_tk("diff", 2); |
| 1275 | 1340 | return; |
| 1276 | 1341 | } |
| @@ -1278,11 +1343,11 @@ | ||
| 1278 | 1343 | zFrom = find_option("from", "r", 1); |
| 1279 | 1344 | zTo = find_option("to", 0, 1); |
| 1280 | 1345 | zCheckin = find_option("checkin", "ci", 1); |
| 1281 | 1346 | zBranch = find_option("branch", 0, 1); |
| 1282 | 1347 | againstUndo = find_option("undo",0,0)!=0; |
| 1283 | - if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ | |
| 1348 | + if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ | |
| 1284 | 1349 | fossil_fatal("cannot use --undo together with --from, --to, --checkin," |
| 1285 | 1350 | " or --branch"); |
| 1286 | 1351 | } |
| 1287 | 1352 | if( zBranch ){ |
| 1288 | 1353 | if( zTo || zFrom || zCheckin ){ |
| @@ -1289,15 +1354,13 @@ | ||
| 1289 | 1354 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1290 | 1355 | } |
| 1291 | 1356 | zTo = zBranch; |
| 1292 | 1357 | zFrom = mprintf("root:%s", zBranch); |
| 1293 | 1358 | } |
| 1294 | - if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ | |
| 1359 | + if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ | |
| 1295 | 1360 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1296 | 1361 | } |
| 1297 | - diff_options(&DCfg, isGDiff, 0); | |
| 1298 | - determine_exec_relative_option(1); | |
| 1299 | 1362 | if( 0==zCheckin ){ |
| 1300 | 1363 | if( zTo==0 || againstUndo ){ |
| 1301 | 1364 | db_must_be_within_tree(); |
| 1302 | 1365 | }else if( zFrom==0 ){ |
| 1303 | 1366 | fossil_fatal("must use --from if --to is present"); |
| @@ -1305,10 +1368,21 @@ | ||
| 1305 | 1368 | db_find_and_open_repository(0, 0); |
| 1306 | 1369 | } |
| 1307 | 1370 | }else{ |
| 1308 | 1371 | db_find_and_open_repository(0, 0); |
| 1309 | 1372 | } |
| 1373 | + determine_exec_relative_option(1); | |
| 1374 | + if( zFrom!=file_tail(zFrom) | |
| 1375 | + && file_isdir(zFrom, ExtFILE)==1 | |
| 1376 | + && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) | |
| 1377 | + ){ | |
| 1378 | + bFromIsDir = 1; | |
| 1379 | + if( zTo ){ | |
| 1380 | + fossil_fatal("cannot use --to together with \"--from PATH\""); | |
| 1381 | + } | |
| 1382 | + } | |
| 1383 | + diff_options(&DCfg, isGDiff, 0); | |
| 1310 | 1384 | verify_all_options(); |
| 1311 | 1385 | g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; |
| 1312 | 1386 | if( g.argc>=3 ){ |
| 1313 | 1387 | int i; |
| 1314 | 1388 | Blob fname; |
| @@ -1337,18 +1411,20 @@ | ||
| 1337 | 1411 | if( zFrom==0 ){ |
| 1338 | 1412 | fossil_fatal("check-in %s has no parent", zTo); |
| 1339 | 1413 | } |
| 1340 | 1414 | } |
| 1341 | 1415 | diff_begin(&DCfg); |
| 1342 | - if( againstUndo ){ | |
| 1416 | + if( bFromIsDir ){ | |
| 1417 | + diff_externbase_to_checkout(zFrom, &DCfg, pFileDir); | |
| 1418 | + }else if( againstUndo ){ | |
| 1343 | 1419 | if( db_lget_int("undo_available",0)==0 ){ |
| 1344 | 1420 | fossil_print("No undo or redo is available\n"); |
| 1345 | 1421 | return; |
| 1346 | 1422 | } |
| 1347 | - diff_against_undo(&DCfg, pFileDir); | |
| 1423 | + diff_undo_to_checkout(&DCfg, pFileDir); | |
| 1348 | 1424 | }else if( zTo==0 ){ |
| 1349 | - diff_against_disk(zFrom, &DCfg, pFileDir, 0); | |
| 1425 | + diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0); | |
| 1350 | 1426 | }else{ |
| 1351 | 1427 | diff_two_versions(zFrom, zTo, &DCfg, pFileDir); |
| 1352 | 1428 | } |
| 1353 | 1429 | if( pFileDir ){ |
| 1354 | 1430 | int i; |
| 1355 | 1431 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -764,26 +764,27 @@ | |
| 764 | blob_reset(&file); |
| 765 | return rc; |
| 766 | } |
| 767 | |
| 768 | /* |
| 769 | ** Run a diff between the version zFrom and files on disk. zFrom might |
| 770 | ** be NULL which means to simply show the difference between the edited |
| 771 | ** files on disk and the check-out on which they are based. |
| 772 | ** |
| 773 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 774 | ** command zDiffCmd to do the diffing. |
| 775 | ** |
| 776 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 777 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 778 | ** will be skipped in addition to files that may contain binary content. |
| 779 | */ |
| 780 | void diff_against_disk( |
| 781 | const char *zFrom, /* Version to difference from */ |
| 782 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 783 | FileDirList *pFileDir, /* Which files to diff */ |
| 784 | Blob *pOut /* Blob to output diff instead of stdout */ |
| 785 | ){ |
| 786 | int vid; |
| 787 | Blob sql; |
| 788 | Stmt q; |
| 789 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -908,20 +909,20 @@ | |
| 908 | db_finalize(&q); |
| 909 | db_end_transaction(1); /* ROLLBACK */ |
| 910 | } |
| 911 | |
| 912 | /* |
| 913 | ** Run a diff between the undo buffer and files on disk. |
| 914 | ** |
| 915 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 916 | ** command zDiffCmd to do the diffing. |
| 917 | ** |
| 918 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 919 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 920 | ** will be skipped in addition to files that may contain binary content. |
| 921 | */ |
| 922 | static void diff_against_undo( |
| 923 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 924 | FileDirList *pFileDir /* List of files and directories to diff */ |
| 925 | ){ |
| 926 | Stmt q; |
| 927 | Blob content; |
| @@ -1068,10 +1069,67 @@ | |
| 1068 | } |
| 1069 | } |
| 1070 | manifest_destroy(pFrom); |
| 1071 | manifest_destroy(pTo); |
| 1072 | } |
| 1073 | |
| 1074 | /* |
| 1075 | ** Return the name of the external diff command, or return NULL if |
| 1076 | ** no external diff command is defined. |
| 1077 | */ |
| @@ -1204,10 +1262,14 @@ | |
| 1204 | ** option specifies the check-in from which the second version of the file |
| 1205 | ** or files is taken. If there is no "--to" option then the (possibly edited) |
| 1206 | ** files in the current check-out are used. The "--checkin VERSION" option |
| 1207 | ** shows the changes made by check-in VERSION relative to its primary parent. |
| 1208 | ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. |
| 1209 | ** |
| 1210 | ** The "-i" command-line option forces the use of Fossil's own internal |
| 1211 | ** diff logic rather than any external diff program that might be configured |
| 1212 | ** using the "setting" command. If no external diff program is configured, |
| 1213 | ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into |
| @@ -1236,11 +1298,13 @@ | |
| 1236 | ** with negative N meaning show all content |
| 1237 | ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML |
| 1238 | ** --diff-binary BOOL Include binary files with external commands |
| 1239 | ** --exec-abs-paths Force absolute path names on external commands |
| 1240 | ** --exec-rel-paths Force relative path names on external commands |
| 1241 | ** -r|--from VERSION Select VERSION as source for the diff |
| 1242 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 1243 | ** -i|--internal Use internal diff logic |
| 1244 | ** --invert Invert the diff |
| 1245 | ** --json Output formatted as JSON |
| 1246 | ** -n|--linenum Show line numbers |
| @@ -1250,11 +1314,11 @@ | |
| 1250 | ** --strip-trailing-cr Strip trailing CR |
| 1251 | ** --tcl Tcl-formatted output used internally by --tk |
| 1252 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1253 | ** --tk Launch a Tcl/Tk GUI for display |
| 1254 | ** --to VERSION Select VERSION as target for the diff |
| 1255 | ** --undo Diff against the "undo" buffer |
| 1256 | ** --unified Unified diff |
| 1257 | ** -v|--verbose Output complete text of added or deleted files |
| 1258 | ** -h|--versions Show compared versions in the diff header |
| 1259 | ** --webpage Format output as a stand-alone HTML webpage |
| 1260 | ** -W|--width N Width of lines in side-by-side diff |
| @@ -1267,10 +1331,11 @@ | |
| 1267 | const char *zCheckin; /* Check-in version number */ |
| 1268 | const char *zBranch; /* Branch to diff */ |
| 1269 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1270 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1271 | DiffConfig DCfg; /* Diff configuration object */ |
| 1272 | |
| 1273 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1274 | diff_tk("diff", 2); |
| 1275 | return; |
| 1276 | } |
| @@ -1278,11 +1343,11 @@ | |
| 1278 | zFrom = find_option("from", "r", 1); |
| 1279 | zTo = find_option("to", 0, 1); |
| 1280 | zCheckin = find_option("checkin", "ci", 1); |
| 1281 | zBranch = find_option("branch", 0, 1); |
| 1282 | againstUndo = find_option("undo",0,0)!=0; |
| 1283 | if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ |
| 1284 | fossil_fatal("cannot use --undo together with --from, --to, --checkin," |
| 1285 | " or --branch"); |
| 1286 | } |
| 1287 | if( zBranch ){ |
| 1288 | if( zTo || zFrom || zCheckin ){ |
| @@ -1289,15 +1354,13 @@ | |
| 1289 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1290 | } |
| 1291 | zTo = zBranch; |
| 1292 | zFrom = mprintf("root:%s", zBranch); |
| 1293 | } |
| 1294 | if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ |
| 1295 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1296 | } |
| 1297 | diff_options(&DCfg, isGDiff, 0); |
| 1298 | determine_exec_relative_option(1); |
| 1299 | if( 0==zCheckin ){ |
| 1300 | if( zTo==0 || againstUndo ){ |
| 1301 | db_must_be_within_tree(); |
| 1302 | }else if( zFrom==0 ){ |
| 1303 | fossil_fatal("must use --from if --to is present"); |
| @@ -1305,10 +1368,21 @@ | |
| 1305 | db_find_and_open_repository(0, 0); |
| 1306 | } |
| 1307 | }else{ |
| 1308 | db_find_and_open_repository(0, 0); |
| 1309 | } |
| 1310 | verify_all_options(); |
| 1311 | g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; |
| 1312 | if( g.argc>=3 ){ |
| 1313 | int i; |
| 1314 | Blob fname; |
| @@ -1337,18 +1411,20 @@ | |
| 1337 | if( zFrom==0 ){ |
| 1338 | fossil_fatal("check-in %s has no parent", zTo); |
| 1339 | } |
| 1340 | } |
| 1341 | diff_begin(&DCfg); |
| 1342 | if( againstUndo ){ |
| 1343 | if( db_lget_int("undo_available",0)==0 ){ |
| 1344 | fossil_print("No undo or redo is available\n"); |
| 1345 | return; |
| 1346 | } |
| 1347 | diff_against_undo(&DCfg, pFileDir); |
| 1348 | }else if( zTo==0 ){ |
| 1349 | diff_against_disk(zFrom, &DCfg, pFileDir, 0); |
| 1350 | }else{ |
| 1351 | diff_two_versions(zFrom, zTo, &DCfg, pFileDir); |
| 1352 | } |
| 1353 | if( pFileDir ){ |
| 1354 | int i; |
| 1355 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -764,26 +764,27 @@ | |
| 764 | blob_reset(&file); |
| 765 | return rc; |
| 766 | } |
| 767 | |
| 768 | /* |
| 769 | ** Run a diff between the version zFrom and files on disk in the current |
| 770 | ** working checkout. zFrom might be NULL which means to simply show the |
| 771 | ** difference between the edited files on disk and the check-out on which |
| 772 | ** they are based. |
| 773 | ** |
| 774 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 775 | ** command zDiffCmd to do the diffing. |
| 776 | ** |
| 777 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 778 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 779 | ** will be skipped in addition to files that may contain binary content. |
| 780 | */ |
| 781 | void diff_version_to_checkout( |
| 782 | const char *zFrom, /* Version to difference from */ |
| 783 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 784 | FileDirList *pFileDir, /* Which files to diff */ |
| 785 | Blob *pOut /* Blob to output diff instead of stdout */ |
| 786 | ){ |
| 787 | int vid; |
| 788 | Blob sql; |
| 789 | Stmt q; |
| 790 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -908,20 +909,20 @@ | |
| 909 | db_finalize(&q); |
| 910 | db_end_transaction(1); /* ROLLBACK */ |
| 911 | } |
| 912 | |
| 913 | /* |
| 914 | ** Run a diff from the undo buffer to files on disk. |
| 915 | ** |
| 916 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 917 | ** command zDiffCmd to do the diffing. |
| 918 | ** |
| 919 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 920 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 921 | ** will be skipped in addition to files that may contain binary content. |
| 922 | */ |
| 923 | static void diff_undo_to_checkout( |
| 924 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 925 | FileDirList *pFileDir /* List of files and directories to diff */ |
| 926 | ){ |
| 927 | Stmt q; |
| 928 | Blob content; |
| @@ -1068,10 +1069,67 @@ | |
| 1069 | } |
| 1070 | } |
| 1071 | manifest_destroy(pFrom); |
| 1072 | manifest_destroy(pTo); |
| 1073 | } |
| 1074 | |
| 1075 | /* |
| 1076 | ** Compute the difference from an external tree of files to the current |
| 1077 | ** working checkout with its edits. |
| 1078 | ** |
| 1079 | ** To put it another way: Every managed file in the current working |
| 1080 | ** checkout is compared to the file with same name under zExternBase. The |
| 1081 | ** zExternBase files are on the left and the files in the current working |
| 1082 | ** directory are on the right. |
| 1083 | */ |
| 1084 | void diff_externbase_to_checkout( |
| 1085 | const char *zExternBase, /* Remote tree to use as the baseline */ |
| 1086 | DiffConfig *pCfg, /* Diff settings */ |
| 1087 | FileDirList *pFileDir /* Only look at these files */ |
| 1088 | ){ |
| 1089 | int vid; |
| 1090 | Stmt q; |
| 1091 | |
| 1092 | vid = db_lget_int("checkout",0); |
| 1093 | if( file_isdir(zExternBase, ExtFILE)!=1 ){ |
| 1094 | fossil_fatal("\"%s\" is not a directory", zExternBase); |
| 1095 | } |
| 1096 | db_prepare(&q, |
| 1097 | "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", |
| 1098 | vid |
| 1099 | ); |
| 1100 | while( db_step(&q)==SQLITE_ROW ){ |
| 1101 | const char *zFile; /* Name of file in the repository */ |
| 1102 | char *zLhs; /* Full name of left-hand side file */ |
| 1103 | char *zRhs; /* Full name of right-hand side file */ |
| 1104 | Blob rhs; /* Full text of RHS */ |
| 1105 | Blob lhs; /* Full text of LHS */ |
| 1106 | |
| 1107 | zFile = db_column_text(&q,0); |
| 1108 | if( !file_dir_match(pFileDir, zFile) ) continue; |
| 1109 | zLhs = mprintf("%s/%s", zExternBase, zFile); |
| 1110 | zRhs = mprintf("%s%s", g.zLocalRoot, zFile); |
| 1111 | if( file_size(zLhs, ExtFILE)<0 ){ |
| 1112 | blob_zero(&lhs); |
| 1113 | }else{ |
| 1114 | blob_read_from_file(&lhs, zLhs, ExtFILE); |
| 1115 | } |
| 1116 | blob_read_from_file(&rhs, zRhs, ExtFILE); |
| 1117 | if( blob_size(&lhs)!=blob_size(&rhs) |
| 1118 | || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 |
| 1119 | ){ |
| 1120 | diff_print_index(zFile, pCfg, 0); |
| 1121 | diff_file_mem(&lhs, &rhs, zFile, pCfg); |
| 1122 | } |
| 1123 | blob_reset(&lhs); |
| 1124 | blob_reset(&rhs); |
| 1125 | fossil_free(zLhs); |
| 1126 | fossil_free(zRhs); |
| 1127 | } |
| 1128 | db_finalize(&q); |
| 1129 | } |
| 1130 | |
| 1131 | |
| 1132 | /* |
| 1133 | ** Return the name of the external diff command, or return NULL if |
| 1134 | ** no external diff command is defined. |
| 1135 | */ |
| @@ -1204,10 +1262,14 @@ | |
| 1262 | ** option specifies the check-in from which the second version of the file |
| 1263 | ** or files is taken. If there is no "--to" option then the (possibly edited) |
| 1264 | ** files in the current check-out are used. The "--checkin VERSION" option |
| 1265 | ** shows the changes made by check-in VERSION relative to its primary parent. |
| 1266 | ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. |
| 1267 | ** |
| 1268 | ** With the "--from VERSION" option, if VERSION is actually a directory name |
| 1269 | ** (not a tag or check-in hash) then the files under that directory are used |
| 1270 | ** as the baseline for the diff. |
| 1271 | ** |
| 1272 | ** The "-i" command-line option forces the use of Fossil's own internal |
| 1273 | ** diff logic rather than any external diff program that might be configured |
| 1274 | ** using the "setting" command. If no external diff program is configured, |
| 1275 | ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into |
| @@ -1236,11 +1298,13 @@ | |
| 1298 | ** with negative N meaning show all content |
| 1299 | ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML |
| 1300 | ** --diff-binary BOOL Include binary files with external commands |
| 1301 | ** --exec-abs-paths Force absolute path names on external commands |
| 1302 | ** --exec-rel-paths Force relative path names on external commands |
| 1303 | ** -r|--from VERSION Use VERSION as the baseline for the diff, or |
| 1304 | ** if VERSION is a directory name, use files in |
| 1305 | ** that directory as the baseline. |
| 1306 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 1307 | ** -i|--internal Use internal diff logic |
| 1308 | ** --invert Invert the diff |
| 1309 | ** --json Output formatted as JSON |
| 1310 | ** -n|--linenum Show line numbers |
| @@ -1250,11 +1314,11 @@ | |
| 1314 | ** --strip-trailing-cr Strip trailing CR |
| 1315 | ** --tcl Tcl-formatted output used internally by --tk |
| 1316 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1317 | ** --tk Launch a Tcl/Tk GUI for display |
| 1318 | ** --to VERSION Select VERSION as target for the diff |
| 1319 | ** --undo Use the undo buffer as the baseline |
| 1320 | ** --unified Unified diff |
| 1321 | ** -v|--verbose Output complete text of added or deleted files |
| 1322 | ** -h|--versions Show compared versions in the diff header |
| 1323 | ** --webpage Format output as a stand-alone HTML webpage |
| 1324 | ** -W|--width N Width of lines in side-by-side diff |
| @@ -1267,10 +1331,11 @@ | |
| 1331 | const char *zCheckin; /* Check-in version number */ |
| 1332 | const char *zBranch; /* Branch to diff */ |
| 1333 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1334 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1335 | DiffConfig DCfg; /* Diff configuration object */ |
| 1336 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1337 | |
| 1338 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1339 | diff_tk("diff", 2); |
| 1340 | return; |
| 1341 | } |
| @@ -1278,11 +1343,11 @@ | |
| 1343 | zFrom = find_option("from", "r", 1); |
| 1344 | zTo = find_option("to", 0, 1); |
| 1345 | zCheckin = find_option("checkin", "ci", 1); |
| 1346 | zBranch = find_option("branch", 0, 1); |
| 1347 | againstUndo = find_option("undo",0,0)!=0; |
| 1348 | if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ |
| 1349 | fossil_fatal("cannot use --undo together with --from, --to, --checkin," |
| 1350 | " or --branch"); |
| 1351 | } |
| 1352 | if( zBranch ){ |
| 1353 | if( zTo || zFrom || zCheckin ){ |
| @@ -1289,15 +1354,13 @@ | |
| 1354 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1355 | } |
| 1356 | zTo = zBranch; |
| 1357 | zFrom = mprintf("root:%s", zBranch); |
| 1358 | } |
| 1359 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1360 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1361 | } |
| 1362 | if( 0==zCheckin ){ |
| 1363 | if( zTo==0 || againstUndo ){ |
| 1364 | db_must_be_within_tree(); |
| 1365 | }else if( zFrom==0 ){ |
| 1366 | fossil_fatal("must use --from if --to is present"); |
| @@ -1305,10 +1368,21 @@ | |
| 1368 | db_find_and_open_repository(0, 0); |
| 1369 | } |
| 1370 | }else{ |
| 1371 | db_find_and_open_repository(0, 0); |
| 1372 | } |
| 1373 | determine_exec_relative_option(1); |
| 1374 | if( zFrom!=file_tail(zFrom) |
| 1375 | && file_isdir(zFrom, ExtFILE)==1 |
| 1376 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| 1377 | ){ |
| 1378 | bFromIsDir = 1; |
| 1379 | if( zTo ){ |
| 1380 | fossil_fatal("cannot use --to together with \"--from PATH\""); |
| 1381 | } |
| 1382 | } |
| 1383 | diff_options(&DCfg, isGDiff, 0); |
| 1384 | verify_all_options(); |
| 1385 | g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; |
| 1386 | if( g.argc>=3 ){ |
| 1387 | int i; |
| 1388 | Blob fname; |
| @@ -1337,18 +1411,20 @@ | |
| 1411 | if( zFrom==0 ){ |
| 1412 | fossil_fatal("check-in %s has no parent", zTo); |
| 1413 | } |
| 1414 | } |
| 1415 | diff_begin(&DCfg); |
| 1416 | if( bFromIsDir ){ |
| 1417 | diff_externbase_to_checkout(zFrom, &DCfg, pFileDir); |
| 1418 | }else if( againstUndo ){ |
| 1419 | if( db_lget_int("undo_available",0)==0 ){ |
| 1420 | fossil_print("No undo or redo is available\n"); |
| 1421 | return; |
| 1422 | } |
| 1423 | diff_undo_to_checkout(&DCfg, pFileDir); |
| 1424 | }else if( zTo==0 ){ |
| 1425 | diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0); |
| 1426 | }else{ |
| 1427 | diff_two_versions(zFrom, zTo, &DCfg, pFileDir); |
| 1428 | } |
| 1429 | if( pFileDir ){ |
| 1430 | int i; |
| 1431 |
+24
-13
| --- src/fossil.diff.js | ||
| +++ src/fossil.diff.js | ||
| @@ -28,48 +28,59 @@ | ||
| 28 | 28 | window.fossil.onPageLoad(function(){ |
| 29 | 29 | /** |
| 30 | 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | 31 | /info and similar pages. |
| 32 | 32 | */ |
| 33 | + if( !window.fossil.page.diffControlContainer ){ | |
| 34 | + return; | |
| 35 | + } | |
| 33 | 36 | const D = window.fossil.dom; |
| 34 | - const allToggles = [/*collection of all diff-toggle checkboxes */]; | |
| 37 | + const allToggles = [/*collection of all diff-toggle checkboxes*/]; | |
| 38 | + let checkedCount = | |
| 39 | + 0 /* When showing more than one diff, keep track of how many | |
| 40 | + "show/hide" checkboxes are are checked so we can update the | |
| 41 | + "show/hide all" label dynamically. */; | |
| 42 | + let btnAll /* UI control to show/hide all diffs */; | |
| 43 | + /* Install a diff-toggle button for the given diff table element. */ | |
| 35 | 44 | const addToggle = function(diffElem){ |
| 36 | 45 | const sib = diffElem.previousElementSibling, |
| 37 | 46 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 38 | 47 | if(!sib) return; |
| 39 | 48 | const lblToggle = D.label(); |
| 40 | 49 | D.append(lblToggle, ckbox, D.text(" show/hide ")); |
| 41 | 50 | const wrapper = D.append(D.span(), lblToggle); |
| 42 | 51 | allToggles.push(ckbox); |
| 52 | + ++checkedCount; | |
| 43 | 53 | D.append(sib, D.append(wrapper, lblToggle)); |
| 44 | 54 | ckbox.addEventListener('change', function(){ |
| 45 | 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 | + } | |
| 46 | 61 | }, false); |
| 47 | 62 | }; |
| 48 | 63 | if( !document.querySelector('body.fdiff') ){ |
| 49 | 64 | /* Don't show the diff toggle button for /fdiff because it only |
| 50 | 65 | has a single file to show (and also a different DOM layout). */ |
| 51 | 66 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 52 | 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 | + */ | |
| 53 | 73 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 54 | 74 | if(icm) { |
| 55 | - const btnAll = D.addClass(D.a("#", "Show/Hide"), "button"); | |
| 75 | + btnAll = D.addClass(D.a("#", "Hide diffs"), "button"); | |
| 56 | 76 | D.append( icm, btnAll ); |
| 57 | 77 | btnAll.addEventListener('click', function(ev){ |
| 58 | 78 | ev.preventDefault(); |
| 59 | 79 | ev.stopPropagation(); |
| 60 | - /* Figure out whether we want to show all or hide all: if any diffs are | |
| 61 | - toggled off, show all, else hide all. */ | |
| 62 | - let show = false; | |
| 63 | - let ckbox; | |
| 64 | - for( ckbox of allToggles ){ | |
| 65 | - if( !ckbox.checked ){ | |
| 66 | - show = true; | |
| 67 | - break; | |
| 68 | - } | |
| 69 | - } | |
| 70 | - for( ckbox of allToggles ){ | |
| 80 | + const show = checkedCount < allToggles.length; | |
| 81 | + for( const ckbox of allToggles ){ | |
| 71 | 82 | /* Toggle all entries to match this new state. We use click() |
| 72 | 83 | instead of ckbox.checked=... so that the on-change event handler |
| 73 | 84 | fires. */ |
| 74 | 85 | if(ckbox.checked!==show) ckbox.click(); |
| 75 | 86 | } |
| 76 | 87 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -28,48 +28,59 @@ | |
| 28 | window.fossil.onPageLoad(function(){ |
| 29 | /** |
| 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | /info and similar pages. |
| 32 | */ |
| 33 | const D = window.fossil.dom; |
| 34 | const allToggles = [/*collection of all diff-toggle checkboxes */]; |
| 35 | const addToggle = function(diffElem){ |
| 36 | const sib = diffElem.previousElementSibling, |
| 37 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 38 | if(!sib) return; |
| 39 | const lblToggle = D.label(); |
| 40 | D.append(lblToggle, ckbox, D.text(" show/hide ")); |
| 41 | const wrapper = D.append(D.span(), lblToggle); |
| 42 | allToggles.push(ckbox); |
| 43 | D.append(sib, D.append(wrapper, lblToggle)); |
| 44 | ckbox.addEventListener('change', function(){ |
| 45 | diffElem.classList[this.checked ? 'remove' : 'add']('hidden'); |
| 46 | }, false); |
| 47 | }; |
| 48 | if( !document.querySelector('body.fdiff') ){ |
| 49 | /* Don't show the diff toggle button for /fdiff because it only |
| 50 | has a single file to show (and also a different DOM layout). */ |
| 51 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 52 | } |
| 53 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 54 | if(icm) { |
| 55 | const btnAll = D.addClass(D.a("#", "Show/Hide"), "button"); |
| 56 | D.append( icm, btnAll ); |
| 57 | btnAll.addEventListener('click', function(ev){ |
| 58 | ev.preventDefault(); |
| 59 | ev.stopPropagation(); |
| 60 | /* Figure out whether we want to show all or hide all: if any diffs are |
| 61 | toggled off, show all, else hide all. */ |
| 62 | let show = false; |
| 63 | let ckbox; |
| 64 | for( ckbox of allToggles ){ |
| 65 | if( !ckbox.checked ){ |
| 66 | show = true; |
| 67 | break; |
| 68 | } |
| 69 | } |
| 70 | for( ckbox of allToggles ){ |
| 71 | /* Toggle all entries to match this new state. We use click() |
| 72 | instead of ckbox.checked=... so that the on-change event handler |
| 73 | fires. */ |
| 74 | if(ckbox.checked!==show) ckbox.click(); |
| 75 | } |
| 76 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -28,48 +28,59 @@ | |
| 28 | window.fossil.onPageLoad(function(){ |
| 29 | /** |
| 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | /info and similar pages. |
| 32 | */ |
| 33 | if( !window.fossil.page.diffControlContainer ){ |
| 34 | return; |
| 35 | } |
| 36 | const D = window.fossil.dom; |
| 37 | const allToggles = [/*collection of all diff-toggle checkboxes*/]; |
| 38 | let checkedCount = |
| 39 | 0 /* When showing more than one diff, keep track of how many |
| 40 | "show/hide" checkboxes are are checked so we can update the |
| 41 | "show/hide all" label dynamically. */; |
| 42 | let btnAll /* UI control to show/hide all diffs */; |
| 43 | /* Install a diff-toggle button for the given diff table element. */ |
| 44 | const addToggle = function(diffElem){ |
| 45 | const sib = diffElem.previousElementSibling, |
| 46 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 47 | if(!sib) return; |
| 48 | const lblToggle = D.label(); |
| 49 | D.append(lblToggle, ckbox, D.text(" show/hide ")); |
| 50 | const wrapper = D.append(D.span(), lblToggle); |
| 51 | allToggles.push(ckbox); |
| 52 | ++checkedCount; |
| 53 | D.append(sib, D.append(wrapper, lblToggle)); |
| 54 | ckbox.addEventListener('change', function(){ |
| 55 | diffElem.classList[this.checked ? 'remove' : 'add']('hidden'); |
| 56 | if(btnAll){ |
| 57 | checkedCount += (this.checked ? 1 : -1); |
| 58 | btnAll.innerText = (checkedCount < allToggles.length) |
| 59 | ? "Show diffs" : "Hide diffs"; |
| 60 | } |
| 61 | }, false); |
| 62 | }; |
| 63 | if( !document.querySelector('body.fdiff') ){ |
| 64 | /* Don't show the diff toggle button for /fdiff because it only |
| 65 | has a single file to show (and also a different DOM layout). */ |
| 66 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 67 | } |
| 68 | /** |
| 69 | Set up a "toggle all diffs" button which toggles all of the |
| 70 | above-installed checkboxes, but only if more than one diff is |
| 71 | rendered. |
| 72 | */ |
| 73 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 74 | if(icm) { |
| 75 | btnAll = D.addClass(D.a("#", "Hide diffs"), "button"); |
| 76 | D.append( icm, btnAll ); |
| 77 | btnAll.addEventListener('click', function(ev){ |
| 78 | ev.preventDefault(); |
| 79 | ev.stopPropagation(); |
| 80 | const show = checkedCount < allToggles.length; |
| 81 | for( const ckbox of allToggles ){ |
| 82 | /* Toggle all entries to match this new state. We use click() |
| 83 | instead of ckbox.checked=... so that the on-change event handler |
| 84 | fires. */ |
| 85 | if(ckbox.checked!==show) ckbox.click(); |
| 86 | } |
| 87 |
+24
-13
| --- src/fossil.diff.js | ||
| +++ src/fossil.diff.js | ||
| @@ -28,48 +28,59 @@ | ||
| 28 | 28 | window.fossil.onPageLoad(function(){ |
| 29 | 29 | /** |
| 30 | 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | 31 | /info and similar pages. |
| 32 | 32 | */ |
| 33 | + if( !window.fossil.page.diffControlContainer ){ | |
| 34 | + return; | |
| 35 | + } | |
| 33 | 36 | const D = window.fossil.dom; |
| 34 | - const allToggles = [/*collection of all diff-toggle checkboxes */]; | |
| 37 | + const allToggles = [/*collection of all diff-toggle checkboxes*/]; | |
| 38 | + let checkedCount = | |
| 39 | + 0 /* When showing more than one diff, keep track of how many | |
| 40 | + "show/hide" checkboxes are are checked so we can update the | |
| 41 | + "show/hide all" label dynamically. */; | |
| 42 | + let btnAll /* UI control to show/hide all diffs */; | |
| 43 | + /* Install a diff-toggle button for the given diff table element. */ | |
| 35 | 44 | const addToggle = function(diffElem){ |
| 36 | 45 | const sib = diffElem.previousElementSibling, |
| 37 | 46 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 38 | 47 | if(!sib) return; |
| 39 | 48 | const lblToggle = D.label(); |
| 40 | 49 | D.append(lblToggle, ckbox, D.text(" show/hide ")); |
| 41 | 50 | const wrapper = D.append(D.span(), lblToggle); |
| 42 | 51 | allToggles.push(ckbox); |
| 52 | + ++checkedCount; | |
| 43 | 53 | D.append(sib, D.append(wrapper, lblToggle)); |
| 44 | 54 | ckbox.addEventListener('change', function(){ |
| 45 | 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 | + } | |
| 46 | 61 | }, false); |
| 47 | 62 | }; |
| 48 | 63 | if( !document.querySelector('body.fdiff') ){ |
| 49 | 64 | /* Don't show the diff toggle button for /fdiff because it only |
| 50 | 65 | has a single file to show (and also a different DOM layout). */ |
| 51 | 66 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 52 | 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 | + */ | |
| 53 | 73 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 54 | 74 | if(icm) { |
| 55 | - const btnAll = D.addClass(D.a("#", "Show/Hide"), "button"); | |
| 75 | + btnAll = D.addClass(D.a("#", "Hide diffs"), "button"); | |
| 56 | 76 | D.append( icm, btnAll ); |
| 57 | 77 | btnAll.addEventListener('click', function(ev){ |
| 58 | 78 | ev.preventDefault(); |
| 59 | 79 | ev.stopPropagation(); |
| 60 | - /* Figure out whether we want to show all or hide all: if any diffs are | |
| 61 | - toggled off, show all, else hide all. */ | |
| 62 | - let show = false; | |
| 63 | - let ckbox; | |
| 64 | - for( ckbox of allToggles ){ | |
| 65 | - if( !ckbox.checked ){ | |
| 66 | - show = true; | |
| 67 | - break; | |
| 68 | - } | |
| 69 | - } | |
| 70 | - for( ckbox of allToggles ){ | |
| 80 | + const show = checkedCount < allToggles.length; | |
| 81 | + for( const ckbox of allToggles ){ | |
| 71 | 82 | /* Toggle all entries to match this new state. We use click() |
| 72 | 83 | instead of ckbox.checked=... so that the on-change event handler |
| 73 | 84 | fires. */ |
| 74 | 85 | if(ckbox.checked!==show) ckbox.click(); |
| 75 | 86 | } |
| 76 | 87 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -28,48 +28,59 @@ | |
| 28 | window.fossil.onPageLoad(function(){ |
| 29 | /** |
| 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | /info and similar pages. |
| 32 | */ |
| 33 | const D = window.fossil.dom; |
| 34 | const allToggles = [/*collection of all diff-toggle checkboxes */]; |
| 35 | const addToggle = function(diffElem){ |
| 36 | const sib = diffElem.previousElementSibling, |
| 37 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 38 | if(!sib) return; |
| 39 | const lblToggle = D.label(); |
| 40 | D.append(lblToggle, ckbox, D.text(" show/hide ")); |
| 41 | const wrapper = D.append(D.span(), lblToggle); |
| 42 | allToggles.push(ckbox); |
| 43 | D.append(sib, D.append(wrapper, lblToggle)); |
| 44 | ckbox.addEventListener('change', function(){ |
| 45 | diffElem.classList[this.checked ? 'remove' : 'add']('hidden'); |
| 46 | }, false); |
| 47 | }; |
| 48 | if( !document.querySelector('body.fdiff') ){ |
| 49 | /* Don't show the diff toggle button for /fdiff because it only |
| 50 | has a single file to show (and also a different DOM layout). */ |
| 51 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 52 | } |
| 53 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 54 | if(icm) { |
| 55 | const btnAll = D.addClass(D.a("#", "Show/Hide"), "button"); |
| 56 | D.append( icm, btnAll ); |
| 57 | btnAll.addEventListener('click', function(ev){ |
| 58 | ev.preventDefault(); |
| 59 | ev.stopPropagation(); |
| 60 | /* Figure out whether we want to show all or hide all: if any diffs are |
| 61 | toggled off, show all, else hide all. */ |
| 62 | let show = false; |
| 63 | let ckbox; |
| 64 | for( ckbox of allToggles ){ |
| 65 | if( !ckbox.checked ){ |
| 66 | show = true; |
| 67 | break; |
| 68 | } |
| 69 | } |
| 70 | for( ckbox of allToggles ){ |
| 71 | /* Toggle all entries to match this new state. We use click() |
| 72 | instead of ckbox.checked=... so that the on-change event handler |
| 73 | fires. */ |
| 74 | if(ckbox.checked!==show) ckbox.click(); |
| 75 | } |
| 76 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -28,48 +28,59 @@ | |
| 28 | window.fossil.onPageLoad(function(){ |
| 29 | /** |
| 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | /info and similar pages. |
| 32 | */ |
| 33 | if( !window.fossil.page.diffControlContainer ){ |
| 34 | return; |
| 35 | } |
| 36 | const D = window.fossil.dom; |
| 37 | const allToggles = [/*collection of all diff-toggle checkboxes*/]; |
| 38 | let checkedCount = |
| 39 | 0 /* When showing more than one diff, keep track of how many |
| 40 | "show/hide" checkboxes are are checked so we can update the |
| 41 | "show/hide all" label dynamically. */; |
| 42 | let btnAll /* UI control to show/hide all diffs */; |
| 43 | /* Install a diff-toggle button for the given diff table element. */ |
| 44 | const addToggle = function(diffElem){ |
| 45 | const sib = diffElem.previousElementSibling, |
| 46 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 47 | if(!sib) return; |
| 48 | const lblToggle = D.label(); |
| 49 | D.append(lblToggle, ckbox, D.text(" show/hide ")); |
| 50 | const wrapper = D.append(D.span(), lblToggle); |
| 51 | allToggles.push(ckbox); |
| 52 | ++checkedCount; |
| 53 | D.append(sib, D.append(wrapper, lblToggle)); |
| 54 | ckbox.addEventListener('change', function(){ |
| 55 | diffElem.classList[this.checked ? 'remove' : 'add']('hidden'); |
| 56 | if(btnAll){ |
| 57 | checkedCount += (this.checked ? 1 : -1); |
| 58 | btnAll.innerText = (checkedCount < allToggles.length) |
| 59 | ? "Show diffs" : "Hide diffs"; |
| 60 | } |
| 61 | }, false); |
| 62 | }; |
| 63 | if( !document.querySelector('body.fdiff') ){ |
| 64 | /* Don't show the diff toggle button for /fdiff because it only |
| 65 | has a single file to show (and also a different DOM layout). */ |
| 66 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 67 | } |
| 68 | /** |
| 69 | Set up a "toggle all diffs" button which toggles all of the |
| 70 | above-installed checkboxes, but only if more than one diff is |
| 71 | rendered. |
| 72 | */ |
| 73 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 74 | if(icm) { |
| 75 | btnAll = D.addClass(D.a("#", "Hide diffs"), "button"); |
| 76 | D.append( icm, btnAll ); |
| 77 | btnAll.addEventListener('click', function(ev){ |
| 78 | ev.preventDefault(); |
| 79 | ev.stopPropagation(); |
| 80 | const show = checkedCount < allToggles.length; |
| 81 | for( const ckbox of allToggles ){ |
| 82 | /* Toggle all entries to match this new state. We use click() |
| 83 | instead of ckbox.checked=... so that the on-change event handler |
| 84 | fires. */ |
| 85 | if(ckbox.checked!==show) ckbox.click(); |
| 86 | } |
| 87 |
+207
-59
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -609,59 +609,25 @@ | ||
| 609 | 609 | db_finalize(&q); |
| 610 | 610 | style_finish_page(); |
| 611 | 611 | } |
| 612 | 612 | |
| 613 | 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. | |
| 614 | +** Render a web-page diff of the changes in the working check-out | |
| 619 | 615 | */ |
| 620 | -void ckout_page(void){ | |
| 621 | - int vid; | |
| 622 | - char *zHostname; | |
| 623 | - char *zCwd; | |
| 616 | +static void ckout_normal_diff(int vid){ | |
| 624 | 617 | int diffType; /* 0: no diff, 1: unified, 2: side-by-side */ |
| 625 | 618 | DiffConfig DCfg,*pCfg; /* Diff details */ |
| 626 | - const char *zHome; /* Home directory */ | |
| 627 | 619 | const char *zW; /* The "w" query parameter */ |
| 628 | 620 | int nChng; /* Number of changes */ |
| 629 | 621 | Stmt q; |
| 630 | 622 | |
| 631 | - if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){ | |
| 632 | - cgi_redirectf("%R/home"); | |
| 633 | - return; | |
| 634 | - } | |
| 635 | - file_chdir(g.zLocalRoot, 0); | |
| 636 | 623 | diffType = preferred_diff_type(); |
| 637 | 624 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 638 | - vid = db_lget_int("checkout", 0); | |
| 639 | - db_unprotect(PROTECT_ALL); | |
| 640 | - vfile_check_signature(vid, CKSIG_ENOTFILE); | |
| 641 | - db_protect_pop(); | |
| 642 | - style_set_current_feature("vinfo"); | |
| 643 | - zHostname = fossil_hostname(); | |
| 644 | - zCwd = file_getcwd(0,0); | |
| 645 | - zHome = fossil_getenv("HOME"); | |
| 646 | - if( zHome ){ | |
| 647 | - int nHome = (int)strlen(zHome); | |
| 648 | - if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){ | |
| 649 | - zCwd = mprintf("~%s", zCwd+nHome); | |
| 650 | - } | |
| 651 | - } | |
| 652 | - if( zHostname ){ | |
| 653 | - style_header("Checkout Status: %h on %h", zCwd, zHostname); | |
| 654 | - }else{ | |
| 655 | - style_header("Checkout Status: %h", zCwd); | |
| 656 | - } | |
| 657 | - render_checkin_context(vid, 0, 0, 0); | |
| 658 | 625 | nChng = db_int(0, "SELECT count(*) FROM vfile" |
| 659 | 626 | " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid); |
| 660 | 627 | if( nChng==0 ){ |
| 661 | 628 | @ <p>No uncommitted changes</p> |
| 662 | - style_finish_page(); | |
| 663 | 629 | return; |
| 664 | 630 | } |
| 665 | 631 | db_prepare(&q, |
| 666 | 632 | /* 0 1 2 3 4 5 6 */ |
| 667 | 633 | "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" |
| @@ -674,11 +640,10 @@ | ||
| 674 | 640 | if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){ |
| 675 | 641 | DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; |
| 676 | 642 | }else{ |
| 677 | 643 | DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG; |
| 678 | 644 | } |
| 679 | - @ <hr> | |
| 680 | 645 | @ <div class="sectionmenu info-changes-menu"> |
| 681 | 646 | zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; |
| 682 | 647 | if( diffType!=1 ){ |
| 683 | 648 | @ %z(chref("button","%R?diff=1%s",zW))Unified Diff</a> |
| 684 | 649 | } |
| @@ -750,12 +715,167 @@ | ||
| 750 | 715 | blob_reset(&old); |
| 751 | 716 | blob_reset(&new); |
| 752 | 717 | } |
| 753 | 718 | } |
| 754 | 719 | db_finalize(&q); |
| 755 | - // @ </div> <!-- ap-002 --> | |
| 720 | + append_diff_javascript(diffType); | |
| 721 | +} | |
| 722 | + | |
| 723 | +/* | |
| 724 | +** Render a web-page diff of the changes in the working check-out to | |
| 725 | +** an external reference. | |
| 726 | +*/ | |
| 727 | +static void ckout_external_base_diff(int vid, const char *zExBase){ | |
| 728 | + int diffType; /* 0: no diff, 1: unified, 2: side-by-side */ | |
| 729 | + DiffConfig DCfg,*pCfg; /* Diff details */ | |
| 730 | + const char *zW; /* The "w" query parameter */ | |
| 731 | + Stmt q; | |
| 732 | + | |
| 733 | + diffType = preferred_diff_type(); | |
| 734 | + pCfg = construct_diff_flags(diffType, &DCfg); | |
| 735 | + db_prepare(&q, | |
| 736 | + "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid | |
| 737 | + ); | |
| 738 | + if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){ | |
| 739 | + DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; | |
| 740 | + }else{ | |
| 741 | + DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG; | |
| 742 | + } | |
| 743 | + @ <div class="sectionmenu info-changes-menu"> | |
| 744 | + zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; | |
| 745 | + if( diffType!=1 ){ | |
| 746 | + @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\ | |
| 747 | + @ Unified Diff</a> | |
| 748 | + } | |
| 749 | + if( diffType!=2 ){ | |
| 750 | + @ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\ | |
| 751 | + @ Side-by-Side Diff</a> | |
| 752 | + } | |
| 753 | + if( diffType!=0 ){ | |
| 754 | + if( *zW ){ | |
| 755 | + @ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\ | |
| 756 | + @ Show Whitespace Changes</a> | |
| 757 | + }else{ | |
| 758 | + @ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\ | |
| 759 | + @ Ignore 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); | |
| 756 | 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 | + } | |
| 757 | 877 | style_finish_page(); |
| 758 | 878 | } |
| 759 | 879 | |
| 760 | 880 | /* |
| 761 | 881 | ** WEBPAGE: vinfo |
| @@ -2077,10 +2197,16 @@ | ||
| 2077 | 2197 | ** WEBPAGE: jchunk hidden |
| 2078 | 2198 | ** URL: /jchunk/HASH?from=N&to=M |
| 2079 | 2199 | ** |
| 2080 | 2200 | ** Return lines of text from a file as a JSON array - one entry in the |
| 2081 | 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. | |
| 2082 | 2208 | ** |
| 2083 | 2209 | ** **Warning:** This is an internal-use-only interface that is subject to |
| 2084 | 2210 | ** change at any moment. External application should not use this interface |
| 2085 | 2211 | ** since the application will break when this interface changes, and this |
| 2086 | 2212 | ** interface will undoubtedly change. |
| @@ -2092,10 +2218,11 @@ | ||
| 2092 | 2218 | ** ajax_route_error(). |
| 2093 | 2219 | */ |
| 2094 | 2220 | void jchunk_page(void){ |
| 2095 | 2221 | int rid = 0; |
| 2096 | 2222 | const char *zName = PD("name", ""); |
| 2223 | + int nName = (int)(strlen(zName)&0x7fffffff); | |
| 2097 | 2224 | int iFrom = atoi(PD("from","0")); |
| 2098 | 2225 | int iTo = atoi(PD("to","0")); |
| 2099 | 2226 | int ln; |
| 2100 | 2227 | int go = 1; |
| 2101 | 2228 | const char *zSep; |
| @@ -2112,36 +2239,57 @@ | ||
| 2112 | 2239 | cgi_check_for_malice(); |
| 2113 | 2240 | if( !g.perm.Read ){ |
| 2114 | 2241 | ajax_route_error(403, "Access requires Read permissions."); |
| 2115 | 2242 | return; |
| 2116 | 2243 | } |
| 2117 | -#if 1 | |
| 2118 | - /* Re-enable this block once this code is integrated somewhere into | |
| 2119 | - the UI. */ | |
| 2120 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); | |
| 2121 | - if( rid==0 ){ | |
| 2122 | - ajax_route_error(404, "Unknown artifact: %h", zName); | |
| 2123 | - return; | |
| 2124 | - } | |
| 2125 | -#else | |
| 2126 | - /* This impl is only to simplify "manual" testing via the JS | |
| 2127 | - console. */ | |
| 2128 | - rid = symbolic_name_to_rid(zName, "*"); | |
| 2129 | - if( rid==0 ){ | |
| 2130 | - ajax_route_error(404, "Unknown artifact: %h", zName); | |
| 2131 | - return; | |
| 2132 | - }else if( rid<0 ){ | |
| 2133 | - ajax_route_error(418, "Ambiguous artifact name: %h", zName); | |
| 2134 | - return; | |
| 2135 | - } | |
| 2136 | -#endif | |
| 2137 | 2244 | if( iFrom<1 || iTo<iFrom ){ |
| 2138 | 2245 | ajax_route_error(500, "Invalid line range from=%d, to=%d.", |
| 2139 | 2246 | iFrom, iTo); |
| 2140 | 2247 | return; |
| 2141 | 2248 | } |
| 2142 | - content_get(rid, &content); | |
| 2249 | + if( zName[0]=='x' | |
| 2250 | + && ((nName-1)&1)==0 | |
| 2251 | + && validate16(&zName[1],nName-1) | |
| 2252 | + && g.perm.Admin | |
| 2253 | + && db_open_local(0) | |
| 2254 | + && cgi_is_loopback(g.zIpAddr) | |
| 2255 | + ){ | |
| 2256 | + /* Treat the HASH as a hex-encoded filename */ | |
| 2257 | + int n = (nName-1)/2; | |
| 2258 | + char *zFN = fossil_malloc(n+1); | |
| 2259 | + decode16((const u8*)&zName[1], (u8*)zFN, nName-1); | |
| 2260 | + zFN[n] = 0; | |
| 2261 | + if( file_size(zFN, ExtFILE)<0 ){ | |
| 2262 | + blob_zero(&content); | |
| 2263 | + }else{ | |
| 2264 | + blob_read_from_file(&content, zFN, ExtFILE); | |
| 2265 | + } | |
| 2266 | + fossil_free(zFN); | |
| 2267 | + }else{ | |
| 2268 | + /* Treat the HASH as an artifact hash matching BLOB.UUID */ | |
| 2269 | +#if 1 | |
| 2270 | + /* Re-enable this block once this code is integrated somewhere into | |
| 2271 | + the UI. */ | |
| 2272 | + rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); | |
| 2273 | + if( rid==0 ){ | |
| 2274 | + ajax_route_error(404, "Unknown artifact: %h", zName); | |
| 2275 | + return; | |
| 2276 | + } | |
| 2277 | +#else | |
| 2278 | + /* This impl is only to simplify "manual" testing via the JS | |
| 2279 | + console. */ | |
| 2280 | + rid = symbolic_name_to_rid(zName, "*"); | |
| 2281 | + if( rid==0 ){ | |
| 2282 | + ajax_route_error(404, "Unknown artifact: %h", zName); | |
| 2283 | + return; | |
| 2284 | + }else if( rid<0 ){ | |
| 2285 | + ajax_route_error(418, "Ambiguous artifact name: %h", zName); | |
| 2286 | + return; | |
| 2287 | + } | |
| 2288 | +#endif | |
| 2289 | + content_get(rid, &content); | |
| 2290 | + } | |
| 2143 | 2291 | g.isConst = 1; |
| 2144 | 2292 | cgi_set_content_type("application/json"); |
| 2145 | 2293 | ln = 0; |
| 2146 | 2294 | while( go && ln<iFrom ){ |
| 2147 | 2295 | go = blob_line(&content, &line); |
| 2148 | 2296 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -609,59 +609,25 @@ | |
| 609 | db_finalize(&q); |
| 610 | style_finish_page(); |
| 611 | } |
| 612 | |
| 613 | /* |
| 614 | ** WEBPAGE: ckout |
| 615 | ** |
| 616 | ** Show information about the current checkout. This page only functions |
| 617 | ** if the web server is run on a loopback interface (in other words, was |
| 618 | ** started using "fossil ui" or similar) from with on open check-out. |
| 619 | */ |
| 620 | void ckout_page(void){ |
| 621 | int vid; |
| 622 | char *zHostname; |
| 623 | char *zCwd; |
| 624 | int diffType; /* 0: no diff, 1: unified, 2: side-by-side */ |
| 625 | DiffConfig DCfg,*pCfg; /* Diff details */ |
| 626 | const char *zHome; /* Home directory */ |
| 627 | const char *zW; /* The "w" query parameter */ |
| 628 | int nChng; /* Number of changes */ |
| 629 | Stmt q; |
| 630 | |
| 631 | if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){ |
| 632 | cgi_redirectf("%R/home"); |
| 633 | return; |
| 634 | } |
| 635 | file_chdir(g.zLocalRoot, 0); |
| 636 | diffType = preferred_diff_type(); |
| 637 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 638 | vid = db_lget_int("checkout", 0); |
| 639 | db_unprotect(PROTECT_ALL); |
| 640 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 641 | db_protect_pop(); |
| 642 | style_set_current_feature("vinfo"); |
| 643 | zHostname = fossil_hostname(); |
| 644 | zCwd = file_getcwd(0,0); |
| 645 | zHome = fossil_getenv("HOME"); |
| 646 | if( zHome ){ |
| 647 | int nHome = (int)strlen(zHome); |
| 648 | if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){ |
| 649 | zCwd = mprintf("~%s", zCwd+nHome); |
| 650 | } |
| 651 | } |
| 652 | if( zHostname ){ |
| 653 | style_header("Checkout Status: %h on %h", zCwd, zHostname); |
| 654 | }else{ |
| 655 | style_header("Checkout Status: %h", zCwd); |
| 656 | } |
| 657 | render_checkin_context(vid, 0, 0, 0); |
| 658 | nChng = db_int(0, "SELECT count(*) FROM vfile" |
| 659 | " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid); |
| 660 | if( nChng==0 ){ |
| 661 | @ <p>No uncommitted changes</p> |
| 662 | style_finish_page(); |
| 663 | return; |
| 664 | } |
| 665 | db_prepare(&q, |
| 666 | /* 0 1 2 3 4 5 6 */ |
| 667 | "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" |
| @@ -674,11 +640,10 @@ | |
| 674 | if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){ |
| 675 | DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; |
| 676 | }else{ |
| 677 | DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG; |
| 678 | } |
| 679 | @ <hr> |
| 680 | @ <div class="sectionmenu info-changes-menu"> |
| 681 | zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; |
| 682 | if( diffType!=1 ){ |
| 683 | @ %z(chref("button","%R?diff=1%s",zW))Unified Diff</a> |
| 684 | } |
| @@ -750,12 +715,167 @@ | |
| 750 | blob_reset(&old); |
| 751 | blob_reset(&new); |
| 752 | } |
| 753 | } |
| 754 | db_finalize(&q); |
| 755 | // @ </div> <!-- ap-002 --> |
| 756 | append_diff_javascript(diffType); |
| 757 | style_finish_page(); |
| 758 | } |
| 759 | |
| 760 | /* |
| 761 | ** WEBPAGE: vinfo |
| @@ -2077,10 +2197,16 @@ | |
| 2077 | ** WEBPAGE: jchunk hidden |
| 2078 | ** URL: /jchunk/HASH?from=N&to=M |
| 2079 | ** |
| 2080 | ** Return lines of text from a file as a JSON array - one entry in the |
| 2081 | ** array for each line of text. |
| 2082 | ** |
| 2083 | ** **Warning:** This is an internal-use-only interface that is subject to |
| 2084 | ** change at any moment. External application should not use this interface |
| 2085 | ** since the application will break when this interface changes, and this |
| 2086 | ** interface will undoubtedly change. |
| @@ -2092,10 +2218,11 @@ | |
| 2092 | ** ajax_route_error(). |
| 2093 | */ |
| 2094 | void jchunk_page(void){ |
| 2095 | int rid = 0; |
| 2096 | const char *zName = PD("name", ""); |
| 2097 | int iFrom = atoi(PD("from","0")); |
| 2098 | int iTo = atoi(PD("to","0")); |
| 2099 | int ln; |
| 2100 | int go = 1; |
| 2101 | const char *zSep; |
| @@ -2112,36 +2239,57 @@ | |
| 2112 | cgi_check_for_malice(); |
| 2113 | if( !g.perm.Read ){ |
| 2114 | ajax_route_error(403, "Access requires Read permissions."); |
| 2115 | return; |
| 2116 | } |
| 2117 | #if 1 |
| 2118 | /* Re-enable this block once this code is integrated somewhere into |
| 2119 | the UI. */ |
| 2120 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); |
| 2121 | if( rid==0 ){ |
| 2122 | ajax_route_error(404, "Unknown artifact: %h", zName); |
| 2123 | return; |
| 2124 | } |
| 2125 | #else |
| 2126 | /* This impl is only to simplify "manual" testing via the JS |
| 2127 | console. */ |
| 2128 | rid = symbolic_name_to_rid(zName, "*"); |
| 2129 | if( rid==0 ){ |
| 2130 | ajax_route_error(404, "Unknown artifact: %h", zName); |
| 2131 | return; |
| 2132 | }else if( rid<0 ){ |
| 2133 | ajax_route_error(418, "Ambiguous artifact name: %h", zName); |
| 2134 | return; |
| 2135 | } |
| 2136 | #endif |
| 2137 | if( iFrom<1 || iTo<iFrom ){ |
| 2138 | ajax_route_error(500, "Invalid line range from=%d, to=%d.", |
| 2139 | iFrom, iTo); |
| 2140 | return; |
| 2141 | } |
| 2142 | content_get(rid, &content); |
| 2143 | g.isConst = 1; |
| 2144 | cgi_set_content_type("application/json"); |
| 2145 | ln = 0; |
| 2146 | while( go && ln<iFrom ){ |
| 2147 | go = blob_line(&content, &line); |
| 2148 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -609,59 +609,25 @@ | |
| 609 | db_finalize(&q); |
| 610 | style_finish_page(); |
| 611 | } |
| 612 | |
| 613 | /* |
| 614 | ** Render a web-page diff of the changes in the working check-out |
| 615 | */ |
| 616 | static void ckout_normal_diff(int vid){ |
| 617 | int diffType; /* 0: no diff, 1: unified, 2: side-by-side */ |
| 618 | DiffConfig DCfg,*pCfg; /* Diff details */ |
| 619 | const char *zW; /* The "w" query parameter */ |
| 620 | int nChng; /* Number of changes */ |
| 621 | Stmt q; |
| 622 | |
| 623 | diffType = preferred_diff_type(); |
| 624 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 625 | nChng = db_int(0, "SELECT count(*) FROM vfile" |
| 626 | " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid); |
| 627 | if( nChng==0 ){ |
| 628 | @ <p>No uncommitted changes</p> |
| 629 | return; |
| 630 | } |
| 631 | db_prepare(&q, |
| 632 | /* 0 1 2 3 4 5 6 */ |
| 633 | "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" |
| @@ -674,11 +640,10 @@ | |
| 640 | if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){ |
| 641 | DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; |
| 642 | }else{ |
| 643 | DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG; |
| 644 | } |
| 645 | @ <div class="sectionmenu info-changes-menu"> |
| 646 | zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; |
| 647 | if( diffType!=1 ){ |
| 648 | @ %z(chref("button","%R?diff=1%s",zW))Unified Diff</a> |
| 649 | } |
| @@ -750,12 +715,167 @@ | |
| 715 | blob_reset(&old); |
| 716 | blob_reset(&new); |
| 717 | } |
| 718 | } |
| 719 | db_finalize(&q); |
| 720 | append_diff_javascript(diffType); |
| 721 | } |
| 722 | |
| 723 | /* |
| 724 | ** Render a web-page diff of the changes in the working check-out to |
| 725 | ** an external reference. |
| 726 | */ |
| 727 | static void ckout_external_base_diff(int vid, const char *zExBase){ |
| 728 | int diffType; /* 0: no diff, 1: unified, 2: side-by-side */ |
| 729 | DiffConfig DCfg,*pCfg; /* Diff details */ |
| 730 | const char *zW; /* The "w" query parameter */ |
| 731 | Stmt q; |
| 732 | |
| 733 | diffType = preferred_diff_type(); |
| 734 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 735 | db_prepare(&q, |
| 736 | "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid |
| 737 | ); |
| 738 | if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){ |
| 739 | DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; |
| 740 | }else{ |
| 741 | DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG; |
| 742 | } |
| 743 | @ <div class="sectionmenu info-changes-menu"> |
| 744 | zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; |
| 745 | if( diffType!=1 ){ |
| 746 | @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\ |
| 747 | @ Unified Diff</a> |
| 748 | } |
| 749 | if( diffType!=2 ){ |
| 750 | @ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\ |
| 751 | @ Side-by-Side Diff</a> |
| 752 | } |
| 753 | if( diffType!=0 ){ |
| 754 | if( *zW ){ |
| 755 | @ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\ |
| 756 | @ Show Whitespace Changes</a> |
| 757 | }else{ |
| 758 | @ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\ |
| 759 | @ Ignore Whitespace</a> |
| 760 | } |
| 761 | } |
| 762 | @ </div> |
| 763 | while( db_step(&q)==SQLITE_ROW ){ |
| 764 | const char *zFile; /* Name of file in the repository */ |
| 765 | char *zLhs; /* Full name of left-hand side file */ |
| 766 | char *zRhs; /* Full name of right-hand side file */ |
| 767 | Blob rhs; /* Full text of RHS */ |
| 768 | Blob lhs; /* Full text of LHS */ |
| 769 | |
| 770 | zFile = db_column_text(&q,0); |
| 771 | zLhs = mprintf("%s/%s", zExBase, zFile); |
| 772 | zRhs = mprintf("%s%s", g.zLocalRoot, zFile); |
| 773 | if( file_size(zLhs, ExtFILE)<0 ){ |
| 774 | @ <div class='file-change-line'><span> |
| 775 | @ Missing from external baseline: %h(zFile) |
| 776 | @ </span></div> |
| 777 | }else{ |
| 778 | blob_read_from_file(&lhs, zLhs, ExtFILE); |
| 779 | blob_read_from_file(&rhs, zRhs, ExtFILE); |
| 780 | if( blob_size(&lhs)!=blob_size(&rhs) |
| 781 | || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 |
| 782 | ){ |
| 783 | @ <div class='file-change-line'><span> |
| 784 | @ Changes to %h(zFile) |
| 785 | @ </span></div> |
| 786 | if( pCfg ){ |
| 787 | char *zFullFN; |
| 788 | char *zHexFN; |
| 789 | int nFullFN; |
| 790 | zFullFN = file_canonical_name_dup(zLhs); |
| 791 | nFullFN = (int)strlen(zFullFN); |
| 792 | zHexFN = fossil_malloc( nFullFN*2 + 5 ); |
| 793 | zHexFN[0] = 'x'; |
| 794 | encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN); |
| 795 | zHexFN[1+nFullFN*2] = 0; |
| 796 | fossil_free(zFullFN); |
| 797 | pCfg->zLeftHash = zHexFN; |
| 798 | text_diff(&lhs, &rhs, cgi_output_blob(), pCfg); |
| 799 | pCfg->zLeftHash = 0; |
| 800 | fossil_free(zHexFN); |
| 801 | } |
| 802 | } |
| 803 | blob_reset(&lhs); |
| 804 | blob_reset(&rhs); |
| 805 | } |
| 806 | fossil_free(zLhs); |
| 807 | fossil_free(zRhs); |
| 808 | } |
| 809 | db_finalize(&q); |
| 810 | append_diff_javascript(diffType); |
| 811 | } |
| 812 | |
| 813 | /* |
| 814 | ** WEBPAGE: ckout |
| 815 | ** |
| 816 | ** Show information about the current checkout. This page only functions |
| 817 | ** if the web server is run on a loopback interface (in other words, was |
| 818 | ** started using "fossil ui" or similar) from within an open check-out. |
| 819 | ** |
| 820 | ** If the "exbase=PATH" query parameter is provided, then the diff shown |
| 821 | ** uses the files in PATH as the baseline. This is the same as using |
| 822 | ** the "--from PATH" argument to the "fossil diff" command-line. In fact, |
| 823 | ** when using "fossil ui --from PATH", the --from argument becomes the value |
| 824 | ** of the exbase query parameter for the start page. |
| 825 | ** |
| 826 | ** Other query parameters related to diffs are also accepted. |
| 827 | */ |
| 828 | void ckout_page(void){ |
| 829 | int vid; |
| 830 | const char *zHome; /* Home directory */ |
| 831 | int nHome; |
| 832 | const char *zExBase; |
| 833 | char *zHostname; |
| 834 | char *zCwd; |
| 835 | |
| 836 | if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){ |
| 837 | cgi_redirectf("%R/home"); |
| 838 | return; |
| 839 | } |
| 840 | file_chdir(g.zLocalRoot, 0); |
| 841 | vid = db_lget_int("checkout", 0); |
| 842 | db_unprotect(PROTECT_ALL); |
| 843 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 844 | db_protect_pop(); |
| 845 | style_set_current_feature("vinfo"); |
| 846 | zHostname = fossil_hostname(); |
| 847 | zCwd = file_getcwd(0,0); |
| 848 | zHome = fossil_getenv("HOME"); |
| 849 | if( zHome ){ |
| 850 | nHome = (int)strlen(zHome); |
| 851 | if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){ |
| 852 | zCwd = mprintf("~%s", zCwd+nHome); |
| 853 | } |
| 854 | }else{ |
| 855 | nHome = 0; |
| 856 | } |
| 857 | if( zHostname ){ |
| 858 | style_header("Checkout Status: %h on %h", zCwd, zHostname); |
| 859 | }else{ |
| 860 | style_header("Checkout Status: %h", zCwd); |
| 861 | } |
| 862 | render_checkin_context(vid, 0, 0, 0); |
| 863 | @ <hr> |
| 864 | zExBase = P("exbase"); |
| 865 | if( zExBase && zExBase[0] ){ |
| 866 | char *zCBase = file_canonical_name_dup(zExBase); |
| 867 | if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){ |
| 868 | @ <p>Using external baseline: ~%h(zCBase+nHome)</p> |
| 869 | }else{ |
| 870 | @ <p>Using external baseline: %h(zCBase)</p> |
| 871 | } |
| 872 | ckout_external_base_diff(vid, zCBase); |
| 873 | fossil_free(zCBase); |
| 874 | }else{ |
| 875 | ckout_normal_diff(vid); |
| 876 | } |
| 877 | style_finish_page(); |
| 878 | } |
| 879 | |
| 880 | /* |
| 881 | ** WEBPAGE: vinfo |
| @@ -2077,10 +2197,16 @@ | |
| 2197 | ** WEBPAGE: jchunk hidden |
| 2198 | ** URL: /jchunk/HASH?from=N&to=M |
| 2199 | ** |
| 2200 | ** Return lines of text from a file as a JSON array - one entry in the |
| 2201 | ** array for each line of text. |
| 2202 | ** |
| 2203 | ** The HASH is normally a sha1 or sha3 hash that identifies an artifact |
| 2204 | ** in the BLOB table of the database. However, if HASH starts with an "x" |
| 2205 | ** and is followed by valid hexadecimal, and if we are running in a |
| 2206 | ** "fossil ui" situation (locally and with privilege), then decode the hex |
| 2207 | ** into a filename and read the file content from that name. |
| 2208 | ** |
| 2209 | ** **Warning:** This is an internal-use-only interface that is subject to |
| 2210 | ** change at any moment. External application should not use this interface |
| 2211 | ** since the application will break when this interface changes, and this |
| 2212 | ** interface will undoubtedly change. |
| @@ -2092,10 +2218,11 @@ | |
| 2218 | ** ajax_route_error(). |
| 2219 | */ |
| 2220 | void jchunk_page(void){ |
| 2221 | int rid = 0; |
| 2222 | const char *zName = PD("name", ""); |
| 2223 | int nName = (int)(strlen(zName)&0x7fffffff); |
| 2224 | int iFrom = atoi(PD("from","0")); |
| 2225 | int iTo = atoi(PD("to","0")); |
| 2226 | int ln; |
| 2227 | int go = 1; |
| 2228 | const char *zSep; |
| @@ -2112,36 +2239,57 @@ | |
| 2239 | cgi_check_for_malice(); |
| 2240 | if( !g.perm.Read ){ |
| 2241 | ajax_route_error(403, "Access requires Read permissions."); |
| 2242 | return; |
| 2243 | } |
| 2244 | if( iFrom<1 || iTo<iFrom ){ |
| 2245 | ajax_route_error(500, "Invalid line range from=%d, to=%d.", |
| 2246 | iFrom, iTo); |
| 2247 | return; |
| 2248 | } |
| 2249 | if( zName[0]=='x' |
| 2250 | && ((nName-1)&1)==0 |
| 2251 | && validate16(&zName[1],nName-1) |
| 2252 | && g.perm.Admin |
| 2253 | && db_open_local(0) |
| 2254 | && cgi_is_loopback(g.zIpAddr) |
| 2255 | ){ |
| 2256 | /* Treat the HASH as a hex-encoded filename */ |
| 2257 | int n = (nName-1)/2; |
| 2258 | char *zFN = fossil_malloc(n+1); |
| 2259 | decode16((const u8*)&zName[1], (u8*)zFN, nName-1); |
| 2260 | zFN[n] = 0; |
| 2261 | if( file_size(zFN, ExtFILE)<0 ){ |
| 2262 | blob_zero(&content); |
| 2263 | }else{ |
| 2264 | blob_read_from_file(&content, zFN, ExtFILE); |
| 2265 | } |
| 2266 | fossil_free(zFN); |
| 2267 | }else{ |
| 2268 | /* Treat the HASH as an artifact hash matching BLOB.UUID */ |
| 2269 | #if 1 |
| 2270 | /* Re-enable this block once this code is integrated somewhere into |
| 2271 | the UI. */ |
| 2272 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); |
| 2273 | if( rid==0 ){ |
| 2274 | ajax_route_error(404, "Unknown artifact: %h", zName); |
| 2275 | return; |
| 2276 | } |
| 2277 | #else |
| 2278 | /* This impl is only to simplify "manual" testing via the JS |
| 2279 | console. */ |
| 2280 | rid = symbolic_name_to_rid(zName, "*"); |
| 2281 | if( rid==0 ){ |
| 2282 | ajax_route_error(404, "Unknown artifact: %h", zName); |
| 2283 | return; |
| 2284 | }else if( rid<0 ){ |
| 2285 | ajax_route_error(418, "Ambiguous artifact name: %h", zName); |
| 2286 | return; |
| 2287 | } |
| 2288 | #endif |
| 2289 | content_get(rid, &content); |
| 2290 | } |
| 2291 | g.isConst = 1; |
| 2292 | cgi_set_content_type("application/json"); |
| 2293 | ln = 0; |
| 2294 | while( go && ln<iFrom ){ |
| 2295 | go = blob_line(&content, &line); |
| 2296 |
+10
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -3178,10 +3178,11 @@ | ||
| 3178 | 3178 | ** --errorlog FILE Append HTTP error messages to FILE |
| 3179 | 3179 | ** --extroot DIR Document root for the /ext extension mechanism |
| 3180 | 3180 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 3181 | 3181 | ** --fossilcmd PATH The pathname of the "fossil" executable on the remote |
| 3182 | 3182 | ** system when REPOSITORY is remote. |
| 3183 | +** --from PATH Use PATH as the diff baseline for the /ckout page | |
| 3183 | 3184 | ** --localauth Enable automatic login for requests from localhost |
| 3184 | 3185 | ** --localhost Listen on 127.0.0.1 only (always true for "ui") |
| 3185 | 3186 | ** --https Indicates that the input is coming through a reverse |
| 3186 | 3187 | ** proxy that has already translated HTTPS into HTTP. |
| 3187 | 3188 | ** --jsmode MODE Determine how JavaScript is delivered with pages. |
| @@ -3250,10 +3251,11 @@ | ||
| 3250 | 3251 | const char *zInitPage = 0; /* Start on this page. --page option */ |
| 3251 | 3252 | int findServerArg = 2; /* argv index for find_server_repository() */ |
| 3252 | 3253 | char *zRemote = 0; /* Remote host on which to run "fossil ui" */ |
| 3253 | 3254 | const char *zJsMode; /* The --jsmode parameter */ |
| 3254 | 3255 | const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ |
| 3256 | + const char *zFrom; /* Value for --from */ | |
| 3255 | 3257 | |
| 3256 | 3258 | |
| 3257 | 3259 | #if USE_SEE |
| 3258 | 3260 | db_setup_for_saved_encryption_key(); |
| 3259 | 3261 | #endif |
| @@ -3286,13 +3288,21 @@ | ||
| 3286 | 3288 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 3287 | 3289 | Th_InitTraceLog(); |
| 3288 | 3290 | zPort = find_option("port", "P", 1); |
| 3289 | 3291 | isUiCmd = g.argv[1][0]=='u'; |
| 3290 | 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 | + } | |
| 3291 | 3298 | zInitPage = find_option("page", "p", 1); |
| 3292 | 3299 | if( zInitPage && zInitPage[0]=='/' ) zInitPage++; |
| 3293 | 3300 | zFossilCmd = find_option("fossilcmd", 0, 1); |
| 3301 | + if( zFrom && zInitPage==0 ){ | |
| 3302 | + zInitPage = mprintf("ckout?exbase=%T", zFrom); | |
| 3303 | + } | |
| 3294 | 3304 | } |
| 3295 | 3305 | zNotFound = find_option("notfound", 0, 1); |
| 3296 | 3306 | allowRepoList = find_option("repolist",0,0)!=0; |
| 3297 | 3307 | if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; |
| 3298 | 3308 | zAltBase = find_option("baseurl", 0, 1); |
| 3299 | 3309 |
| --- 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 @@ | ||
| 687 | 687 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 688 | 688 | ** used for merging, named *-baseline, *-original, |
| 689 | 689 | ** and *-merge. |
| 690 | 690 | ** -n|--dry-run Do not actually change files on disk |
| 691 | 691 | ** --nosync Do not auto-sync prior to merging |
| 692 | +** --noundo Do not record changes in the undo log | |
| 692 | 693 | ** -v|--verbose Show additional details of the merge |
| 693 | 694 | */ |
| 694 | 695 | void merge_cmd(void){ |
| 695 | 696 | int vid; /* Current version "V" */ |
| 696 | 697 | int mid; /* Version we are merging from "M" */ |
| @@ -712,10 +713,11 @@ | ||
| 712 | 713 | int nOverwrite = 0; /* Number of unmanaged files overwritten */ |
| 713 | 714 | char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ |
| 714 | 715 | const char *zVersion; /* The VERSION argument */ |
| 715 | 716 | int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ |
| 716 | 717 | int nMerge = 0; /* Number of prior merges processed */ |
| 718 | + int useUndo = 1; /* True to record changes in the undo log */ | |
| 717 | 719 | Stmt q; /* SQL statment used for merge processing */ |
| 718 | 720 | |
| 719 | 721 | |
| 720 | 722 | /* Notation: |
| 721 | 723 | ** |
| @@ -760,10 +762,12 @@ | ||
| 760 | 762 | ** * The --dry-run option is also useful in combination with --debug. |
| 761 | 763 | */ |
| 762 | 764 | debugFlag = find_option("debug",0,0)!=0; |
| 763 | 765 | if( debugFlag && verboseFlag ) debugFlag = 2; |
| 764 | 766 | showVfileFlag = find_option("show-vfile",0,0)!=0; |
| 767 | + useUndo = find_option("noundo",0,0)==0; | |
| 768 | + if( dryRunFlag ) useUndo = 0; | |
| 765 | 769 | |
| 766 | 770 | verify_all_options(); |
| 767 | 771 | db_must_be_within_tree(); |
| 768 | 772 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 769 | 773 | vid = db_lget_int("checkout", 0); |
| @@ -926,11 +930,11 @@ | ||
| 926 | 930 | integrateFlag ? "integrate:" : "merge-from:"); |
| 927 | 931 | print_checkin_description(pid, 12, "baseline:"); |
| 928 | 932 | } |
| 929 | 933 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 930 | 934 | if( nMerge==0 ) db_begin_transaction(); |
| 931 | - if( !dryRunFlag ) undo_begin(); | |
| 935 | + if( useUndo ) undo_begin(); | |
| 932 | 936 | if( load_vfile_from_rid(mid) && !forceMissingFlag ){ |
| 933 | 937 | fossil_fatal("missing content, unable to merge"); |
| 934 | 938 | } |
| 935 | 939 | if( load_vfile_from_rid(pid) && !forceMissingFlag ){ |
| 936 | 940 | fossil_fatal("missing content, unable to merge"); |
| @@ -1183,12 +1187,12 @@ | ||
| 1183 | 1187 | int ridm = db_column_int(&q, 1); |
| 1184 | 1188 | const char *zName = db_column_text(&q, 2); |
| 1185 | 1189 | int islinkm = db_column_int(&q, 3); |
| 1186 | 1190 | /* Copy content from idm over into idv. Overwrite idv. */ |
| 1187 | 1191 | fossil_print("UPDATE %s\n", zName); |
| 1192 | + if( useUndo ) undo_save(zName); | |
| 1188 | 1193 | if( !dryRunFlag ){ |
| 1189 | - undo_save(zName); | |
| 1190 | 1194 | db_multi_exec( |
| 1191 | 1195 | "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d," |
| 1192 | 1196 | " mhash=CASE WHEN rid<>%d" |
| 1193 | 1197 | " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END" |
| 1194 | 1198 | " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv |
| @@ -1265,11 +1269,11 @@ | ||
| 1265 | 1269 | }else{ |
| 1266 | 1270 | i64 sz; |
| 1267 | 1271 | const char *zErrMsg = 0; |
| 1268 | 1272 | int nc = 0; |
| 1269 | 1273 | |
| 1270 | - if( !dryRunFlag ) undo_save(zName); | |
| 1274 | + if( useUndo ) undo_save(zName); | |
| 1271 | 1275 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1272 | 1276 | sz = file_size(zFullPath, ExtFILE); |
| 1273 | 1277 | content_get(ridp, &p); |
| 1274 | 1278 | content_get(ridm, &m); |
| 1275 | 1279 | if( isBinary ){ |
| @@ -1352,11 +1356,11 @@ | ||
| 1352 | 1356 | zErrMsg = "local edits lost"; |
| 1353 | 1357 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1354 | 1358 | sz = file_size(zFullPath, ExtFILE); |
| 1355 | 1359 | fossil_free(zFullPath); |
| 1356 | 1360 | } |
| 1357 | - if( !dryRunFlag ) undo_save(zName); | |
| 1361 | + if( useUndo ) undo_save(zName); | |
| 1358 | 1362 | db_multi_exec( |
| 1359 | 1363 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 1360 | 1364 | ); |
| 1361 | 1365 | if( !dryRunFlag ){ |
| 1362 | 1366 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| @@ -1399,12 +1403,12 @@ | ||
| 1399 | 1403 | int idv = db_column_int(&q, 0); |
| 1400 | 1404 | const char *zOldName = db_column_text(&q, 1); |
| 1401 | 1405 | const char *zNewName = db_column_text(&q, 2); |
| 1402 | 1406 | int isExe = db_column_int(&q, 3); |
| 1403 | 1407 | 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); | |
| 1406 | 1410 | db_multi_exec( |
| 1407 | 1411 | "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q", |
| 1408 | 1412 | zOldName |
| 1409 | 1413 | ); |
| 1410 | 1414 | db_multi_exec( |
| @@ -1498,12 +1502,12 @@ | ||
| 1498 | 1502 | "VALUES('ADDED',%Q,%d,%Q)", |
| 1499 | 1503 | /* fnm */ zName, |
| 1500 | 1504 | /* ridm */ db_column_int(&q,2), |
| 1501 | 1505 | /* fnr */ zName |
| 1502 | 1506 | ); |
| 1507 | + if( useUndo ) undo_save(zName); | |
| 1503 | 1508 | if( !dryRunFlag ){ |
| 1504 | - undo_save(zName); | |
| 1505 | 1509 | vfile_to_disk(0, idm, 0, 0); |
| 1506 | 1510 | } |
| 1507 | 1511 | } |
| 1508 | 1512 | db_finalize(&q); |
| 1509 | 1513 | |
| @@ -1558,9 +1562,9 @@ | ||
| 1558 | 1562 | } |
| 1559 | 1563 | if( bMultiMerge && nConflict==0 ){ |
| 1560 | 1564 | nMerge++; |
| 1561 | 1565 | goto merge_next_child; |
| 1562 | 1566 | } |
| 1563 | - if( !dryRunFlag ) undo_finish(); | |
| 1567 | + if( useUndo ) undo_finish(); | |
| 1564 | 1568 | |
| 1565 | 1569 | db_end_transaction(dryRunFlag); |
| 1566 | 1570 | } |
| 1567 | 1571 |
| --- 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 @@ | ||
| 387 | 387 | if( unsaved_changes(0) ){ |
| 388 | 388 | if( (mFlags & PATCH_FORCE)==0 ){ |
| 389 | 389 | fossil_fatal("Cannot apply patch: there are unsaved changes " |
| 390 | 390 | "in the current check-out"); |
| 391 | 391 | }else{ |
| 392 | - blob_appendf(&cmd, "%$ revert", g.nameOfExe); | |
| 392 | + blob_appendf(&cmd, "%$ revert --noundo", g.nameOfExe); | |
| 393 | 393 | if( mFlags & PATCH_DRYRUN ){ |
| 394 | 394 | fossil_print("%s\n", blob_str(&cmd)); |
| 395 | 395 | }else{ |
| 396 | 396 | int rc = fossil_system(blob_str(&cmd)); |
| 397 | 397 | if( rc ){ |
| @@ -429,23 +429,27 @@ | ||
| 429 | 429 | } |
| 430 | 430 | } |
| 431 | 431 | } |
| 432 | 432 | blob_reset(&cmd); |
| 433 | 433 | if( db_table_exists("patch","patchmerge") ){ |
| 434 | + int nMerge = 0; | |
| 434 | 435 | db_prepare(&q, |
| 435 | 436 | "SELECT type, mhash, upper(type) FROM patch.patchmerge" |
| 436 | 437 | " WHERE type IN ('merge','cherrypick','backout','integrate')" |
| 437 | 438 | " AND mhash NOT GLOB '*[^a-fA-F0-9]*';" |
| 438 | 439 | ); |
| 439 | 440 | while( db_step(&q)==SQLITE_ROW ){ |
| 440 | 441 | const char *zType = db_column_text(&q,0); |
| 441 | 442 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 442 | 443 | 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)); | |
| 444 | 446 | }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)); | |
| 446 | 449 | } |
| 450 | + nMerge++; | |
| 447 | 451 | if( mFlags & PATCH_VERBOSE ){ |
| 448 | 452 | fossil_print("%-10s %s\n", db_column_text(&q,2), |
| 449 | 453 | db_column_text(&q,0)); |
| 450 | 454 | } |
| 451 | 455 | } |
| @@ -458,10 +462,45 @@ | ||
| 458 | 462 | fossil_fatal("unable to do merges:\n%s", |
| 459 | 463 | blob_str(&cmd)); |
| 460 | 464 | } |
| 461 | 465 | } |
| 462 | 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 | + } | |
| 463 | 502 | } |
| 464 | 503 | |
| 465 | 504 | /* Deletions */ |
| 466 | 505 | db_prepare(&q, "SELECT pathname FROM patch.chng" |
| 467 | 506 | " WHERE origname IS NULL AND delta IS NULL"); |
| 468 | 507 |
| --- 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 @@ | ||
| 854 | 854 | ** |
| 855 | 855 | ** If a file is reverted accidentally, it can be restored using |
| 856 | 856 | ** the "fossil undo" command. |
| 857 | 857 | ** |
| 858 | 858 | ** Options: |
| 859 | +** --noundo Do not record changes in the undo/redo log. | |
| 859 | 860 | ** -r|--revision VERSION Revert given FILE(s) back to given |
| 860 | 861 | ** VERSION |
| 861 | 862 | ** |
| 862 | 863 | ** See also: [[redo]], [[undo]], [[checkout]], [[update]] |
| 863 | 864 | */ |
| @@ -867,17 +868,19 @@ | ||
| 867 | 868 | ManifestFile *pCoFile; /* File within current check-out manifest */ |
| 868 | 869 | ManifestFile *pRvFile; /* File within revert version manifest */ |
| 869 | 870 | const char *zFile; /* Filename relative to check-out root */ |
| 870 | 871 | const char *zRevision; /* Selected revert version, NULL if current */ |
| 871 | 872 | Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */ |
| 873 | + int useUndo = 1; /* True to record changes in UNDO */ | |
| 872 | 874 | int i; |
| 873 | 875 | Stmt q; |
| 874 | 876 | int revertAll = 0; |
| 875 | 877 | int revisionOptNotSupported = 0; |
| 876 | 878 | |
| 877 | 879 | undo_capture_command_line(); |
| 878 | 880 | zRevision = find_option("revision", "r", 1); |
| 881 | + useUndo = find_option("noundo", 0, 0)==0; | |
| 879 | 882 | verify_all_options(); |
| 880 | 883 | |
| 881 | 884 | if( g.argc<2 ){ |
| 882 | 885 | usage("?OPTIONS? [FILE] ..."); |
| 883 | 886 | } |
| @@ -890,11 +893,15 @@ | ||
| 890 | 893 | /* Get manifests of revert version and (if different) current check-out. */ |
| 891 | 894 | pRvManifest = historical_manifest(zRevision); |
| 892 | 895 | pCoManifest = zRevision ? historical_manifest(0) : 0; |
| 893 | 896 | |
| 894 | 897 | db_begin_transaction(); |
| 895 | - undo_begin(); | |
| 898 | + if( useUndo ){ | |
| 899 | + undo_begin(); | |
| 900 | + }else{ | |
| 901 | + undo_reset(); | |
| 902 | + } | |
| 896 | 903 | db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);"); |
| 897 | 904 | |
| 898 | 905 | if( g.argc>2 ){ |
| 899 | 906 | for(i=2; i<g.argc; i++){ |
| 900 | 907 | Blob fname; |
| @@ -987,11 +994,11 @@ | ||
| 987 | 994 | if( !pRvFile ){ |
| 988 | 995 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", |
| 989 | 996 | zFile, zFile)==0 ){ |
| 990 | 997 | fossil_print("UNMANAGE %s\n", zFile); |
| 991 | 998 | }else{ |
| 992 | - undo_save(zFile); | |
| 999 | + if( useUndo ) undo_save(zFile); | |
| 993 | 1000 | file_delete(zFull); |
| 994 | 1001 | fossil_print("DELETE %s\n", zFile); |
| 995 | 1002 | } |
| 996 | 1003 | db_multi_exec( |
| 997 | 1004 | "UPDATE OR REPLACE vfile" |
| @@ -1014,11 +1021,11 @@ | ||
| 1014 | 1021 | } |
| 1015 | 1022 | |
| 1016 | 1023 | /* Get contents of reverted-to file. */ |
| 1017 | 1024 | content_get(fast_uuid_to_rid(pRvFile->zUuid), &record); |
| 1018 | 1025 | |
| 1019 | - undo_save(zFile); | |
| 1026 | + if( useUndo ) undo_save(zFile); | |
| 1020 | 1027 | if( file_size(zFull, RepoFILE)>=0 |
| 1021 | 1028 | && (rvPerm==PERM_LNK || file_islink(0)) |
| 1022 | 1029 | ){ |
| 1023 | 1030 | file_delete(zFull); |
| 1024 | 1031 | } |
| @@ -1040,12 +1047,12 @@ | ||
| 1040 | 1047 | } |
| 1041 | 1048 | blob_reset(&record); |
| 1042 | 1049 | free(zFull); |
| 1043 | 1050 | } |
| 1044 | 1051 | db_finalize(&q); |
| 1045 | - undo_finish(); | |
| 1052 | + if( useUndo) undo_finish(); | |
| 1046 | 1053 | db_end_transaction(0); |
| 1047 | 1054 | |
| 1048 | 1055 | /* Deallocate parsed manifest structures. */ |
| 1049 | 1056 | manifest_destroy(pRvManifest); |
| 1050 | 1057 | manifest_destroy(pCoManifest); |
| 1051 | 1058 | } |
| 1052 | 1059 |
| --- 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 |
+7
-2
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,13 +1,18 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <h2 id='v2_26'>Changes for version 2.26 (pending)</h2> |
| 4 | 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. | |
| 5 | 8 | * Added the [/help?cmd=/ckout|/ckout web page] to provide information |
| 6 | 9 | 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". | |
| 9 | 14 | * Added the [/help?cmd=merge-info|fossil merge-info] command and especially |
| 10 | 15 | the --tk option to that command, to provide analysis of the most recent |
| 11 | 16 | merge or update operation. |
| 12 | 17 | * Issue a warning if a user tries to commit on a check-in where the |
| 13 | 18 | branch has been changed. |
| 14 | 19 |
| --- 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 |