Fossil SCM
Work toward improving the "diff" command. Get the "-r" or "--from" option working.
Commit
a51808c0a514480020084bb848abdb869a41aab8
Parent
e200c8d65f4fa4e…
2 files changed
+229
-170
+8
-1
+229
-170
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -46,180 +46,10 @@ | ||
| 46 | 46 | } |
| 47 | 47 | } |
| 48 | 48 | blob_append(pBlob, zIn, -1); |
| 49 | 49 | } |
| 50 | 50 | |
| 51 | -/* | |
| 52 | -** Run the fossil diff command separately for every file in the current | |
| 53 | -** checkout that has changed. | |
| 54 | -*/ | |
| 55 | -static void diff_all(int internalDiff, const char *zRevision){ | |
| 56 | - Stmt q; | |
| 57 | - Blob cmd; | |
| 58 | - int nCmdBase; | |
| 59 | - int vid; | |
| 60 | - | |
| 61 | - vid = db_lget_int("checkout", 0); | |
| 62 | - vfile_check_signature(vid); | |
| 63 | - blob_zero(&cmd); | |
| 64 | - shell_escape(&cmd, g.argv[0]); | |
| 65 | - blob_append(&cmd, " diff ", -1); | |
| 66 | - if( internalDiff ){ | |
| 67 | - blob_append(&cmd, "-i ", -1); | |
| 68 | - } | |
| 69 | - if( zRevision ){ | |
| 70 | - blob_append(&cmd, "-r ", -1); | |
| 71 | - shell_escape(&cmd, zRevision); | |
| 72 | - blob_append(&cmd, " ", 1); | |
| 73 | - } | |
| 74 | - nCmdBase = blob_size(&cmd); | |
| 75 | - db_prepare(&q, | |
| 76 | - "SELECT pathname, deleted, chnged, rid FROM vfile " | |
| 77 | - "WHERE chnged OR deleted OR rid=0 ORDER BY 1" | |
| 78 | - ); | |
| 79 | - | |
| 80 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 81 | - const char *zPathname = db_column_text(&q,0); | |
| 82 | - int isDeleted = db_column_int(&q, 1); | |
| 83 | - int isChnged = db_column_int(&q,2); | |
| 84 | - int isNew = db_column_int(&q,3)==0; | |
| 85 | - char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); | |
| 86 | - cmd.nUsed = nCmdBase; | |
| 87 | - if( isDeleted ){ | |
| 88 | - printf("DELETED %s\n", zPathname); | |
| 89 | - }else if( access(zFullName, 0) ){ | |
| 90 | - printf("MISSING %s\n", zPathname); | |
| 91 | - }else if( isNew ){ | |
| 92 | - printf("ADDED %s\n", zPathname); | |
| 93 | - }else if( isDeleted ){ | |
| 94 | - printf("DELETED %s\n", zPathname); | |
| 95 | - }else if( isChnged==3 ){ | |
| 96 | - printf("ADDED_BY_MERGE %s\n", zPathname); | |
| 97 | - }else{ | |
| 98 | - printf("Index: %s\n=======================================" | |
| 99 | - "============================\n", | |
| 100 | - zPathname | |
| 101 | - ); | |
| 102 | - shell_escape(&cmd, zFullName); | |
| 103 | - printf("%s\n", blob_str(&cmd)); | |
| 104 | - fflush(stdout); | |
| 105 | - portable_system(blob_str(&cmd)); | |
| 106 | - } | |
| 107 | - free(zFullName); | |
| 108 | - } | |
| 109 | - db_finalize(&q); | |
| 110 | -} | |
| 111 | - | |
| 112 | -/* | |
| 113 | -** COMMAND: diff | |
| 114 | -** COMMAND: gdiff | |
| 115 | -** | |
| 116 | -** Usage: %fossil diff|gdiff ?-i? ?-r REVISION? FILE... | |
| 117 | -** | |
| 118 | -** Show the difference between the current version of a file (as it | |
| 119 | -** exists on disk) and that same file as it was checked out. | |
| 120 | -** | |
| 121 | -** diff will show a textual diff while gdiff will attempt to run a | |
| 122 | -** graphical diff command that you have setup. If the choosen command | |
| 123 | -** is not yet configured, the internal textual diff command will be | |
| 124 | -** used. | |
| 125 | -** | |
| 126 | -** If -i is supplied for either diff or gdiff, the internal textual | |
| 127 | -** diff command will be executed. | |
| 128 | -** | |
| 129 | -** Here are a few external diff command settings, for example: | |
| 130 | -** | |
| 131 | -** %fossil setting diff-command diff | |
| 132 | -** | |
| 133 | -** %fossil setting gdiff-command tkdiff | |
| 134 | -** %fossil setting gdiff-command eskill22 | |
| 135 | -** %fossil setting gdiff-command tortoisemerge | |
| 136 | -** %fossil setting gdiff-command meld | |
| 137 | -** %fossil setting gdiff-command xxdiff | |
| 138 | -** %fossil setting gdiff-command kdiff3 | |
| 139 | -*/ | |
| 140 | -void diff_cmd(void){ | |
| 141 | - int isGDiff; /* True for gdiff. False for normal diff */ | |
| 142 | - const char *zFile; /* Name of file to diff */ | |
| 143 | - const char *zRevision; /* Version of file to diff against current */ | |
| 144 | - Blob cmd; /* The diff command-line for external diff */ | |
| 145 | - Blob fname; /* */ | |
| 146 | - Blob vname; | |
| 147 | - Blob record; | |
| 148 | - int cnt=0; | |
| 149 | - int internalDiff; /* True to use the internal diff engine */ | |
| 150 | - | |
| 151 | - isGDiff = g.argv[1][0]=='g'; | |
| 152 | - internalDiff = find_option("internal","i",0)!=0; | |
| 153 | - zRevision = find_option("revision", "r", 1); | |
| 154 | - verify_all_options(); | |
| 155 | - db_must_be_within_tree(); | |
| 156 | - | |
| 157 | - if( !isGDiff && g.argc==2 ){ | |
| 158 | - diff_all(internalDiff, zRevision); | |
| 159 | - return; | |
| 160 | - } | |
| 161 | - if( g.argc<3 ){ | |
| 162 | - usage("?OPTIONS? FILE"); | |
| 163 | - } | |
| 164 | - | |
| 165 | - if( internalDiff==0 ){ | |
| 166 | - const char *zExternalCommand; | |
| 167 | - if( !isGDiff ){ | |
| 168 | - zExternalCommand = db_get("diff-command", 0); | |
| 169 | - }else{ | |
| 170 | - zExternalCommand = db_get("gdiff-command", 0); | |
| 171 | - } | |
| 172 | - if( zExternalCommand==0 ){ | |
| 173 | - internalDiff=1; | |
| 174 | - }else{ | |
| 175 | - blob_zero(&cmd); | |
| 176 | - blob_appendf(&cmd,"%s ",zExternalCommand); | |
| 177 | - } | |
| 178 | - } | |
| 179 | - zFile = g.argv[g.argc-1]; | |
| 180 | - file_tree_name(zFile, &fname, 1); | |
| 181 | - | |
| 182 | - blob_zero(&vname); | |
| 183 | - do{ | |
| 184 | - blob_reset(&vname); | |
| 185 | - blob_appendf(&vname, "%s~%d", zFile, cnt++); | |
| 186 | - }while( access(blob_str(&vname),0)==0 ); | |
| 187 | - | |
| 188 | - if( zRevision==0 ){ | |
| 189 | - int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); | |
| 190 | - if( rid==0 ){ | |
| 191 | - fossil_fatal("no history for file: %b", &fname); | |
| 192 | - } | |
| 193 | - content_get(rid, &record); | |
| 194 | - }else{ | |
| 195 | - historical_version_of_file(zRevision, blob_str(&fname), &record); | |
| 196 | - } | |
| 197 | - if( internalDiff ){ | |
| 198 | - Blob out; | |
| 199 | - Blob current; | |
| 200 | - blob_zero(¤t); | |
| 201 | - blob_read_from_file(¤t, zFile); | |
| 202 | - blob_zero(&out); | |
| 203 | - text_diff(&record, ¤t, &out, 5); | |
| 204 | - printf("--- %s\n+++ %s\n", blob_str(&fname), blob_str(&fname)); | |
| 205 | - printf("%s\n", blob_str(&out)); | |
| 206 | - blob_reset(¤t); | |
| 207 | - blob_reset(&out); | |
| 208 | - }else{ | |
| 209 | - blob_write_to_file(&record, blob_str(&vname)); | |
| 210 | - blob_reset(&record); | |
| 211 | - blob_appendf(&cmd, "%s ", blob_str(&vname)); | |
| 212 | - shell_escape(&cmd, zFile); | |
| 213 | - portable_system(blob_str(&cmd)); | |
| 214 | - unlink(blob_str(&vname)); | |
| 215 | - blob_reset(&vname); | |
| 216 | - blob_reset(&cmd); | |
| 217 | - } | |
| 218 | - blob_reset(&fname); | |
| 219 | -} | |
| 220 | - | |
| 221 | 51 | /* |
| 222 | 52 | ** This function implements a cross-platform "system()" interface. |
| 223 | 53 | */ |
| 224 | 54 | int portable_system(char *zOrigCmd){ |
| 225 | 55 | int rc; |
| @@ -235,5 +65,234 @@ | ||
| 235 | 65 | */ |
| 236 | 66 | rc = system(zOrigCmd); |
| 237 | 67 | #endif |
| 238 | 68 | return rc; |
| 239 | 69 | } |
| 70 | + | |
| 71 | +/* | |
| 72 | +** Show the difference between two files, one in memory and one on disk. | |
| 73 | +** | |
| 74 | +** The difference is the set of edits needed to transform pFile1 into | |
| 75 | +** zFile2. The content of pFile1 is in memory. zFile2 exists on disk. | |
| 76 | +** | |
| 77 | +** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the | |
| 78 | +** command zDiffCmd to do the diffing. | |
| 79 | +*/ | |
| 80 | +static void diff_file( | |
| 81 | + Blob *pFile1, /* In memory content to compare from */ | |
| 82 | + const char *zFile2, /* On disk content to compare to */ | |
| 83 | + const char *zName, /* Display name of the file */ | |
| 84 | + const char *zDiffCmd /* Command for comparison */ | |
| 85 | +){ | |
| 86 | + if( zDiffCmd==0 ){ | |
| 87 | + Blob out; /* Diff output text */ | |
| 88 | + Blob file2; /* Content of zFile2 */ | |
| 89 | + | |
| 90 | + /* Read content of zFile2 into memory */ | |
| 91 | + blob_zero(&file2); | |
| 92 | + blob_read_from_file(&file2, zFile2); | |
| 93 | + | |
| 94 | + /* Compute and output the differences */ | |
| 95 | + blob_zero(&out); | |
| 96 | + text_diff(pFile1, &file2, &out, 5); | |
| 97 | + printf("--- %s\n+++ %s\n", zName, zName); | |
| 98 | + printf("%s\n", blob_str(&out)); | |
| 99 | + | |
| 100 | + /* Release memory resources */ | |
| 101 | + blob_reset(&file2); | |
| 102 | + blob_reset(&out); | |
| 103 | + }else{ | |
| 104 | + int cnt = 0; | |
| 105 | + Blob nameFile1; /* Name of temporary file to old pFile1 content */ | |
| 106 | + Blob cmd; /* Text of command to run */ | |
| 107 | + | |
| 108 | + /* Construct a temporary file to hold pFile1 based on the name of | |
| 109 | + ** zFile2 */ | |
| 110 | + blob_zero(&nameFile1); | |
| 111 | + do{ | |
| 112 | + blob_reset(&nameFile1); | |
| 113 | + blob_appendf(&nameFile1, "%s~%d", zFile2, cnt++); | |
| 114 | + }while( access(blob_str(&nameFile1),0)==0 ); | |
| 115 | + blob_write_to_file(pFile1, blob_str(&nameFile1)); | |
| 116 | + | |
| 117 | + /* Construct the external diff command */ | |
| 118 | + blob_zero(&cmd); | |
| 119 | + blob_appendf(&cmd, "%s ", zDiffCmd); | |
| 120 | + shell_escape(&cmd, blob_str(&nameFile1)); | |
| 121 | + blob_append(&cmd, " ", 1); | |
| 122 | + shell_escape(&cmd, zFile2); | |
| 123 | + | |
| 124 | + /* Run the external diff command */ | |
| 125 | + portable_system(blob_str(&cmd)); | |
| 126 | + | |
| 127 | + /* Delete the temporary file and clean up memory used */ | |
| 128 | + unlink(blob_str(&nameFile1)); | |
| 129 | + blob_reset(&nameFile1); | |
| 130 | + blob_reset(&cmd); | |
| 131 | + } | |
| 132 | +} | |
| 133 | + | |
| 134 | +/* | |
| 135 | +** Do a diff against a single file named in g.argv[2] from version zFrom | |
| 136 | +** against the same file on disk. | |
| 137 | +*/ | |
| 138 | +static void diff_one_against_disk(const char *zFrom, const char *zDiffCmd){ | |
| 139 | + Blob fname; | |
| 140 | + Blob content; | |
| 141 | + file_tree_name(g.argv[2], &fname, 1); | |
| 142 | + historical_version_of_file(zFrom, blob_str(&fname), &content); | |
| 143 | + diff_file(&content, g.argv[2], g.argv[2], zDiffCmd); | |
| 144 | + blob_reset(&content); | |
| 145 | + blob_reset(&fname); | |
| 146 | +} | |
| 147 | + | |
| 148 | +/* | |
| 149 | +** Run a diff between the version zFrom and files on disk. zFrom might | |
| 150 | +** be NULL which means to simply show the difference between the edited | |
| 151 | +** files on disk and the check-out on which they are based. | |
| 152 | +*/ | |
| 153 | +static void diff_all_against_disk(const char *zFrom, const char *zDiffCmd){ | |
| 154 | + int vid; | |
| 155 | + Blob sql; | |
| 156 | + Stmt q; | |
| 157 | + | |
| 158 | + vid = db_lget_int("checkout", 0); | |
| 159 | + blob_zero(&sql); | |
| 160 | + db_begin_transaction(); | |
| 161 | + if( zFrom ){ | |
| 162 | + int rid = name_to_rid(zFrom); | |
| 163 | + if( !is_a_version(rid) ){ | |
| 164 | + fossil_fatal("no such check-in: %s", zFrom); | |
| 165 | + } | |
| 166 | + load_vfile_from_rid(rid); | |
| 167 | + blob_appendf(&sql, | |
| 168 | + "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid" | |
| 169 | + " FROM vfile v1, vfile v2 " | |
| 170 | + " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d" | |
| 171 | + " AND (v2.deleted OR v2.chnged OR v2.rid==0)" | |
| 172 | + "UNION " | |
| 173 | + "SELECT pathname, 1, 0, 0, 0" | |
| 174 | + " FROM vfile v1" | |
| 175 | + " WHERE v1.vid=%d" | |
| 176 | + " AND NOT EXISTS(SELECT 1 FROM vfile v2" | |
| 177 | + " WHERE v2.vid=%d AND v2.pathname=v1.pathname)" | |
| 178 | + "UNION " | |
| 179 | + "SELECT pathname, 0, 0, 1, 0" | |
| 180 | + " FROM vfile v2" | |
| 181 | + " WHERE v2.vid=%d" | |
| 182 | + " AND NOT EXISTS(SELECT 1 FROM vfile v1" | |
| 183 | + " WHERE v1.vid=%d AND v1.pathname=v2.pathname)" | |
| 184 | + " ORDER BY 1", | |
| 185 | + rid, vid, rid, vid, vid, rid | |
| 186 | + ); | |
| 187 | + }else{ | |
| 188 | + blob_appendf(&sql, | |
| 189 | + "SELECT pathname, deleted, chnged , rid==0, rid" | |
| 190 | + " FROM vfile" | |
| 191 | + " WHERE vid=%d" | |
| 192 | + " AND (deleted OR chnged OR rid==0)" | |
| 193 | + " ORDER BY pathname", | |
| 194 | + vid | |
| 195 | + ); | |
| 196 | + } | |
| 197 | + db_prepare(&q, blob_str(&sql)); | |
| 198 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 199 | + const char *zPathname = db_column_text(&q,0); | |
| 200 | + int isDeleted = db_column_int(&q, 1); | |
| 201 | + int isChnged = db_column_int(&q,2); | |
| 202 | + int isNew = db_column_int(&q,3); | |
| 203 | + char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); | |
| 204 | + if( isDeleted ){ | |
| 205 | + printf("DELETED %s\n", zPathname); | |
| 206 | + }else if( access(zFullName, 0) ){ | |
| 207 | + printf("MISSING %s\n", zPathname); | |
| 208 | + }else if( isNew ){ | |
| 209 | + printf("ADDED %s\n", zPathname); | |
| 210 | + }else if( isDeleted ){ | |
| 211 | + printf("DELETED %s\n", zPathname); | |
| 212 | + }else if( isChnged==3 ){ | |
| 213 | + printf("ADDED_BY_MERGE %s\n", zPathname); | |
| 214 | + }else{ | |
| 215 | + int srcid = db_column_int(&q, 4); | |
| 216 | + Blob content; | |
| 217 | + content_get(srcid, &content); | |
| 218 | + printf("Index: %s\n=======================================" | |
| 219 | + "============================\n", | |
| 220 | + zPathname | |
| 221 | + ); | |
| 222 | + diff_file(&content, zFullName, zPathname, zDiffCmd); | |
| 223 | + blob_reset(&content); | |
| 224 | + } | |
| 225 | + free(zFullName); | |
| 226 | + } | |
| 227 | + db_finalize(&q); | |
| 228 | + db_end_transaction(1); | |
| 229 | +} | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | +/* | |
| 234 | +** COMMAND: diff | |
| 235 | +** COMMAND: gdiff | |
| 236 | +** | |
| 237 | +** Usage: %fossil diff|gdiff ?options? ?FILE? | |
| 238 | +** | |
| 239 | +** Show the difference between the current version of FILE (as it | |
| 240 | +** exists on disk) and that same file as it was checked out. Or | |
| 241 | +** if the FILE argument is omitted, show the unsaved changed currently | |
| 242 | +** in the working check-out. | |
| 243 | +** | |
| 244 | +** If the "--from VERSION" or "-r VERSION" option is used it specifies | |
| 245 | +** the source check-in for the diff operation. If not specified, the | |
| 246 | +** source check-in is the base check-in for the current check-out. | |
| 247 | +** | |
| 248 | +** If the "--to VERSION" option appears, it specifies the check-in from | |
| 249 | +** which the second version of the file or files is taken. If there is | |
| 250 | +** no "--to" option then the (possibly edited) files in the current check-out | |
| 251 | +** are used. | |
| 252 | +** | |
| 253 | +** The "-i" command-line option forces the use of the internal diff logic | |
| 254 | +** rather than any external diff program that might be configured using | |
| 255 | +** the "setting" command. If no external diff program is configured, then | |
| 256 | +** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". | |
| 257 | +*/ | |
| 258 | +void diff_cmd(void){ | |
| 259 | + int isGDiff; /* True for gdiff. False for normal diff */ | |
| 260 | + int isInternDiff; /* True for internal diff */ | |
| 261 | + const char *zFrom; /* Source version number */ | |
| 262 | + const char *zTo; /* Target version number */ | |
| 263 | + const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ | |
| 264 | + | |
| 265 | + isGDiff = g.argv[1][0]=='g'; | |
| 266 | + isInternDiff = find_option("internal","i",0)!=0; | |
| 267 | + zFrom = find_option("from", "r", 1); | |
| 268 | + zTo = find_option("to", 0, 1); | |
| 269 | + | |
| 270 | + if( zTo==0 ){ | |
| 271 | + db_must_be_within_tree(); | |
| 272 | + if( !isInternDiff ){ | |
| 273 | + zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); | |
| 274 | + } | |
| 275 | + verify_all_options(); | |
| 276 | + if( g.argc==3 ){ | |
| 277 | + diff_one_against_disk(zFrom, zDiffCmd); | |
| 278 | + }else{ | |
| 279 | + diff_all_against_disk(zFrom, zDiffCmd); | |
| 280 | + } | |
| 281 | + }else if( zFrom==0 ){ | |
| 282 | + fossil_fatal("must use --from if --to is present"); | |
| 283 | + }else{ | |
| 284 | + db_find_and_open_repository(1); | |
| 285 | + if( !isInternDiff ){ | |
| 286 | + zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); | |
| 287 | + } | |
| 288 | + verify_all_options(); | |
| 289 | + fossil_fatal("--to not yet implemented"); | |
| 290 | +#if 0 | |
| 291 | + if( g.argc==3 ){ | |
| 292 | + diff_one_two_versions(zFrom, zTo, zDiffCmd); | |
| 293 | + }else{ | |
| 294 | + diff_all_two_versions(zFrom, zTo, zDiffCmd); | |
| 295 | + } | |
| 296 | +#endif | |
| 297 | + } | |
| 298 | +} | |
| 240 | 299 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -46,180 +46,10 @@ | |
| 46 | } |
| 47 | } |
| 48 | blob_append(pBlob, zIn, -1); |
| 49 | } |
| 50 | |
| 51 | /* |
| 52 | ** Run the fossil diff command separately for every file in the current |
| 53 | ** checkout that has changed. |
| 54 | */ |
| 55 | static void diff_all(int internalDiff, const char *zRevision){ |
| 56 | Stmt q; |
| 57 | Blob cmd; |
| 58 | int nCmdBase; |
| 59 | int vid; |
| 60 | |
| 61 | vid = db_lget_int("checkout", 0); |
| 62 | vfile_check_signature(vid); |
| 63 | blob_zero(&cmd); |
| 64 | shell_escape(&cmd, g.argv[0]); |
| 65 | blob_append(&cmd, " diff ", -1); |
| 66 | if( internalDiff ){ |
| 67 | blob_append(&cmd, "-i ", -1); |
| 68 | } |
| 69 | if( zRevision ){ |
| 70 | blob_append(&cmd, "-r ", -1); |
| 71 | shell_escape(&cmd, zRevision); |
| 72 | blob_append(&cmd, " ", 1); |
| 73 | } |
| 74 | nCmdBase = blob_size(&cmd); |
| 75 | db_prepare(&q, |
| 76 | "SELECT pathname, deleted, chnged, rid FROM vfile " |
| 77 | "WHERE chnged OR deleted OR rid=0 ORDER BY 1" |
| 78 | ); |
| 79 | |
| 80 | while( db_step(&q)==SQLITE_ROW ){ |
| 81 | const char *zPathname = db_column_text(&q,0); |
| 82 | int isDeleted = db_column_int(&q, 1); |
| 83 | int isChnged = db_column_int(&q,2); |
| 84 | int isNew = db_column_int(&q,3)==0; |
| 85 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 86 | cmd.nUsed = nCmdBase; |
| 87 | if( isDeleted ){ |
| 88 | printf("DELETED %s\n", zPathname); |
| 89 | }else if( access(zFullName, 0) ){ |
| 90 | printf("MISSING %s\n", zPathname); |
| 91 | }else if( isNew ){ |
| 92 | printf("ADDED %s\n", zPathname); |
| 93 | }else if( isDeleted ){ |
| 94 | printf("DELETED %s\n", zPathname); |
| 95 | }else if( isChnged==3 ){ |
| 96 | printf("ADDED_BY_MERGE %s\n", zPathname); |
| 97 | }else{ |
| 98 | printf("Index: %s\n=======================================" |
| 99 | "============================\n", |
| 100 | zPathname |
| 101 | ); |
| 102 | shell_escape(&cmd, zFullName); |
| 103 | printf("%s\n", blob_str(&cmd)); |
| 104 | fflush(stdout); |
| 105 | portable_system(blob_str(&cmd)); |
| 106 | } |
| 107 | free(zFullName); |
| 108 | } |
| 109 | db_finalize(&q); |
| 110 | } |
| 111 | |
| 112 | /* |
| 113 | ** COMMAND: diff |
| 114 | ** COMMAND: gdiff |
| 115 | ** |
| 116 | ** Usage: %fossil diff|gdiff ?-i? ?-r REVISION? FILE... |
| 117 | ** |
| 118 | ** Show the difference between the current version of a file (as it |
| 119 | ** exists on disk) and that same file as it was checked out. |
| 120 | ** |
| 121 | ** diff will show a textual diff while gdiff will attempt to run a |
| 122 | ** graphical diff command that you have setup. If the choosen command |
| 123 | ** is not yet configured, the internal textual diff command will be |
| 124 | ** used. |
| 125 | ** |
| 126 | ** If -i is supplied for either diff or gdiff, the internal textual |
| 127 | ** diff command will be executed. |
| 128 | ** |
| 129 | ** Here are a few external diff command settings, for example: |
| 130 | ** |
| 131 | ** %fossil setting diff-command diff |
| 132 | ** |
| 133 | ** %fossil setting gdiff-command tkdiff |
| 134 | ** %fossil setting gdiff-command eskill22 |
| 135 | ** %fossil setting gdiff-command tortoisemerge |
| 136 | ** %fossil setting gdiff-command meld |
| 137 | ** %fossil setting gdiff-command xxdiff |
| 138 | ** %fossil setting gdiff-command kdiff3 |
| 139 | */ |
| 140 | void diff_cmd(void){ |
| 141 | int isGDiff; /* True for gdiff. False for normal diff */ |
| 142 | const char *zFile; /* Name of file to diff */ |
| 143 | const char *zRevision; /* Version of file to diff against current */ |
| 144 | Blob cmd; /* The diff command-line for external diff */ |
| 145 | Blob fname; /* */ |
| 146 | Blob vname; |
| 147 | Blob record; |
| 148 | int cnt=0; |
| 149 | int internalDiff; /* True to use the internal diff engine */ |
| 150 | |
| 151 | isGDiff = g.argv[1][0]=='g'; |
| 152 | internalDiff = find_option("internal","i",0)!=0; |
| 153 | zRevision = find_option("revision", "r", 1); |
| 154 | verify_all_options(); |
| 155 | db_must_be_within_tree(); |
| 156 | |
| 157 | if( !isGDiff && g.argc==2 ){ |
| 158 | diff_all(internalDiff, zRevision); |
| 159 | return; |
| 160 | } |
| 161 | if( g.argc<3 ){ |
| 162 | usage("?OPTIONS? FILE"); |
| 163 | } |
| 164 | |
| 165 | if( internalDiff==0 ){ |
| 166 | const char *zExternalCommand; |
| 167 | if( !isGDiff ){ |
| 168 | zExternalCommand = db_get("diff-command", 0); |
| 169 | }else{ |
| 170 | zExternalCommand = db_get("gdiff-command", 0); |
| 171 | } |
| 172 | if( zExternalCommand==0 ){ |
| 173 | internalDiff=1; |
| 174 | }else{ |
| 175 | blob_zero(&cmd); |
| 176 | blob_appendf(&cmd,"%s ",zExternalCommand); |
| 177 | } |
| 178 | } |
| 179 | zFile = g.argv[g.argc-1]; |
| 180 | file_tree_name(zFile, &fname, 1); |
| 181 | |
| 182 | blob_zero(&vname); |
| 183 | do{ |
| 184 | blob_reset(&vname); |
| 185 | blob_appendf(&vname, "%s~%d", zFile, cnt++); |
| 186 | }while( access(blob_str(&vname),0)==0 ); |
| 187 | |
| 188 | if( zRevision==0 ){ |
| 189 | int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); |
| 190 | if( rid==0 ){ |
| 191 | fossil_fatal("no history for file: %b", &fname); |
| 192 | } |
| 193 | content_get(rid, &record); |
| 194 | }else{ |
| 195 | historical_version_of_file(zRevision, blob_str(&fname), &record); |
| 196 | } |
| 197 | if( internalDiff ){ |
| 198 | Blob out; |
| 199 | Blob current; |
| 200 | blob_zero(¤t); |
| 201 | blob_read_from_file(¤t, zFile); |
| 202 | blob_zero(&out); |
| 203 | text_diff(&record, ¤t, &out, 5); |
| 204 | printf("--- %s\n+++ %s\n", blob_str(&fname), blob_str(&fname)); |
| 205 | printf("%s\n", blob_str(&out)); |
| 206 | blob_reset(¤t); |
| 207 | blob_reset(&out); |
| 208 | }else{ |
| 209 | blob_write_to_file(&record, blob_str(&vname)); |
| 210 | blob_reset(&record); |
| 211 | blob_appendf(&cmd, "%s ", blob_str(&vname)); |
| 212 | shell_escape(&cmd, zFile); |
| 213 | portable_system(blob_str(&cmd)); |
| 214 | unlink(blob_str(&vname)); |
| 215 | blob_reset(&vname); |
| 216 | blob_reset(&cmd); |
| 217 | } |
| 218 | blob_reset(&fname); |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | ** This function implements a cross-platform "system()" interface. |
| 223 | */ |
| 224 | int portable_system(char *zOrigCmd){ |
| 225 | int rc; |
| @@ -235,5 +65,234 @@ | |
| 235 | */ |
| 236 | rc = system(zOrigCmd); |
| 237 | #endif |
| 238 | return rc; |
| 239 | } |
| 240 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -46,180 +46,10 @@ | |
| 46 | } |
| 47 | } |
| 48 | blob_append(pBlob, zIn, -1); |
| 49 | } |
| 50 | |
| 51 | /* |
| 52 | ** This function implements a cross-platform "system()" interface. |
| 53 | */ |
| 54 | int portable_system(char *zOrigCmd){ |
| 55 | int rc; |
| @@ -235,5 +65,234 @@ | |
| 65 | */ |
| 66 | rc = system(zOrigCmd); |
| 67 | #endif |
| 68 | return rc; |
| 69 | } |
| 70 | |
| 71 | /* |
| 72 | ** Show the difference between two files, one in memory and one on disk. |
| 73 | ** |
| 74 | ** The difference is the set of edits needed to transform pFile1 into |
| 75 | ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk. |
| 76 | ** |
| 77 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 78 | ** command zDiffCmd to do the diffing. |
| 79 | */ |
| 80 | static void diff_file( |
| 81 | Blob *pFile1, /* In memory content to compare from */ |
| 82 | const char *zFile2, /* On disk content to compare to */ |
| 83 | const char *zName, /* Display name of the file */ |
| 84 | const char *zDiffCmd /* Command for comparison */ |
| 85 | ){ |
| 86 | if( zDiffCmd==0 ){ |
| 87 | Blob out; /* Diff output text */ |
| 88 | Blob file2; /* Content of zFile2 */ |
| 89 | |
| 90 | /* Read content of zFile2 into memory */ |
| 91 | blob_zero(&file2); |
| 92 | blob_read_from_file(&file2, zFile2); |
| 93 | |
| 94 | /* Compute and output the differences */ |
| 95 | blob_zero(&out); |
| 96 | text_diff(pFile1, &file2, &out, 5); |
| 97 | printf("--- %s\n+++ %s\n", zName, zName); |
| 98 | printf("%s\n", blob_str(&out)); |
| 99 | |
| 100 | /* Release memory resources */ |
| 101 | blob_reset(&file2); |
| 102 | blob_reset(&out); |
| 103 | }else{ |
| 104 | int cnt = 0; |
| 105 | Blob nameFile1; /* Name of temporary file to old pFile1 content */ |
| 106 | Blob cmd; /* Text of command to run */ |
| 107 | |
| 108 | /* Construct a temporary file to hold pFile1 based on the name of |
| 109 | ** zFile2 */ |
| 110 | blob_zero(&nameFile1); |
| 111 | do{ |
| 112 | blob_reset(&nameFile1); |
| 113 | blob_appendf(&nameFile1, "%s~%d", zFile2, cnt++); |
| 114 | }while( access(blob_str(&nameFile1),0)==0 ); |
| 115 | blob_write_to_file(pFile1, blob_str(&nameFile1)); |
| 116 | |
| 117 | /* Construct the external diff command */ |
| 118 | blob_zero(&cmd); |
| 119 | blob_appendf(&cmd, "%s ", zDiffCmd); |
| 120 | shell_escape(&cmd, blob_str(&nameFile1)); |
| 121 | blob_append(&cmd, " ", 1); |
| 122 | shell_escape(&cmd, zFile2); |
| 123 | |
| 124 | /* Run the external diff command */ |
| 125 | portable_system(blob_str(&cmd)); |
| 126 | |
| 127 | /* Delete the temporary file and clean up memory used */ |
| 128 | unlink(blob_str(&nameFile1)); |
| 129 | blob_reset(&nameFile1); |
| 130 | blob_reset(&cmd); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Do a diff against a single file named in g.argv[2] from version zFrom |
| 136 | ** against the same file on disk. |
| 137 | */ |
| 138 | static void diff_one_against_disk(const char *zFrom, const char *zDiffCmd){ |
| 139 | Blob fname; |
| 140 | Blob content; |
| 141 | file_tree_name(g.argv[2], &fname, 1); |
| 142 | historical_version_of_file(zFrom, blob_str(&fname), &content); |
| 143 | diff_file(&content, g.argv[2], g.argv[2], zDiffCmd); |
| 144 | blob_reset(&content); |
| 145 | blob_reset(&fname); |
| 146 | } |
| 147 | |
| 148 | /* |
| 149 | ** Run a diff between the version zFrom and files on disk. zFrom might |
| 150 | ** be NULL which means to simply show the difference between the edited |
| 151 | ** files on disk and the check-out on which they are based. |
| 152 | */ |
| 153 | static void diff_all_against_disk(const char *zFrom, const char *zDiffCmd){ |
| 154 | int vid; |
| 155 | Blob sql; |
| 156 | Stmt q; |
| 157 | |
| 158 | vid = db_lget_int("checkout", 0); |
| 159 | blob_zero(&sql); |
| 160 | db_begin_transaction(); |
| 161 | if( zFrom ){ |
| 162 | int rid = name_to_rid(zFrom); |
| 163 | if( !is_a_version(rid) ){ |
| 164 | fossil_fatal("no such check-in: %s", zFrom); |
| 165 | } |
| 166 | load_vfile_from_rid(rid); |
| 167 | blob_appendf(&sql, |
| 168 | "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid" |
| 169 | " FROM vfile v1, vfile v2 " |
| 170 | " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d" |
| 171 | " AND (v2.deleted OR v2.chnged OR v2.rid==0)" |
| 172 | "UNION " |
| 173 | "SELECT pathname, 1, 0, 0, 0" |
| 174 | " FROM vfile v1" |
| 175 | " WHERE v1.vid=%d" |
| 176 | " AND NOT EXISTS(SELECT 1 FROM vfile v2" |
| 177 | " WHERE v2.vid=%d AND v2.pathname=v1.pathname)" |
| 178 | "UNION " |
| 179 | "SELECT pathname, 0, 0, 1, 0" |
| 180 | " FROM vfile v2" |
| 181 | " WHERE v2.vid=%d" |
| 182 | " AND NOT EXISTS(SELECT 1 FROM vfile v1" |
| 183 | " WHERE v1.vid=%d AND v1.pathname=v2.pathname)" |
| 184 | " ORDER BY 1", |
| 185 | rid, vid, rid, vid, vid, rid |
| 186 | ); |
| 187 | }else{ |
| 188 | blob_appendf(&sql, |
| 189 | "SELECT pathname, deleted, chnged , rid==0, rid" |
| 190 | " FROM vfile" |
| 191 | " WHERE vid=%d" |
| 192 | " AND (deleted OR chnged OR rid==0)" |
| 193 | " ORDER BY pathname", |
| 194 | vid |
| 195 | ); |
| 196 | } |
| 197 | db_prepare(&q, blob_str(&sql)); |
| 198 | while( db_step(&q)==SQLITE_ROW ){ |
| 199 | const char *zPathname = db_column_text(&q,0); |
| 200 | int isDeleted = db_column_int(&q, 1); |
| 201 | int isChnged = db_column_int(&q,2); |
| 202 | int isNew = db_column_int(&q,3); |
| 203 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 204 | if( isDeleted ){ |
| 205 | printf("DELETED %s\n", zPathname); |
| 206 | }else if( access(zFullName, 0) ){ |
| 207 | printf("MISSING %s\n", zPathname); |
| 208 | }else if( isNew ){ |
| 209 | printf("ADDED %s\n", zPathname); |
| 210 | }else if( isDeleted ){ |
| 211 | printf("DELETED %s\n", zPathname); |
| 212 | }else if( isChnged==3 ){ |
| 213 | printf("ADDED_BY_MERGE %s\n", zPathname); |
| 214 | }else{ |
| 215 | int srcid = db_column_int(&q, 4); |
| 216 | Blob content; |
| 217 | content_get(srcid, &content); |
| 218 | printf("Index: %s\n=======================================" |
| 219 | "============================\n", |
| 220 | zPathname |
| 221 | ); |
| 222 | diff_file(&content, zFullName, zPathname, zDiffCmd); |
| 223 | blob_reset(&content); |
| 224 | } |
| 225 | free(zFullName); |
| 226 | } |
| 227 | db_finalize(&q); |
| 228 | db_end_transaction(1); |
| 229 | } |
| 230 | |
| 231 | |
| 232 | |
| 233 | /* |
| 234 | ** COMMAND: diff |
| 235 | ** COMMAND: gdiff |
| 236 | ** |
| 237 | ** Usage: %fossil diff|gdiff ?options? ?FILE? |
| 238 | ** |
| 239 | ** Show the difference between the current version of FILE (as it |
| 240 | ** exists on disk) and that same file as it was checked out. Or |
| 241 | ** if the FILE argument is omitted, show the unsaved changed currently |
| 242 | ** in the working check-out. |
| 243 | ** |
| 244 | ** If the "--from VERSION" or "-r VERSION" option is used it specifies |
| 245 | ** the source check-in for the diff operation. If not specified, the |
| 246 | ** source check-in is the base check-in for the current check-out. |
| 247 | ** |
| 248 | ** If the "--to VERSION" option appears, it specifies the check-in from |
| 249 | ** which the second version of the file or files is taken. If there is |
| 250 | ** no "--to" option then the (possibly edited) files in the current check-out |
| 251 | ** are used. |
| 252 | ** |
| 253 | ** The "-i" command-line option forces the use of the internal diff logic |
| 254 | ** rather than any external diff program that might be configured using |
| 255 | ** the "setting" command. If no external diff program is configured, then |
| 256 | ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". |
| 257 | */ |
| 258 | void diff_cmd(void){ |
| 259 | int isGDiff; /* True for gdiff. False for normal diff */ |
| 260 | int isInternDiff; /* True for internal diff */ |
| 261 | const char *zFrom; /* Source version number */ |
| 262 | const char *zTo; /* Target version number */ |
| 263 | const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ |
| 264 | |
| 265 | isGDiff = g.argv[1][0]=='g'; |
| 266 | isInternDiff = find_option("internal","i",0)!=0; |
| 267 | zFrom = find_option("from", "r", 1); |
| 268 | zTo = find_option("to", 0, 1); |
| 269 | |
| 270 | if( zTo==0 ){ |
| 271 | db_must_be_within_tree(); |
| 272 | if( !isInternDiff ){ |
| 273 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 274 | } |
| 275 | verify_all_options(); |
| 276 | if( g.argc==3 ){ |
| 277 | diff_one_against_disk(zFrom, zDiffCmd); |
| 278 | }else{ |
| 279 | diff_all_against_disk(zFrom, zDiffCmd); |
| 280 | } |
| 281 | }else if( zFrom==0 ){ |
| 282 | fossil_fatal("must use --from if --to is present"); |
| 283 | }else{ |
| 284 | db_find_and_open_repository(1); |
| 285 | if( !isInternDiff ){ |
| 286 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 287 | } |
| 288 | verify_all_options(); |
| 289 | fossil_fatal("--to not yet implemented"); |
| 290 | #if 0 |
| 291 | if( g.argc==3 ){ |
| 292 | diff_one_two_versions(zFrom, zTo, zDiffCmd); |
| 293 | }else{ |
| 294 | diff_all_two_versions(zFrom, zTo, zDiffCmd); |
| 295 | } |
| 296 | #endif |
| 297 | } |
| 298 | } |
| 299 |
+8
-1
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -245,11 +245,18 @@ | ||
| 245 | 245 | ){ |
| 246 | 246 | Blob mfile; |
| 247 | 247 | Manifest m; |
| 248 | 248 | int i, rid=0; |
| 249 | 249 | |
| 250 | - rid = name_to_rid(revision); | |
| 250 | + if( revision ){ | |
| 251 | + rid = name_to_rid(revision); | |
| 252 | + }else{ | |
| 253 | + rid = db_lget_int("checkout", 0); | |
| 254 | + } | |
| 255 | + if( !is_a_version(rid) ){ | |
| 256 | + fossil_fatal("no such check-out: %s", revision); | |
| 257 | + } | |
| 251 | 258 | content_get(rid, &mfile); |
| 252 | 259 | |
| 253 | 260 | if( manifest_parse(&m, &mfile) ){ |
| 254 | 261 | for(i=0; i<m.nFile; i++){ |
| 255 | 262 | if( strcmp(m.aFile[i].zName, file)==0 ){ |
| 256 | 263 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -245,11 +245,18 @@ | |
| 245 | ){ |
| 246 | Blob mfile; |
| 247 | Manifest m; |
| 248 | int i, rid=0; |
| 249 | |
| 250 | rid = name_to_rid(revision); |
| 251 | content_get(rid, &mfile); |
| 252 | |
| 253 | if( manifest_parse(&m, &mfile) ){ |
| 254 | for(i=0; i<m.nFile; i++){ |
| 255 | if( strcmp(m.aFile[i].zName, file)==0 ){ |
| 256 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -245,11 +245,18 @@ | |
| 245 | ){ |
| 246 | Blob mfile; |
| 247 | Manifest m; |
| 248 | int i, rid=0; |
| 249 | |
| 250 | if( revision ){ |
| 251 | rid = name_to_rid(revision); |
| 252 | }else{ |
| 253 | rid = db_lget_int("checkout", 0); |
| 254 | } |
| 255 | if( !is_a_version(rid) ){ |
| 256 | fossil_fatal("no such check-out: %s", revision); |
| 257 | } |
| 258 | content_get(rid, &mfile); |
| 259 | |
| 260 | if( manifest_parse(&m, &mfile) ){ |
| 261 | for(i=0; i<m.nFile; i++){ |
| 262 | if( strcmp(m.aFile[i].zName, file)==0 ){ |
| 263 |