| | @@ -784,26 +784,27 @@ |
| 784 | 784 | blob_reset(&file); |
| 785 | 785 | return rc; |
| 786 | 786 | } |
| 787 | 787 | |
| 788 | 788 | /* |
| 789 | | -** Run a diff between the version zFrom and files on disk. zFrom might |
| 790 | | -** be NULL which means to simply show the difference between the edited |
| 791 | | -** files on disk and the check-out on which they are based. |
| 789 | +** Run a diff between the version zFrom and files on disk in the current |
| 790 | +** working checkout. zFrom might be NULL which means to simply show the |
| 791 | +** difference between the edited files on disk and the check-out on which |
| 792 | +** they are based. |
| 792 | 793 | ** |
| 793 | 794 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 794 | 795 | ** command zDiffCmd to do the diffing. |
| 795 | 796 | ** |
| 796 | 797 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 797 | 798 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 798 | 799 | ** will be skipped in addition to files that may contain binary content. |
| 799 | 800 | */ |
| 800 | | -void diff_against_disk( |
| 801 | +void diff_version_to_checkout( |
| 801 | 802 | const char *zFrom, /* Version to difference from */ |
| 802 | 803 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 803 | 804 | FileDirList *pFileDir, /* Which files to diff */ |
| 804 | | - Blob *pOut /* Blob to output diff instead of stdout */ |
| 805 | + Blob *pOut /* Blob to output diff instead of stdout */ |
| 805 | 806 | ){ |
| 806 | 807 | int vid; |
| 807 | 808 | Blob sql; |
| 808 | 809 | Stmt q; |
| 809 | 810 | int asNewFile; /* Treat non-existant files as empty files */ |
| | @@ -928,20 +929,20 @@ |
| 928 | 929 | db_finalize(&q); |
| 929 | 930 | db_end_transaction(1); /* ROLLBACK */ |
| 930 | 931 | } |
| 931 | 932 | |
| 932 | 933 | /* |
| 933 | | -** Run a diff between the undo buffer and files on disk. |
| 934 | +** Run a diff from the undo buffer to files on disk. |
| 934 | 935 | ** |
| 935 | 936 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 936 | 937 | ** command zDiffCmd to do the diffing. |
| 937 | 938 | ** |
| 938 | 939 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 939 | 940 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 940 | 941 | ** will be skipped in addition to files that may contain binary content. |
| 941 | 942 | */ |
| 942 | | -static void diff_against_undo( |
| 943 | +static void diff_undo_to_checkout( |
| 943 | 944 | DiffConfig *pCfg, /* Flags controlling diff output */ |
| 944 | 945 | FileDirList *pFileDir /* List of files and directories to diff */ |
| 945 | 946 | ){ |
| 946 | 947 | Stmt q; |
| 947 | 948 | Blob content; |
| | @@ -1088,10 +1089,67 @@ |
| 1088 | 1089 | } |
| 1089 | 1090 | } |
| 1090 | 1091 | manifest_destroy(pFrom); |
| 1091 | 1092 | manifest_destroy(pTo); |
| 1092 | 1093 | } |
| 1094 | + |
| 1095 | +/* |
| 1096 | +** Compute the difference from an external tree of files to the current |
| 1097 | +** working checkout with its edits. |
| 1098 | +** |
| 1099 | +** To put it another way: Every managed file in the current working |
| 1100 | +** checkout is compared to the file with same under in zExternBase. The |
| 1101 | +** zExternBase files are on the left and the files in the current working |
| 1102 | +** directory are on the right. |
| 1103 | +*/ |
| 1104 | +void diff_externbase_to_checkout( |
| 1105 | + const char *zExternBase, /* Remote tree to use as the baseline */ |
| 1106 | + DiffConfig *pCfg, /* Diff settings */ |
| 1107 | + FileDirList *pFileDir /* Only look at these files */ |
| 1108 | +){ |
| 1109 | + int vid; |
| 1110 | + Stmt q; |
| 1111 | + |
| 1112 | + vid = db_lget_int("checkout",0); |
| 1113 | + if( file_isdir(zExternBase, ExtFILE)!=1 ){ |
| 1114 | + fossil_fatal("\"%s\" is not a directory", zExternBase); |
| 1115 | + } |
| 1116 | + db_prepare(&q, |
| 1117 | + "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", |
| 1118 | + vid |
| 1119 | + ); |
| 1120 | + while( db_step(&q)==SQLITE_ROW ){ |
| 1121 | + const char *zFile; /* Name of file in the repository */ |
| 1122 | + char *zLhs; /* Full name of left-hand side file */ |
| 1123 | + char *zRhs; /* Full name of right-hand side file */ |
| 1124 | + Blob rhs; /* Full text of RHS */ |
| 1125 | + Blob lhs; /* Full text of LHS */ |
| 1126 | + |
| 1127 | + zFile = db_column_text(&q,0); |
| 1128 | + if( !file_dir_match(pFileDir, zFile) ) continue; |
| 1129 | + zLhs = mprintf("%s/%s", zExternBase, zFile); |
| 1130 | + zRhs = mprintf("%s%s", g.zLocalRoot, zFile); |
| 1131 | + if( file_size(zLhs, ExtFILE)<0 ){ |
| 1132 | + blob_zero(&lhs); |
| 1133 | + }else{ |
| 1134 | + blob_read_from_file(&lhs, zLhs, ExtFILE); |
| 1135 | + } |
| 1136 | + blob_read_from_file(&rhs, zRhs, ExtFILE); |
| 1137 | + if( blob_size(&lhs)!=blob_size(&rhs) |
| 1138 | + || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 |
| 1139 | + ){ |
| 1140 | + diff_print_index(zFile, pCfg, 0); |
| 1141 | + diff_file_mem(&lhs, &rhs, zFile, pCfg); |
| 1142 | + } |
| 1143 | + blob_reset(&lhs); |
| 1144 | + blob_reset(&rhs); |
| 1145 | + fossil_free(zLhs); |
| 1146 | + fossil_free(zRhs); |
| 1147 | + } |
| 1148 | + db_finalize(&q); |
| 1149 | +} |
| 1150 | + |
| 1093 | 1151 | |
| 1094 | 1152 | /* |
| 1095 | 1153 | ** Return the name of the external diff command, or return NULL if |
| 1096 | 1154 | ** no external diff command is defined. |
| 1097 | 1155 | */ |
| | @@ -1270,11 +1328,12 @@ |
| 1270 | 1328 | ** --strip-trailing-cr Strip trailing CR |
| 1271 | 1329 | ** --tcl Tcl-formatted output used internally by --tk |
| 1272 | 1330 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1273 | 1331 | ** --tk Launch a Tcl/Tk GUI for display |
| 1274 | 1332 | ** --to VERSION Select VERSION as target for the diff |
| 1275 | | -** --undo Diff against the "undo" buffer |
| 1333 | +** --external-baseline DIR Use files under DIR as the baseline |
| 1334 | +** --undo Use the undo buffer as the baseline |
| 1276 | 1335 | ** --unified Unified diff |
| 1277 | 1336 | ** -v|--verbose Output complete text of added or deleted files |
| 1278 | 1337 | ** -h|--versions Show compared versions in the diff header |
| 1279 | 1338 | ** --webpage Format output as a stand-alone HTML webpage |
| 1280 | 1339 | ** -W|--width N Width of lines in side-by-side diff |
| | @@ -1284,10 +1343,11 @@ |
| 1284 | 1343 | int isGDiff; /* True for gdiff. False for normal diff */ |
| 1285 | 1344 | const char *zFrom; /* Source version number */ |
| 1286 | 1345 | const char *zTo; /* Target version number */ |
| 1287 | 1346 | const char *zCheckin; /* Check-in version number */ |
| 1288 | 1347 | const char *zBranch; /* Branch to diff */ |
| 1348 | + const char *zExBase; /* The --external-baseline option */ |
| 1289 | 1349 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1290 | 1350 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1291 | 1351 | DiffConfig DCfg; /* Diff configuration object */ |
| 1292 | 1352 | |
| 1293 | 1353 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| | @@ -1297,12 +1357,17 @@ |
| 1297 | 1357 | isGDiff = g.argv[1][0]=='g'; |
| 1298 | 1358 | zFrom = find_option("from", "r", 1); |
| 1299 | 1359 | zTo = find_option("to", 0, 1); |
| 1300 | 1360 | zCheckin = find_option("checkin", "ci", 1); |
| 1301 | 1361 | zBranch = find_option("branch", 0, 1); |
| 1362 | + zExBase = find_option("external-baseline", 0, 1); |
| 1302 | 1363 | againstUndo = find_option("undo",0,0)!=0; |
| 1303 | | - if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ |
| 1364 | + if( zExBase && (zFrom || zTo || zCheckin || zBranch || againstUndo) ){ |
| 1365 | + fossil_fatal("cannot use --external-baseline together with any of" |
| 1366 | + " --from, --to, --checkin, --branch, or --undo"); |
| 1367 | + } |
| 1368 | + if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ |
| 1304 | 1369 | fossil_fatal("cannot use --undo together with --from, --to, --checkin," |
| 1305 | 1370 | " or --branch"); |
| 1306 | 1371 | } |
| 1307 | 1372 | if( zBranch ){ |
| 1308 | 1373 | if( zTo || zFrom || zCheckin ){ |
| | @@ -1309,11 +1374,11 @@ |
| 1309 | 1374 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1310 | 1375 | } |
| 1311 | 1376 | zTo = zBranch; |
| 1312 | 1377 | zFrom = mprintf("root:%s", zBranch); |
| 1313 | 1378 | } |
| 1314 | | - if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ |
| 1379 | + if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1315 | 1380 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1316 | 1381 | } |
| 1317 | 1382 | diff_options(&DCfg, isGDiff, 0); |
| 1318 | 1383 | determine_exec_relative_option(1); |
| 1319 | 1384 | if( 0==zCheckin ){ |
| | @@ -1357,18 +1422,20 @@ |
| 1357 | 1422 | if( zFrom==0 ){ |
| 1358 | 1423 | fossil_fatal("check-in %s has no parent", zTo); |
| 1359 | 1424 | } |
| 1360 | 1425 | } |
| 1361 | 1426 | diff_begin(&DCfg); |
| 1362 | | - if( againstUndo ){ |
| 1427 | + if( zExBase ){ |
| 1428 | + diff_externbase_to_checkout(zExBase, &DCfg, pFileDir); |
| 1429 | + }else if( againstUndo ){ |
| 1363 | 1430 | if( db_lget_int("undo_available",0)==0 ){ |
| 1364 | 1431 | fossil_print("No undo or redo is available\n"); |
| 1365 | 1432 | return; |
| 1366 | 1433 | } |
| 1367 | | - diff_against_undo(&DCfg, pFileDir); |
| 1434 | + diff_undo_to_checkout(&DCfg, pFileDir); |
| 1368 | 1435 | }else if( zTo==0 ){ |
| 1369 | | - diff_against_disk(zFrom, &DCfg, pFileDir, 0); |
| 1436 | + diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0); |
| 1370 | 1437 | }else{ |
| 1371 | 1438 | diff_two_versions(zFrom, zTo, &DCfg, pFileDir); |
| 1372 | 1439 | } |
| 1373 | 1440 | if( pFileDir ){ |
| 1374 | 1441 | int i; |
| 1375 | 1442 | |