Fossil SCM
Fold print_changes() into status_cmd() (formerly changes_cmd()). Remove C_SHA1SUM, C_HEADER, and C_VERBOSE flags which are no longer needed for communicating options to print_changes(). Move locate_unmanaged_files() closer to the top of the file so status_cmd() can call it. Implement C_UNMODIFIED and C_EXTRA in status_report(). Document the fact that the changes and status commands take optional path arguments. Let changes and status commands accept --case-sensitive (was already done), --dotfiles, and --ignore for the benefit of --extras. Incorporate search for extra files into status_cmd().
Commit
f15d20e39b55c7ac3e007a5df84293885c4404a1
Parent
2c3a108c72e7d59…
1 file changed
+146
-107
+146
-107
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -25,14 +25,13 @@ | ||
| 25 | 25 | /* |
| 26 | 26 | ** Change filter options. |
| 27 | 27 | */ |
| 28 | 28 | enum { |
| 29 | 29 | /* Zero-based bit indexes. */ |
| 30 | - CB_EDITED , CB_UPDATED , CB_CHANGED, CB_MISSING , CB_ADDED , CB_DELETED, | |
| 31 | - CB_RENAMED, CB_CONFLICT, CB_META , CB_UNMODIFIED, CB_EXTRA , CB_MERGE , | |
| 32 | - CB_RELPATH, CB_SHA1SUM , CB_HEADER , CB_VERBOSE , CB_CLASSIFY, CB_FATAL , | |
| 33 | - CB_COMMENT, | |
| 30 | + CB_EDITED , CB_UPDATED , CB_CHANGED, CB_MISSING , CB_ADDED, CB_DELETED, | |
| 31 | + CB_RENAMED, CB_CONFLICT, CB_META , CB_UNMODIFIED, CB_EXTRA, CB_MERGE , | |
| 32 | + CB_RELPATH, CB_CLASSIFY, CB_FATAL , CB_COMMENT , | |
| 34 | 33 | |
| 35 | 34 | /* Bitmask values. */ |
| 36 | 35 | C_EDITED = 1 << CB_EDITED, /* Edited, merged, and conflicted files. */ |
| 37 | 36 | C_UPDATED = 1 << CB_UPDATED, /* Files updated by merge/integrate. */ |
| 38 | 37 | C_CHANGED = 1 << CB_CHANGED, /* Treated the same as the above two. */ |
| @@ -48,18 +47,64 @@ | ||
| 48 | 47 | C_FILTER = C_EDITED | C_UPDATED | C_CHANGED | C_MISSING | C_ADDED |
| 49 | 48 | | C_DELETED | C_RENAMED | C_CONFLICT | C_META | C_UNMODIFIED |
| 50 | 49 | | C_EXTRA | C_MERGE, |
| 51 | 50 | C_ALL = C_FILTER & ~(C_EXTRA | C_MERGE), |
| 52 | 51 | C_RELPATH = 1 << CB_RELPATH, /* Show relative paths. */ |
| 53 | - C_SHA1SUM = 1 << CB_SHA1SUM, /* Use SHA1 checksums not mtimes. */ | |
| 54 | - C_HEADER = 1 << CB_HEADER, /* Display repository name if non-empty. */ | |
| 55 | - C_VERBOSE = 1 << CB_VERBOSE, /* Display "(none)" if empty. */ | |
| 56 | 52 | C_CLASSIFY = 1 << CB_CLASSIFY, /* Show file change types. */ |
| 57 | 53 | C_DEFAULT = (C_ALL & ~C_UNMODIFIED) | C_MERGE | C_CLASSIFY, |
| 58 | 54 | C_FATAL = (1 << CB_FATAL) | C_MISSING, /* Fail on MISSING/NOT_A_FILE. */ |
| 59 | 55 | C_COMMENT = 1 << CB_COMMENT, /* Precede each line with "# ". */ |
| 60 | 56 | }; |
| 57 | + | |
| 58 | +/* | |
| 59 | +** Create a TEMP table named SFILE and add all unmanaged files named on | |
| 60 | +** the command-line to that table. If directories are named, then add | |
| 61 | +** all unmanaged files contained underneath those directories. If there | |
| 62 | +** are no files or directories named on the command-line, then add all | |
| 63 | +** unmanaged files anywhere in the checkout. | |
| 64 | +*/ | |
| 65 | +static void locate_unmanaged_files( | |
| 66 | + int argc, /* Number of command-line arguments to examine */ | |
| 67 | + char **argv, /* values of command-line arguments */ | |
| 68 | + unsigned scanFlags, /* Zero or more SCAN_xxx flags */ | |
| 69 | + Glob *pIgnore1, /* Do not add files that match this GLOB */ | |
| 70 | + Glob *pIgnore2 /* Omit files matching this GLOB too */ | |
| 71 | +){ | |
| 72 | + Blob name; /* Name of a candidate file or directory */ | |
| 73 | + char *zName; /* Name of a candidate file or directory */ | |
| 74 | + int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ | |
| 75 | + int i; /* Loop counter */ | |
| 76 | + int nRoot; /* length of g.zLocalRoot */ | |
| 77 | + | |
| 78 | + db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", | |
| 79 | + filename_collation()); | |
| 80 | + nRoot = (int)strlen(g.zLocalRoot); | |
| 81 | + if( argc==0 ){ | |
| 82 | + blob_init(&name, g.zLocalRoot, nRoot - 1); | |
| 83 | + vfile_scan(&name, blob_size(&name), scanFlags, pIgnore1, pIgnore2); | |
| 84 | + blob_reset(&name); | |
| 85 | + }else{ | |
| 86 | + for(i=0; i<argc; i++){ | |
| 87 | + file_canonical_name(argv[i], &name, 0); | |
| 88 | + zName = blob_str(&name); | |
| 89 | + isDir = file_wd_isdir(zName); | |
| 90 | + if( isDir==1 ){ | |
| 91 | + vfile_scan(&name, nRoot-1, scanFlags, pIgnore1, pIgnore2); | |
| 92 | + }else if( isDir==0 ){ | |
| 93 | + fossil_warning("not found: %s", &zName[nRoot]); | |
| 94 | + }else if( file_access(zName, R_OK) ){ | |
| 95 | + fossil_fatal("cannot open %s", &zName[nRoot]); | |
| 96 | + }else{ | |
| 97 | + db_multi_exec( | |
| 98 | + "INSERT OR IGNORE INTO sfile(x) VALUES(%Q)", | |
| 99 | + &zName[nRoot] | |
| 100 | + ); | |
| 101 | + } | |
| 102 | + blob_reset(&name); | |
| 103 | + } | |
| 104 | + } | |
| 105 | +} | |
| 61 | 106 | |
| 62 | 107 | /* |
| 63 | 108 | ** Generate text describing all changes. |
| 64 | 109 | ** |
| 65 | 110 | ** We assume that vfile_check_signature has been run. |
| @@ -69,14 +114,15 @@ | ||
| 69 | 114 | unsigned flags /* Filter and other configuration flags */ |
| 70 | 115 | ){ |
| 71 | 116 | Stmt q; |
| 72 | 117 | int nErr = 0; |
| 73 | 118 | Blob rewrittenPathname; |
| 74 | - Blob where; | |
| 119 | + Blob sql = BLOB_INITIALIZER, where = BLOB_INITIALIZER; | |
| 75 | 120 | const char *zName; |
| 76 | 121 | int i; |
| 77 | 122 | |
| 123 | + /* Assemble the path-limiting WHERE clause, if any. */ | |
| 78 | 124 | blob_zero(&where); |
| 79 | 125 | for(i=2; i<g.argc; i++){ |
| 80 | 126 | Blob fname; |
| 81 | 127 | file_tree_name(g.argv[i], &fname, 0, 1); |
| 82 | 128 | zName = blob_str(&fname); |
| @@ -91,28 +137,47 @@ | ||
| 91 | 137 | filename_collation(), zName, filename_collation(), |
| 92 | 138 | zName, filename_collation() |
| 93 | 139 | ); |
| 94 | 140 | } |
| 95 | 141 | |
| 96 | - db_prepare(&q, | |
| 97 | - "SELECT pathname, deleted, chnged," | |
| 98 | - " rid, coalesce(origname!=pathname,0), islink" | |
| 99 | - " FROM vfile " | |
| 100 | - " WHERE is_selected(id) %s" | |
| 101 | - " AND (chnged OR deleted OR rid=0 OR pathname!=origname)" | |
| 102 | - " ORDER BY 1 /*scan*/", | |
| 103 | - blob_sql_text(&where) | |
| 104 | - ); | |
| 142 | + /* Start building the SELECT statement. */ | |
| 143 | + blob_zero(&sql); | |
| 144 | + blob_append_sql(&sql, | |
| 145 | + "SELECT pathname, deleted, chnged, rid," | |
| 146 | + " coalesce(origname!=pathname,0) AS renamed, islink, 1 AS managed" | |
| 147 | + " FROM vfile" | |
| 148 | + " WHERE is_selected(id)%s", blob_sql_text(&where)); | |
| 149 | + | |
| 150 | + /* Exclude unmodified files unless requested. */ | |
| 151 | + if( !(flags & C_UNMODIFIED) ){ | |
| 152 | + blob_append_sql(&sql, | |
| 153 | + " AND (chnged OR deleted OR rid=0 OR pathname!=origname)"); | |
| 154 | + } | |
| 155 | + | |
| 156 | + /* If C_EXTRA, add unmanaged files to the query result too. */ | |
| 157 | + if( flags & C_EXTRA ){ | |
| 158 | + blob_append_sql(&sql, " UNION ALL SELECT x AS pathname, 0, 0, 0, 0, 0, 0" | |
| 159 | + " FROM sfile WHERE 1%s", blob_sql_text(&where)); | |
| 160 | + } | |
| 161 | + | |
| 162 | + /* Append an ORDER BY clause then compile the query. */ | |
| 163 | + blob_append_sql(&sql, " ORDER BY pathname"); | |
| 164 | + db_prepare(&q, "%s", blob_sql_text(&sql)); | |
| 165 | + blob_reset(&sql); | |
| 166 | + blob_reset(&where); | |
| 167 | + | |
| 168 | + /* Execute the query and assemble the report. */ | |
| 105 | 169 | blob_zero(&rewrittenPathname); |
| 106 | 170 | while( db_step(&q)==SQLITE_ROW ){ |
| 107 | - const char *zPathname = db_column_text(&q,0); | |
| 171 | + const char *zPathname = db_column_text(&q, 0); | |
| 108 | 172 | const char *zClass = 0; |
| 173 | + int isManaged = db_column_int(&q, 6); | |
| 109 | 174 | int isDeleted = db_column_int(&q, 1); |
| 110 | - int isChnged = db_column_int(&q,2); | |
| 111 | - int isNew = db_column_int(&q,3)==0; | |
| 112 | - int isRenamed = db_column_int(&q,4); | |
| 113 | - int isLink = db_column_int(&q,5); | |
| 175 | + int isChnged = db_column_int(&q, 2); | |
| 176 | + int isNew = isManaged && !db_column_int(&q, 3); | |
| 177 | + int isRenamed = db_column_int(&q, 4); | |
| 178 | + int isLink = db_column_int(&q, 5); | |
| 114 | 179 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 115 | 180 | int isMissing = !file_wd_isfile_or_link(zFullName); |
| 116 | 181 | |
| 117 | 182 | /* Determine the file change classification, if any. */ |
| 118 | 183 | if( (flags & C_DELETED) && isDeleted ){ |
| @@ -150,20 +215,21 @@ | ||
| 150 | 215 | }else if( (flags & C_META) && isChnged==9 ){ |
| 151 | 216 | zClass = "UNLINK"; |
| 152 | 217 | }else if( (flags & C_CONFLICT) && isChnged && !isLink |
| 153 | 218 | && file_contains_merge_marker(zFullName) ){ |
| 154 | 219 | zClass = "CONFLICT"; |
| 155 | - }else if( (flags & (C_EDITED | C_CHANGED)) && (isChnged<2 || isChnged>9) ){ | |
| 220 | + }else if( (flags & (C_EDITED | C_CHANGED)) && isChnged | |
| 221 | + && (isChnged<2 || isChnged>9) ){ | |
| 156 | 222 | zClass = "EDITED"; |
| 157 | 223 | }else if( (flags & C_RENAMED) && isRenamed ){ |
| 158 | 224 | zClass = "RENAMED"; |
| 159 | - }else if( (flags & C_UNMODIFIED) && !isDeleted && !isMissing && !isNew | |
| 160 | - && !isChnged && !isRenamed ){ | |
| 161 | - /* TODO: never gets executed because query only yields modified files. */ | |
| 225 | + }else if( (flags & C_UNMODIFIED) && isManaged && !isDeleted && !isMissing | |
| 226 | + && !isNew && !isChnged && !isRenamed ){ | |
| 162 | 227 | zClass = "UNMODIFIED"; |
| 228 | + }else if( (flags & C_EXTRA) && !isManaged ){ | |
| 229 | + zClass = "EXTRA"; | |
| 163 | 230 | } |
| 164 | - /* TODO: implement C_EXTRA. */ | |
| 165 | 231 | /* TODO: reimplement ls and extras in terms of this function. */ |
| 166 | 232 | |
| 167 | 233 | /* Only report files for which a change classification was determined. */ |
| 168 | 234 | if( zClass ){ |
| 169 | 235 | /* If C_COMMENT, precede each line with "# ". */ |
| @@ -243,38 +309,19 @@ | ||
| 243 | 309 | if( absPathOption ){ relativePaths = 0; } |
| 244 | 310 | if( relPathOption ){ relativePaths = 1; } |
| 245 | 311 | return relativePaths; |
| 246 | 312 | } |
| 247 | 313 | |
| 248 | -void print_changes( | |
| 249 | - unsigned flags /* Configuration flags */ | |
| 250 | -){ | |
| 251 | - Blob report; | |
| 252 | - int vid; | |
| 253 | - blob_zero(&report); | |
| 254 | - | |
| 255 | - vid = db_lget_int("checkout", 0); | |
| 256 | - vfile_check_signature(vid, flags & C_SHA1SUM ? CKSIG_SHA1 : 0); | |
| 257 | - status_report(&report, flags); | |
| 258 | - if( (flags & C_VERBOSE) && blob_size(&report)==0 ){ | |
| 259 | - blob_append(&report, " (none)\n", -1); | |
| 260 | - } | |
| 261 | - if( (flags & C_HEADER) && blob_size(&report)>0 ){ | |
| 262 | - fossil_print("Changes for %s at %s:\n", db_get("project-name","???"), | |
| 263 | - g.zLocalRoot); | |
| 264 | - } | |
| 265 | - blob_write_to_file(&report, "-"); | |
| 266 | - blob_reset(&report); | |
| 267 | -} | |
| 268 | - | |
| 269 | 314 | /* |
| 270 | 315 | ** COMMAND: changes |
| 271 | 316 | ** COMMAND: status |
| 272 | 317 | ** |
| 273 | -** Usage: %fossil changes|status ?OPTIONS? | |
| 318 | +** Usage: %fossil changes|status ?OPTIONS? ?PATHS ...? | |
| 274 | 319 | ** |
| 275 | -** Report the change status of files in the current checkout. | |
| 320 | +** Report the change status of files in the current checkout. If one or | |
| 321 | +** more PATHS are specified, only changes among the named files and | |
| 322 | +** directories are reported. Directories are searched recursively. | |
| 276 | 323 | ** |
| 277 | 324 | ** The status command is similar to the changes command, except it lacks |
| 278 | 325 | ** several of the options supported by changes and it has its own header |
| 279 | 326 | ** and footer information. The header information is a subset of that |
| 280 | 327 | ** shown by the info command, and the footer shows if there are any forks. |
| @@ -318,10 +365,13 @@ | ||
| 318 | 365 | ** --abs-paths Display absolute pathnames. |
| 319 | 366 | ** --rel-paths Display pathnames relative to the current working |
| 320 | 367 | ** directory. |
| 321 | 368 | ** --sha1sum Verify file status using SHA1 hashing rather than |
| 322 | 369 | ** relying on file mtimes. |
| 370 | +** --case-sensitive <BOOL> Override case-sensitive setting. | |
| 371 | +** --dotfiles Include unmanaged files beginning with a dot. | |
| 372 | +** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns. | |
| 323 | 373 | ** |
| 324 | 374 | ** Options specific to the changes command: |
| 325 | 375 | ** --header Identify the repository if report is non-empty. |
| 326 | 376 | ** -v|--verbose Say "(none)" if the change report is empty. |
| 327 | 377 | ** --classify Start each line with the file's change type. |
| @@ -356,28 +406,41 @@ | ||
| 356 | 406 | {"changed" , C_CHANGED, 0}, {"missing" , C_MISSING , 0}, |
| 357 | 407 | {"added" , C_ADDED , 0}, {"deleted" , C_DELETED , 0}, |
| 358 | 408 | {"renamed" , C_RENAMED, 0}, {"conflict" , C_CONFLICT , 0}, |
| 359 | 409 | {"meta" , C_META , 0}, {"unmodified" , C_UNMODIFIED, 0}, |
| 360 | 410 | {"all" , C_ALL , 0}, {"extra" , C_EXTRA , 0}, |
| 361 | - {"merge" , C_MERGE , 0}, {"sha1sum" , C_SHA1SUM , 0}, | |
| 362 | - {"header" , C_HEADER , 1}, {"v" , C_VERBOSE , 1}, | |
| 363 | - {"verbose" , C_VERBOSE, 1}, {"classify" , C_CLASSIFY , 1}, | |
| 411 | + {"merge" , C_MERGE , 0}, {"classify" , C_CLASSIFY , 1}, | |
| 364 | 412 | }, noFlagDefs[] = { |
| 365 | 413 | {"no-merge", C_MERGE , 0}, {"no-classify", C_CLASSIFY , 1}, |
| 366 | 414 | }; |
| 367 | 415 | |
| 368 | 416 | #ifdef FOSSIL_DEBUG |
| 369 | 417 | static const char *const bits[] = { |
| 370 | 418 | "EDITED", "UPDATED", "CHANGED", "MISSING", "ADDED", "DELETED", "RENAMED", |
| 371 | - "CONFLICT", "META", "UNMODIFIED", "EXTRA", "MERGE", "RELPATH", "SHA1SUM", | |
| 372 | - "HEADER", "VERBOSE", "CLASSIFY", | |
| 419 | + "CONFLICT", "META", "UNMODIFIED", "EXTRA", "MERGE", "RELPATH", "CLASSIFY", | |
| 373 | 420 | }; |
| 374 | 421 | #endif |
| 375 | 422 | |
| 423 | + Blob report = BLOB_INITIALIZER; | |
| 424 | + int useSha1sum = find_option("sha1sum", 0, 0)!=0; | |
| 425 | + int showHdr = find_option("header",0,0)!=0; | |
| 426 | + int verboseFlag = find_option("verbose","v",0)!=0; | |
| 427 | + const char *zIgnoreFlag = find_option("ignore", 0, 1); | |
| 428 | + unsigned scanFlags = 0; | |
| 376 | 429 | int changes = g.argv[1][0]=='c'; |
| 377 | 430 | unsigned flags = 0; |
| 378 | 431 | int vid, i; |
| 432 | + | |
| 433 | + /* If --ignore is not specified, use the ignore-glob setting. */ | |
| 434 | + if( !zIgnoreFlag ){ | |
| 435 | + zIgnoreFlag = db_get("ignore-glob", 0); | |
| 436 | + } | |
| 437 | + | |
| 438 | + /* Get the --dotfiles argument, or read it from the dotfiles setting. */ | |
| 439 | + if( find_option("dotfiles", 0, 0) || db_get_boolean("dotfiles", 0) ){ | |
| 440 | + scanFlags = SCAN_ALL; | |
| 441 | + } | |
| 379 | 442 | |
| 380 | 443 | /* Load affirmative flag options. */ |
| 381 | 444 | for( i=0; i<count(flagDefs); ++i ){ |
| 382 | 445 | if( (!flagDefs[i].changesOnly || changes) |
| 383 | 446 | && find_option(flagDefs[i].option, 0, 0) ){ |
| @@ -409,10 +472,13 @@ | ||
| 409 | 472 | } |
| 410 | 473 | |
| 411 | 474 | /* Confirm current working directory is within checkout. */ |
| 412 | 475 | db_must_be_within_tree(); |
| 413 | 476 | |
| 477 | + /* Get checkout version. l*/ | |
| 478 | + vid = db_lget_int("checkout", 0); | |
| 479 | + | |
| 414 | 480 | /* Relative path flag determination is done by a shared function. */ |
| 415 | 481 | if( determine_cwd_relative_option() ){ |
| 416 | 482 | flags |= C_RELPATH; |
| 417 | 483 | } |
| 418 | 484 | |
| @@ -425,14 +491,25 @@ | ||
| 425 | 491 | printf("\n"); |
| 426 | 492 | #endif |
| 427 | 493 | |
| 428 | 494 | /* We should be done with options. */ |
| 429 | 495 | verify_all_options(); |
| 496 | + | |
| 497 | + /* Check for changed files. */ | |
| 498 | + vfile_check_signature(vid, useSha1sum ? CKSIG_SHA1 : 0); | |
| 499 | + | |
| 500 | + /* Search for unmanaged files if requested. Exclude reserved files. */ | |
| 501 | + if( flags & C_EXTRA ){ | |
| 502 | + Glob *pIgnore = glob_create(zIgnoreFlag); | |
| 503 | + locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); | |
| 504 | + glob_free(pIgnore); | |
| 505 | + db_multi_exec("DELETE FROM sfile WHERE x IN (%s)", | |
| 506 | + fossil_all_reserved_names(0)); | |
| 507 | + } | |
| 430 | 508 | |
| 431 | 509 | /* The status command prints general information before the change list. */ |
| 432 | 510 | if( !changes ){ |
| 433 | - vid = db_lget_int("checkout", 0); | |
| 434 | 511 | fossil_print("repository: %s\n", db_repository_filename()); |
| 435 | 512 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 436 | 513 | if( g.zConfigDbName ){ |
| 437 | 514 | fossil_print("config-db: %s\n", g.zConfigDbName); |
| 438 | 515 | } |
| @@ -440,12 +517,23 @@ | ||
| 440 | 517 | show_common_info(vid, "checkout:", 1, 1); |
| 441 | 518 | } |
| 442 | 519 | db_record_repository_filename(0); |
| 443 | 520 | } |
| 444 | 521 | |
| 445 | - /* Print all requested changes. */ | |
| 446 | - print_changes(flags); | |
| 522 | + /* Find and print all requested changes. */ | |
| 523 | + blob_zero(&report); | |
| 524 | + status_report(&report, flags); | |
| 525 | + if( blob_size(&report) ){ | |
| 526 | + if( showHdr ){ | |
| 527 | + fossil_print("Changes for %s at %s:\n", db_get("project-name", "???"), | |
| 528 | + g.zLocalRoot); | |
| 529 | + } | |
| 530 | + blob_write_to_file(&report, "-"); | |
| 531 | + }else if( verboseFlag ){ | |
| 532 | + fossil_print(" (none)\n"); | |
| 533 | + } | |
| 534 | + blob_reset(&report); | |
| 447 | 535 | |
| 448 | 536 | /* The status command ends with warnings about ambiguous leaves (forks). */ |
| 449 | 537 | if( !changes ){ |
| 450 | 538 | leaf_ambiguity_warning(vid, vid); |
| 451 | 539 | } |
| @@ -666,59 +754,10 @@ | ||
| 666 | 754 | free(zFullName); |
| 667 | 755 | } |
| 668 | 756 | db_finalize(&q); |
| 669 | 757 | } |
| 670 | 758 | |
| 671 | -/* | |
| 672 | -** Create a TEMP table named SFILE and add all unmanaged files named on | |
| 673 | -** the command-line to that table. If directories are named, then add | |
| 674 | -** all unmanaged files contained underneath those directories. If there | |
| 675 | -** are no files or directories named on the command-line, then add all | |
| 676 | -** unmanaged files anywhere in the checkout. | |
| 677 | -*/ | |
| 678 | -static void locate_unmanaged_files( | |
| 679 | - int argc, /* Number of command-line arguments to examine */ | |
| 680 | - char **argv, /* values of command-line arguments */ | |
| 681 | - unsigned scanFlags, /* Zero or more SCAN_xxx flags */ | |
| 682 | - Glob *pIgnore1, /* Do not add files that match this GLOB */ | |
| 683 | - Glob *pIgnore2 /* Omit files matching this GLOB too */ | |
| 684 | -){ | |
| 685 | - Blob name; /* Name of a candidate file or directory */ | |
| 686 | - char *zName; /* Name of a candidate file or directory */ | |
| 687 | - int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ | |
| 688 | - int i; /* Loop counter */ | |
| 689 | - int nRoot; /* length of g.zLocalRoot */ | |
| 690 | - | |
| 691 | - db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", | |
| 692 | - filename_collation()); | |
| 693 | - nRoot = (int)strlen(g.zLocalRoot); | |
| 694 | - if( argc==0 ){ | |
| 695 | - blob_init(&name, g.zLocalRoot, nRoot - 1); | |
| 696 | - vfile_scan(&name, blob_size(&name), scanFlags, pIgnore1, pIgnore2); | |
| 697 | - blob_reset(&name); | |
| 698 | - }else{ | |
| 699 | - for(i=0; i<argc; i++){ | |
| 700 | - file_canonical_name(argv[i], &name, 0); | |
| 701 | - zName = blob_str(&name); | |
| 702 | - isDir = file_wd_isdir(zName); | |
| 703 | - if( isDir==1 ){ | |
| 704 | - vfile_scan(&name, nRoot-1, scanFlags, pIgnore1, pIgnore2); | |
| 705 | - }else if( isDir==0 ){ | |
| 706 | - fossil_warning("not found: %s", &zName[nRoot]); | |
| 707 | - }else if( file_access(zName, R_OK) ){ | |
| 708 | - fossil_fatal("cannot open %s", &zName[nRoot]); | |
| 709 | - }else{ | |
| 710 | - db_multi_exec( | |
| 711 | - "INSERT OR IGNORE INTO sfile(x) VALUES(%Q)", | |
| 712 | - &zName[nRoot] | |
| 713 | - ); | |
| 714 | - } | |
| 715 | - blob_reset(&name); | |
| 716 | - } | |
| 717 | - } | |
| 718 | -} | |
| 719 | - | |
| 720 | 759 | /* |
| 721 | 760 | ** COMMAND: extras |
| 722 | 761 | ** |
| 723 | 762 | ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? |
| 724 | 763 | ** |
| 725 | 764 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -25,14 +25,13 @@ | |
| 25 | /* |
| 26 | ** Change filter options. |
| 27 | */ |
| 28 | enum { |
| 29 | /* Zero-based bit indexes. */ |
| 30 | CB_EDITED , CB_UPDATED , CB_CHANGED, CB_MISSING , CB_ADDED , CB_DELETED, |
| 31 | CB_RENAMED, CB_CONFLICT, CB_META , CB_UNMODIFIED, CB_EXTRA , CB_MERGE , |
| 32 | CB_RELPATH, CB_SHA1SUM , CB_HEADER , CB_VERBOSE , CB_CLASSIFY, CB_FATAL , |
| 33 | CB_COMMENT, |
| 34 | |
| 35 | /* Bitmask values. */ |
| 36 | C_EDITED = 1 << CB_EDITED, /* Edited, merged, and conflicted files. */ |
| 37 | C_UPDATED = 1 << CB_UPDATED, /* Files updated by merge/integrate. */ |
| 38 | C_CHANGED = 1 << CB_CHANGED, /* Treated the same as the above two. */ |
| @@ -48,18 +47,64 @@ | |
| 48 | C_FILTER = C_EDITED | C_UPDATED | C_CHANGED | C_MISSING | C_ADDED |
| 49 | | C_DELETED | C_RENAMED | C_CONFLICT | C_META | C_UNMODIFIED |
| 50 | | C_EXTRA | C_MERGE, |
| 51 | C_ALL = C_FILTER & ~(C_EXTRA | C_MERGE), |
| 52 | C_RELPATH = 1 << CB_RELPATH, /* Show relative paths. */ |
| 53 | C_SHA1SUM = 1 << CB_SHA1SUM, /* Use SHA1 checksums not mtimes. */ |
| 54 | C_HEADER = 1 << CB_HEADER, /* Display repository name if non-empty. */ |
| 55 | C_VERBOSE = 1 << CB_VERBOSE, /* Display "(none)" if empty. */ |
| 56 | C_CLASSIFY = 1 << CB_CLASSIFY, /* Show file change types. */ |
| 57 | C_DEFAULT = (C_ALL & ~C_UNMODIFIED) | C_MERGE | C_CLASSIFY, |
| 58 | C_FATAL = (1 << CB_FATAL) | C_MISSING, /* Fail on MISSING/NOT_A_FILE. */ |
| 59 | C_COMMENT = 1 << CB_COMMENT, /* Precede each line with "# ". */ |
| 60 | }; |
| 61 | |
| 62 | /* |
| 63 | ** Generate text describing all changes. |
| 64 | ** |
| 65 | ** We assume that vfile_check_signature has been run. |
| @@ -69,14 +114,15 @@ | |
| 69 | unsigned flags /* Filter and other configuration flags */ |
| 70 | ){ |
| 71 | Stmt q; |
| 72 | int nErr = 0; |
| 73 | Blob rewrittenPathname; |
| 74 | Blob where; |
| 75 | const char *zName; |
| 76 | int i; |
| 77 | |
| 78 | blob_zero(&where); |
| 79 | for(i=2; i<g.argc; i++){ |
| 80 | Blob fname; |
| 81 | file_tree_name(g.argv[i], &fname, 0, 1); |
| 82 | zName = blob_str(&fname); |
| @@ -91,28 +137,47 @@ | |
| 91 | filename_collation(), zName, filename_collation(), |
| 92 | zName, filename_collation() |
| 93 | ); |
| 94 | } |
| 95 | |
| 96 | db_prepare(&q, |
| 97 | "SELECT pathname, deleted, chnged," |
| 98 | " rid, coalesce(origname!=pathname,0), islink" |
| 99 | " FROM vfile " |
| 100 | " WHERE is_selected(id) %s" |
| 101 | " AND (chnged OR deleted OR rid=0 OR pathname!=origname)" |
| 102 | " ORDER BY 1 /*scan*/", |
| 103 | blob_sql_text(&where) |
| 104 | ); |
| 105 | blob_zero(&rewrittenPathname); |
| 106 | while( db_step(&q)==SQLITE_ROW ){ |
| 107 | const char *zPathname = db_column_text(&q,0); |
| 108 | const char *zClass = 0; |
| 109 | int isDeleted = db_column_int(&q, 1); |
| 110 | int isChnged = db_column_int(&q,2); |
| 111 | int isNew = db_column_int(&q,3)==0; |
| 112 | int isRenamed = db_column_int(&q,4); |
| 113 | int isLink = db_column_int(&q,5); |
| 114 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 115 | int isMissing = !file_wd_isfile_or_link(zFullName); |
| 116 | |
| 117 | /* Determine the file change classification, if any. */ |
| 118 | if( (flags & C_DELETED) && isDeleted ){ |
| @@ -150,20 +215,21 @@ | |
| 150 | }else if( (flags & C_META) && isChnged==9 ){ |
| 151 | zClass = "UNLINK"; |
| 152 | }else if( (flags & C_CONFLICT) && isChnged && !isLink |
| 153 | && file_contains_merge_marker(zFullName) ){ |
| 154 | zClass = "CONFLICT"; |
| 155 | }else if( (flags & (C_EDITED | C_CHANGED)) && (isChnged<2 || isChnged>9) ){ |
| 156 | zClass = "EDITED"; |
| 157 | }else if( (flags & C_RENAMED) && isRenamed ){ |
| 158 | zClass = "RENAMED"; |
| 159 | }else if( (flags & C_UNMODIFIED) && !isDeleted && !isMissing && !isNew |
| 160 | && !isChnged && !isRenamed ){ |
| 161 | /* TODO: never gets executed because query only yields modified files. */ |
| 162 | zClass = "UNMODIFIED"; |
| 163 | } |
| 164 | /* TODO: implement C_EXTRA. */ |
| 165 | /* TODO: reimplement ls and extras in terms of this function. */ |
| 166 | |
| 167 | /* Only report files for which a change classification was determined. */ |
| 168 | if( zClass ){ |
| 169 | /* If C_COMMENT, precede each line with "# ". */ |
| @@ -243,38 +309,19 @@ | |
| 243 | if( absPathOption ){ relativePaths = 0; } |
| 244 | if( relPathOption ){ relativePaths = 1; } |
| 245 | return relativePaths; |
| 246 | } |
| 247 | |
| 248 | void print_changes( |
| 249 | unsigned flags /* Configuration flags */ |
| 250 | ){ |
| 251 | Blob report; |
| 252 | int vid; |
| 253 | blob_zero(&report); |
| 254 | |
| 255 | vid = db_lget_int("checkout", 0); |
| 256 | vfile_check_signature(vid, flags & C_SHA1SUM ? CKSIG_SHA1 : 0); |
| 257 | status_report(&report, flags); |
| 258 | if( (flags & C_VERBOSE) && blob_size(&report)==0 ){ |
| 259 | blob_append(&report, " (none)\n", -1); |
| 260 | } |
| 261 | if( (flags & C_HEADER) && blob_size(&report)>0 ){ |
| 262 | fossil_print("Changes for %s at %s:\n", db_get("project-name","???"), |
| 263 | g.zLocalRoot); |
| 264 | } |
| 265 | blob_write_to_file(&report, "-"); |
| 266 | blob_reset(&report); |
| 267 | } |
| 268 | |
| 269 | /* |
| 270 | ** COMMAND: changes |
| 271 | ** COMMAND: status |
| 272 | ** |
| 273 | ** Usage: %fossil changes|status ?OPTIONS? |
| 274 | ** |
| 275 | ** Report the change status of files in the current checkout. |
| 276 | ** |
| 277 | ** The status command is similar to the changes command, except it lacks |
| 278 | ** several of the options supported by changes and it has its own header |
| 279 | ** and footer information. The header information is a subset of that |
| 280 | ** shown by the info command, and the footer shows if there are any forks. |
| @@ -318,10 +365,13 @@ | |
| 318 | ** --abs-paths Display absolute pathnames. |
| 319 | ** --rel-paths Display pathnames relative to the current working |
| 320 | ** directory. |
| 321 | ** --sha1sum Verify file status using SHA1 hashing rather than |
| 322 | ** relying on file mtimes. |
| 323 | ** |
| 324 | ** Options specific to the changes command: |
| 325 | ** --header Identify the repository if report is non-empty. |
| 326 | ** -v|--verbose Say "(none)" if the change report is empty. |
| 327 | ** --classify Start each line with the file's change type. |
| @@ -356,28 +406,41 @@ | |
| 356 | {"changed" , C_CHANGED, 0}, {"missing" , C_MISSING , 0}, |
| 357 | {"added" , C_ADDED , 0}, {"deleted" , C_DELETED , 0}, |
| 358 | {"renamed" , C_RENAMED, 0}, {"conflict" , C_CONFLICT , 0}, |
| 359 | {"meta" , C_META , 0}, {"unmodified" , C_UNMODIFIED, 0}, |
| 360 | {"all" , C_ALL , 0}, {"extra" , C_EXTRA , 0}, |
| 361 | {"merge" , C_MERGE , 0}, {"sha1sum" , C_SHA1SUM , 0}, |
| 362 | {"header" , C_HEADER , 1}, {"v" , C_VERBOSE , 1}, |
| 363 | {"verbose" , C_VERBOSE, 1}, {"classify" , C_CLASSIFY , 1}, |
| 364 | }, noFlagDefs[] = { |
| 365 | {"no-merge", C_MERGE , 0}, {"no-classify", C_CLASSIFY , 1}, |
| 366 | }; |
| 367 | |
| 368 | #ifdef FOSSIL_DEBUG |
| 369 | static const char *const bits[] = { |
| 370 | "EDITED", "UPDATED", "CHANGED", "MISSING", "ADDED", "DELETED", "RENAMED", |
| 371 | "CONFLICT", "META", "UNMODIFIED", "EXTRA", "MERGE", "RELPATH", "SHA1SUM", |
| 372 | "HEADER", "VERBOSE", "CLASSIFY", |
| 373 | }; |
| 374 | #endif |
| 375 | |
| 376 | int changes = g.argv[1][0]=='c'; |
| 377 | unsigned flags = 0; |
| 378 | int vid, i; |
| 379 | |
| 380 | /* Load affirmative flag options. */ |
| 381 | for( i=0; i<count(flagDefs); ++i ){ |
| 382 | if( (!flagDefs[i].changesOnly || changes) |
| 383 | && find_option(flagDefs[i].option, 0, 0) ){ |
| @@ -409,10 +472,13 @@ | |
| 409 | } |
| 410 | |
| 411 | /* Confirm current working directory is within checkout. */ |
| 412 | db_must_be_within_tree(); |
| 413 | |
| 414 | /* Relative path flag determination is done by a shared function. */ |
| 415 | if( determine_cwd_relative_option() ){ |
| 416 | flags |= C_RELPATH; |
| 417 | } |
| 418 | |
| @@ -425,14 +491,25 @@ | |
| 425 | printf("\n"); |
| 426 | #endif |
| 427 | |
| 428 | /* We should be done with options. */ |
| 429 | verify_all_options(); |
| 430 | |
| 431 | /* The status command prints general information before the change list. */ |
| 432 | if( !changes ){ |
| 433 | vid = db_lget_int("checkout", 0); |
| 434 | fossil_print("repository: %s\n", db_repository_filename()); |
| 435 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 436 | if( g.zConfigDbName ){ |
| 437 | fossil_print("config-db: %s\n", g.zConfigDbName); |
| 438 | } |
| @@ -440,12 +517,23 @@ | |
| 440 | show_common_info(vid, "checkout:", 1, 1); |
| 441 | } |
| 442 | db_record_repository_filename(0); |
| 443 | } |
| 444 | |
| 445 | /* Print all requested changes. */ |
| 446 | print_changes(flags); |
| 447 | |
| 448 | /* The status command ends with warnings about ambiguous leaves (forks). */ |
| 449 | if( !changes ){ |
| 450 | leaf_ambiguity_warning(vid, vid); |
| 451 | } |
| @@ -666,59 +754,10 @@ | |
| 666 | free(zFullName); |
| 667 | } |
| 668 | db_finalize(&q); |
| 669 | } |
| 670 | |
| 671 | /* |
| 672 | ** Create a TEMP table named SFILE and add all unmanaged files named on |
| 673 | ** the command-line to that table. If directories are named, then add |
| 674 | ** all unmanaged files contained underneath those directories. If there |
| 675 | ** are no files or directories named on the command-line, then add all |
| 676 | ** unmanaged files anywhere in the checkout. |
| 677 | */ |
| 678 | static void locate_unmanaged_files( |
| 679 | int argc, /* Number of command-line arguments to examine */ |
| 680 | char **argv, /* values of command-line arguments */ |
| 681 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 682 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 683 | Glob *pIgnore2 /* Omit files matching this GLOB too */ |
| 684 | ){ |
| 685 | Blob name; /* Name of a candidate file or directory */ |
| 686 | char *zName; /* Name of a candidate file or directory */ |
| 687 | int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ |
| 688 | int i; /* Loop counter */ |
| 689 | int nRoot; /* length of g.zLocalRoot */ |
| 690 | |
| 691 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", |
| 692 | filename_collation()); |
| 693 | nRoot = (int)strlen(g.zLocalRoot); |
| 694 | if( argc==0 ){ |
| 695 | blob_init(&name, g.zLocalRoot, nRoot - 1); |
| 696 | vfile_scan(&name, blob_size(&name), scanFlags, pIgnore1, pIgnore2); |
| 697 | blob_reset(&name); |
| 698 | }else{ |
| 699 | for(i=0; i<argc; i++){ |
| 700 | file_canonical_name(argv[i], &name, 0); |
| 701 | zName = blob_str(&name); |
| 702 | isDir = file_wd_isdir(zName); |
| 703 | if( isDir==1 ){ |
| 704 | vfile_scan(&name, nRoot-1, scanFlags, pIgnore1, pIgnore2); |
| 705 | }else if( isDir==0 ){ |
| 706 | fossil_warning("not found: %s", &zName[nRoot]); |
| 707 | }else if( file_access(zName, R_OK) ){ |
| 708 | fossil_fatal("cannot open %s", &zName[nRoot]); |
| 709 | }else{ |
| 710 | db_multi_exec( |
| 711 | "INSERT OR IGNORE INTO sfile(x) VALUES(%Q)", |
| 712 | &zName[nRoot] |
| 713 | ); |
| 714 | } |
| 715 | blob_reset(&name); |
| 716 | } |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | /* |
| 721 | ** COMMAND: extras |
| 722 | ** |
| 723 | ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? |
| 724 | ** |
| 725 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -25,14 +25,13 @@ | |
| 25 | /* |
| 26 | ** Change filter options. |
| 27 | */ |
| 28 | enum { |
| 29 | /* Zero-based bit indexes. */ |
| 30 | CB_EDITED , CB_UPDATED , CB_CHANGED, CB_MISSING , CB_ADDED, CB_DELETED, |
| 31 | CB_RENAMED, CB_CONFLICT, CB_META , CB_UNMODIFIED, CB_EXTRA, CB_MERGE , |
| 32 | CB_RELPATH, CB_CLASSIFY, CB_FATAL , CB_COMMENT , |
| 33 | |
| 34 | /* Bitmask values. */ |
| 35 | C_EDITED = 1 << CB_EDITED, /* Edited, merged, and conflicted files. */ |
| 36 | C_UPDATED = 1 << CB_UPDATED, /* Files updated by merge/integrate. */ |
| 37 | C_CHANGED = 1 << CB_CHANGED, /* Treated the same as the above two. */ |
| @@ -48,18 +47,64 @@ | |
| 47 | C_FILTER = C_EDITED | C_UPDATED | C_CHANGED | C_MISSING | C_ADDED |
| 48 | | C_DELETED | C_RENAMED | C_CONFLICT | C_META | C_UNMODIFIED |
| 49 | | C_EXTRA | C_MERGE, |
| 50 | C_ALL = C_FILTER & ~(C_EXTRA | C_MERGE), |
| 51 | C_RELPATH = 1 << CB_RELPATH, /* Show relative paths. */ |
| 52 | C_CLASSIFY = 1 << CB_CLASSIFY, /* Show file change types. */ |
| 53 | C_DEFAULT = (C_ALL & ~C_UNMODIFIED) | C_MERGE | C_CLASSIFY, |
| 54 | C_FATAL = (1 << CB_FATAL) | C_MISSING, /* Fail on MISSING/NOT_A_FILE. */ |
| 55 | C_COMMENT = 1 << CB_COMMENT, /* Precede each line with "# ". */ |
| 56 | }; |
| 57 | |
| 58 | /* |
| 59 | ** Create a TEMP table named SFILE and add all unmanaged files named on |
| 60 | ** the command-line to that table. If directories are named, then add |
| 61 | ** all unmanaged files contained underneath those directories. If there |
| 62 | ** are no files or directories named on the command-line, then add all |
| 63 | ** unmanaged files anywhere in the checkout. |
| 64 | */ |
| 65 | static void locate_unmanaged_files( |
| 66 | int argc, /* Number of command-line arguments to examine */ |
| 67 | char **argv, /* values of command-line arguments */ |
| 68 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 69 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 70 | Glob *pIgnore2 /* Omit files matching this GLOB too */ |
| 71 | ){ |
| 72 | Blob name; /* Name of a candidate file or directory */ |
| 73 | char *zName; /* Name of a candidate file or directory */ |
| 74 | int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ |
| 75 | int i; /* Loop counter */ |
| 76 | int nRoot; /* length of g.zLocalRoot */ |
| 77 | |
| 78 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", |
| 79 | filename_collation()); |
| 80 | nRoot = (int)strlen(g.zLocalRoot); |
| 81 | if( argc==0 ){ |
| 82 | blob_init(&name, g.zLocalRoot, nRoot - 1); |
| 83 | vfile_scan(&name, blob_size(&name), scanFlags, pIgnore1, pIgnore2); |
| 84 | blob_reset(&name); |
| 85 | }else{ |
| 86 | for(i=0; i<argc; i++){ |
| 87 | file_canonical_name(argv[i], &name, 0); |
| 88 | zName = blob_str(&name); |
| 89 | isDir = file_wd_isdir(zName); |
| 90 | if( isDir==1 ){ |
| 91 | vfile_scan(&name, nRoot-1, scanFlags, pIgnore1, pIgnore2); |
| 92 | }else if( isDir==0 ){ |
| 93 | fossil_warning("not found: %s", &zName[nRoot]); |
| 94 | }else if( file_access(zName, R_OK) ){ |
| 95 | fossil_fatal("cannot open %s", &zName[nRoot]); |
| 96 | }else{ |
| 97 | db_multi_exec( |
| 98 | "INSERT OR IGNORE INTO sfile(x) VALUES(%Q)", |
| 99 | &zName[nRoot] |
| 100 | ); |
| 101 | } |
| 102 | blob_reset(&name); |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | /* |
| 108 | ** Generate text describing all changes. |
| 109 | ** |
| 110 | ** We assume that vfile_check_signature has been run. |
| @@ -69,14 +114,15 @@ | |
| 114 | unsigned flags /* Filter and other configuration flags */ |
| 115 | ){ |
| 116 | Stmt q; |
| 117 | int nErr = 0; |
| 118 | Blob rewrittenPathname; |
| 119 | Blob sql = BLOB_INITIALIZER, where = BLOB_INITIALIZER; |
| 120 | const char *zName; |
| 121 | int i; |
| 122 | |
| 123 | /* Assemble the path-limiting WHERE clause, if any. */ |
| 124 | blob_zero(&where); |
| 125 | for(i=2; i<g.argc; i++){ |
| 126 | Blob fname; |
| 127 | file_tree_name(g.argv[i], &fname, 0, 1); |
| 128 | zName = blob_str(&fname); |
| @@ -91,28 +137,47 @@ | |
| 137 | filename_collation(), zName, filename_collation(), |
| 138 | zName, filename_collation() |
| 139 | ); |
| 140 | } |
| 141 | |
| 142 | /* Start building the SELECT statement. */ |
| 143 | blob_zero(&sql); |
| 144 | blob_append_sql(&sql, |
| 145 | "SELECT pathname, deleted, chnged, rid," |
| 146 | " coalesce(origname!=pathname,0) AS renamed, islink, 1 AS managed" |
| 147 | " FROM vfile" |
| 148 | " WHERE is_selected(id)%s", blob_sql_text(&where)); |
| 149 | |
| 150 | /* Exclude unmodified files unless requested. */ |
| 151 | if( !(flags & C_UNMODIFIED) ){ |
| 152 | blob_append_sql(&sql, |
| 153 | " AND (chnged OR deleted OR rid=0 OR pathname!=origname)"); |
| 154 | } |
| 155 | |
| 156 | /* If C_EXTRA, add unmanaged files to the query result too. */ |
| 157 | if( flags & C_EXTRA ){ |
| 158 | blob_append_sql(&sql, " UNION ALL SELECT x AS pathname, 0, 0, 0, 0, 0, 0" |
| 159 | " FROM sfile WHERE 1%s", blob_sql_text(&where)); |
| 160 | } |
| 161 | |
| 162 | /* Append an ORDER BY clause then compile the query. */ |
| 163 | blob_append_sql(&sql, " ORDER BY pathname"); |
| 164 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 165 | blob_reset(&sql); |
| 166 | blob_reset(&where); |
| 167 | |
| 168 | /* Execute the query and assemble the report. */ |
| 169 | blob_zero(&rewrittenPathname); |
| 170 | while( db_step(&q)==SQLITE_ROW ){ |
| 171 | const char *zPathname = db_column_text(&q, 0); |
| 172 | const char *zClass = 0; |
| 173 | int isManaged = db_column_int(&q, 6); |
| 174 | int isDeleted = db_column_int(&q, 1); |
| 175 | int isChnged = db_column_int(&q, 2); |
| 176 | int isNew = isManaged && !db_column_int(&q, 3); |
| 177 | int isRenamed = db_column_int(&q, 4); |
| 178 | int isLink = db_column_int(&q, 5); |
| 179 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 180 | int isMissing = !file_wd_isfile_or_link(zFullName); |
| 181 | |
| 182 | /* Determine the file change classification, if any. */ |
| 183 | if( (flags & C_DELETED) && isDeleted ){ |
| @@ -150,20 +215,21 @@ | |
| 215 | }else if( (flags & C_META) && isChnged==9 ){ |
| 216 | zClass = "UNLINK"; |
| 217 | }else if( (flags & C_CONFLICT) && isChnged && !isLink |
| 218 | && file_contains_merge_marker(zFullName) ){ |
| 219 | zClass = "CONFLICT"; |
| 220 | }else if( (flags & (C_EDITED | C_CHANGED)) && isChnged |
| 221 | && (isChnged<2 || isChnged>9) ){ |
| 222 | zClass = "EDITED"; |
| 223 | }else if( (flags & C_RENAMED) && isRenamed ){ |
| 224 | zClass = "RENAMED"; |
| 225 | }else if( (flags & C_UNMODIFIED) && isManaged && !isDeleted && !isMissing |
| 226 | && !isNew && !isChnged && !isRenamed ){ |
| 227 | zClass = "UNMODIFIED"; |
| 228 | }else if( (flags & C_EXTRA) && !isManaged ){ |
| 229 | zClass = "EXTRA"; |
| 230 | } |
| 231 | /* TODO: reimplement ls and extras in terms of this function. */ |
| 232 | |
| 233 | /* Only report files for which a change classification was determined. */ |
| 234 | if( zClass ){ |
| 235 | /* If C_COMMENT, precede each line with "# ". */ |
| @@ -243,38 +309,19 @@ | |
| 309 | if( absPathOption ){ relativePaths = 0; } |
| 310 | if( relPathOption ){ relativePaths = 1; } |
| 311 | return relativePaths; |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | ** COMMAND: changes |
| 316 | ** COMMAND: status |
| 317 | ** |
| 318 | ** Usage: %fossil changes|status ?OPTIONS? ?PATHS ...? |
| 319 | ** |
| 320 | ** Report the change status of files in the current checkout. If one or |
| 321 | ** more PATHS are specified, only changes among the named files and |
| 322 | ** directories are reported. Directories are searched recursively. |
| 323 | ** |
| 324 | ** The status command is similar to the changes command, except it lacks |
| 325 | ** several of the options supported by changes and it has its own header |
| 326 | ** and footer information. The header information is a subset of that |
| 327 | ** shown by the info command, and the footer shows if there are any forks. |
| @@ -318,10 +365,13 @@ | |
| 365 | ** --abs-paths Display absolute pathnames. |
| 366 | ** --rel-paths Display pathnames relative to the current working |
| 367 | ** directory. |
| 368 | ** --sha1sum Verify file status using SHA1 hashing rather than |
| 369 | ** relying on file mtimes. |
| 370 | ** --case-sensitive <BOOL> Override case-sensitive setting. |
| 371 | ** --dotfiles Include unmanaged files beginning with a dot. |
| 372 | ** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns. |
| 373 | ** |
| 374 | ** Options specific to the changes command: |
| 375 | ** --header Identify the repository if report is non-empty. |
| 376 | ** -v|--verbose Say "(none)" if the change report is empty. |
| 377 | ** --classify Start each line with the file's change type. |
| @@ -356,28 +406,41 @@ | |
| 406 | {"changed" , C_CHANGED, 0}, {"missing" , C_MISSING , 0}, |
| 407 | {"added" , C_ADDED , 0}, {"deleted" , C_DELETED , 0}, |
| 408 | {"renamed" , C_RENAMED, 0}, {"conflict" , C_CONFLICT , 0}, |
| 409 | {"meta" , C_META , 0}, {"unmodified" , C_UNMODIFIED, 0}, |
| 410 | {"all" , C_ALL , 0}, {"extra" , C_EXTRA , 0}, |
| 411 | {"merge" , C_MERGE , 0}, {"classify" , C_CLASSIFY , 1}, |
| 412 | }, noFlagDefs[] = { |
| 413 | {"no-merge", C_MERGE , 0}, {"no-classify", C_CLASSIFY , 1}, |
| 414 | }; |
| 415 | |
| 416 | #ifdef FOSSIL_DEBUG |
| 417 | static const char *const bits[] = { |
| 418 | "EDITED", "UPDATED", "CHANGED", "MISSING", "ADDED", "DELETED", "RENAMED", |
| 419 | "CONFLICT", "META", "UNMODIFIED", "EXTRA", "MERGE", "RELPATH", "CLASSIFY", |
| 420 | }; |
| 421 | #endif |
| 422 | |
| 423 | Blob report = BLOB_INITIALIZER; |
| 424 | int useSha1sum = find_option("sha1sum", 0, 0)!=0; |
| 425 | int showHdr = find_option("header",0,0)!=0; |
| 426 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 427 | const char *zIgnoreFlag = find_option("ignore", 0, 1); |
| 428 | unsigned scanFlags = 0; |
| 429 | int changes = g.argv[1][0]=='c'; |
| 430 | unsigned flags = 0; |
| 431 | int vid, i; |
| 432 | |
| 433 | /* If --ignore is not specified, use the ignore-glob setting. */ |
| 434 | if( !zIgnoreFlag ){ |
| 435 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 436 | } |
| 437 | |
| 438 | /* Get the --dotfiles argument, or read it from the dotfiles setting. */ |
| 439 | if( find_option("dotfiles", 0, 0) || db_get_boolean("dotfiles", 0) ){ |
| 440 | scanFlags = SCAN_ALL; |
| 441 | } |
| 442 | |
| 443 | /* Load affirmative flag options. */ |
| 444 | for( i=0; i<count(flagDefs); ++i ){ |
| 445 | if( (!flagDefs[i].changesOnly || changes) |
| 446 | && find_option(flagDefs[i].option, 0, 0) ){ |
| @@ -409,10 +472,13 @@ | |
| 472 | } |
| 473 | |
| 474 | /* Confirm current working directory is within checkout. */ |
| 475 | db_must_be_within_tree(); |
| 476 | |
| 477 | /* Get checkout version. l*/ |
| 478 | vid = db_lget_int("checkout", 0); |
| 479 | |
| 480 | /* Relative path flag determination is done by a shared function. */ |
| 481 | if( determine_cwd_relative_option() ){ |
| 482 | flags |= C_RELPATH; |
| 483 | } |
| 484 | |
| @@ -425,14 +491,25 @@ | |
| 491 | printf("\n"); |
| 492 | #endif |
| 493 | |
| 494 | /* We should be done with options. */ |
| 495 | verify_all_options(); |
| 496 | |
| 497 | /* Check for changed files. */ |
| 498 | vfile_check_signature(vid, useSha1sum ? CKSIG_SHA1 : 0); |
| 499 | |
| 500 | /* Search for unmanaged files if requested. Exclude reserved files. */ |
| 501 | if( flags & C_EXTRA ){ |
| 502 | Glob *pIgnore = glob_create(zIgnoreFlag); |
| 503 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); |
| 504 | glob_free(pIgnore); |
| 505 | db_multi_exec("DELETE FROM sfile WHERE x IN (%s)", |
| 506 | fossil_all_reserved_names(0)); |
| 507 | } |
| 508 | |
| 509 | /* The status command prints general information before the change list. */ |
| 510 | if( !changes ){ |
| 511 | fossil_print("repository: %s\n", db_repository_filename()); |
| 512 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 513 | if( g.zConfigDbName ){ |
| 514 | fossil_print("config-db: %s\n", g.zConfigDbName); |
| 515 | } |
| @@ -440,12 +517,23 @@ | |
| 517 | show_common_info(vid, "checkout:", 1, 1); |
| 518 | } |
| 519 | db_record_repository_filename(0); |
| 520 | } |
| 521 | |
| 522 | /* Find and print all requested changes. */ |
| 523 | blob_zero(&report); |
| 524 | status_report(&report, flags); |
| 525 | if( blob_size(&report) ){ |
| 526 | if( showHdr ){ |
| 527 | fossil_print("Changes for %s at %s:\n", db_get("project-name", "???"), |
| 528 | g.zLocalRoot); |
| 529 | } |
| 530 | blob_write_to_file(&report, "-"); |
| 531 | }else if( verboseFlag ){ |
| 532 | fossil_print(" (none)\n"); |
| 533 | } |
| 534 | blob_reset(&report); |
| 535 | |
| 536 | /* The status command ends with warnings about ambiguous leaves (forks). */ |
| 537 | if( !changes ){ |
| 538 | leaf_ambiguity_warning(vid, vid); |
| 539 | } |
| @@ -666,59 +754,10 @@ | |
| 754 | free(zFullName); |
| 755 | } |
| 756 | db_finalize(&q); |
| 757 | } |
| 758 | |
| 759 | /* |
| 760 | ** COMMAND: extras |
| 761 | ** |
| 762 | ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? |
| 763 | ** |
| 764 |