Fossil SCM
Implement -x|-X|-e|--exclude options for "fossil clean". Compatible with "git clean -x|-X|-e|--exclude".
Commit
44025538c402793240b3e49f0385a44cb17c1e07
Parent
e91d65afe5a7441…
1 file changed
+44
-19
+44
-19
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -236,11 +236,11 @@ | ||
| 236 | 236 | int verboseFlag; |
| 237 | 237 | int showAge; |
| 238 | 238 | char *zOrderBy = "pathname"; |
| 239 | 239 | |
| 240 | 240 | verboseFlag = find_option("verbose","v", 0)!=0; |
| 241 | - if(!verboseFlag){ | |
| 241 | + if( !verboseFlag ){ | |
| 242 | 242 | verboseFlag = find_option("l","l", 0)!=0; /* deprecated */ |
| 243 | 243 | } |
| 244 | 244 | showAge = find_option("age",0,0)!=0; |
| 245 | 245 | db_must_be_within_tree(); |
| 246 | 246 | vid = db_lget_int("checkout", 0); |
| @@ -381,11 +381,13 @@ | ||
| 381 | 381 | ** COMMAND: clean |
| 382 | 382 | ** Usage: %fossil clean ?OPTIONS? |
| 383 | 383 | ** |
| 384 | 384 | ** Delete all "extra" files in the source tree. "Extra" files are |
| 385 | 385 | ** files that are not officially part of the checkout. This operation |
| 386 | -** cannot be undone. | |
| 386 | +** cannot be undone. Normally, only files unknown to fossil are | |
| 387 | +** removed, but if the -x option is specified, ignored files are | |
| 388 | +** removed as well. | |
| 387 | 389 | ** |
| 388 | 390 | ** You will be prompted before removing each eligible file unless the |
| 389 | 391 | ** --force flag is in use or it matches the --clean option. The |
| 390 | 392 | ** GLOBPATTERN specified by the "ignore-glob" setting is used if the |
| 391 | 393 | ** --ignore option is omitted, the same with "clean-glob" and --clean |
| @@ -399,49 +401,63 @@ | ||
| 399 | 401 | ** normally kept. They are handled if the "--dotfiles" option |
| 400 | 402 | ** is used. |
| 401 | 403 | ** |
| 402 | 404 | ** Options: |
| 403 | 405 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 404 | -** --dotfiles include files beginning with a dot (".") | |
| 405 | -** -f|--force Remove files without prompting | |
| 406 | -** --clean <CSG> never prompt for files matching this | |
| 407 | -** comma separated list of glob patterns. | |
| 408 | -** --ignore <CSG> ignore files matching patterns from the | |
| 409 | -** comma separated list of glob patterns. | |
| 410 | -** --keep <CSG> keep files matching this comma separated | |
| 411 | -** list of glob patterns. | |
| 412 | -** -n|--dry-run If given, display instead of run actions | |
| 413 | -** --temp Remove only Fossil-generated temporary files | |
| 414 | -** -v|--verbose Show all files as they are removed | |
| 406 | +** --dotfiles include files beginning with a dot (".") | |
| 407 | +** -f|--force Remove files without prompting | |
| 408 | +** --clean <CSG> never prompt for files matching this | |
| 409 | +** comma separated list of glob patterns. | |
| 410 | +** --ignore <CSG> ignore files matching patterns from the | |
| 411 | +** comma separated list of glob patterns. | |
| 412 | +** -e|--exclude <CSG> In addition to those found in --ignore or | |
| 413 | +** "ignore-glob", consider these patterns to | |
| 414 | +** be in the set of the ignore rules as well. | |
| 415 | +** --keep <CSG> keep files matching this comma separated | |
| 416 | +** list of glob patterns, even with -x/-X. | |
| 417 | +** -n|--dry-run If given, display instead of run actions | |
| 418 | +** --temp Remove only Fossil-generated temporary files | |
| 419 | +** -v|--verbose Show all files as they are removed | |
| 420 | +** -x Don't use "ignore-glob" as default for | |
| 421 | +** --ignore. For compatibility with git. | |
| 422 | +** -X Remove only files ignored by fossil. This | |
| 423 | +** may be useful to rebuild everything from | |
| 424 | +** scratch, but keep manually created files. | |
| 425 | +** Compatible with "git clean -X" | |
| 415 | 426 | ** |
| 416 | 427 | ** See also: addremove, extra, status |
| 417 | 428 | */ |
| 418 | 429 | void clean_cmd(void){ |
| 419 | 430 | int allFlag; |
| 420 | 431 | unsigned scanFlags = 0; |
| 421 | - const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; | |
| 432 | + const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag, *zExcludeFlag; | |
| 422 | 433 | Blob path, repo; |
| 423 | 434 | Stmt q; |
| 424 | 435 | int n; |
| 425 | 436 | Glob *pIgnore, *pKeep, *pClean; |
| 426 | - int dryRunFlag = 0; | |
| 437 | + int xflag, capXflag = 0, dryRunFlag; | |
| 427 | 438 | int verboseFlag; |
| 428 | 439 | |
| 429 | 440 | allFlag = find_option("force","f",0)!=0; |
| 430 | 441 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 431 | 442 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 432 | 443 | zIgnoreFlag = find_option("ignore",0,1); |
| 444 | + zExcludeFlag = find_option("exclude","e",1); | |
| 433 | 445 | verboseFlag = find_option("verbose","v",0)!=0; |
| 434 | 446 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 447 | + xflag = find_option("x","x",0)!=0; | |
| 448 | + if ( !xflag ){ | |
| 449 | + capXflag = find_option("X","X",0)!=0; | |
| 450 | + } | |
| 435 | 451 | if( !dryRunFlag ){ |
| 436 | 452 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 437 | 453 | } |
| 438 | 454 | zKeepFlag = find_option("keep",0,1); |
| 439 | 455 | zCleanFlag = find_option("clean",0,1); |
| 440 | 456 | capture_case_sensitive_option(); |
| 441 | 457 | db_must_be_within_tree(); |
| 442 | - if( zIgnoreFlag==0 ){ | |
| 458 | + if( zIgnoreFlag==0 && !xflag){ | |
| 443 | 459 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 444 | 460 | } |
| 445 | 461 | if( zKeepFlag==0 ){ |
| 446 | 462 | zKeepFlag = db_get("keep-glob", 0); |
| 447 | 463 | } |
| @@ -451,16 +467,22 @@ | ||
| 451 | 467 | verify_all_options(); |
| 452 | 468 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", |
| 453 | 469 | filename_collation()); |
| 454 | 470 | n = strlen(g.zLocalRoot); |
| 455 | 471 | blob_init(&path, g.zLocalRoot, n-1); |
| 472 | + if( zExcludeFlag ){ | |
| 473 | + if( zIgnoreFlag ){ | |
| 474 | + zIgnoreFlag = mprintf("%s,%s", zIgnoreFlag, zExcludeFlag); | |
| 475 | + }else{ | |
| 476 | + zIgnoreFlag = zExcludeFlag; | |
| 477 | + } | |
| 478 | + } | |
| 456 | 479 | pIgnore = glob_create(zIgnoreFlag); |
| 457 | 480 | pKeep = glob_create(zKeepFlag); |
| 458 | 481 | pClean = glob_create(zCleanFlag); |
| 459 | - vfile_scan2(&path, blob_size(&path), scanFlags, pIgnore, pKeep); | |
| 482 | + vfile_scan2(&path, blob_size(&path), scanFlags, capXflag?0:pIgnore, pKeep); | |
| 460 | 483 | glob_free(pKeep); |
| 461 | - glob_free(pIgnore); | |
| 462 | 484 | db_prepare(&q, |
| 463 | 485 | "SELECT %Q || x FROM sfile" |
| 464 | 486 | " WHERE x NOT IN (%s)" |
| 465 | 487 | " ORDER BY 1", |
| 466 | 488 | g.zLocalRoot, fossil_all_reserved_names(0) |
| @@ -469,11 +491,13 @@ | ||
| 469 | 491 | db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); |
| 470 | 492 | } |
| 471 | 493 | db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); |
| 472 | 494 | while( db_step(&q)==SQLITE_ROW ){ |
| 473 | 495 | const char *zName = db_column_text(&q, 0); |
| 474 | - if( !allFlag && !dryRunFlag && !glob_match(pClean, zName+n) ){ | |
| 496 | + if( capXflag ){ | |
| 497 | + if ( !glob_match(pIgnore, zName+n) ) continue; | |
| 498 | + }else if( !allFlag && !dryRunFlag && !glob_match(pClean, zName+n) ){ | |
| 475 | 499 | Blob ans; |
| 476 | 500 | char cReply; |
| 477 | 501 | char *prompt = mprintf("remove unmanaged file \"%s\" (a=all/y/N)? ", |
| 478 | 502 | zName+n); |
| 479 | 503 | blob_zero(&ans); |
| @@ -490,10 +514,11 @@ | ||
| 490 | 514 | } |
| 491 | 515 | if( !dryRunFlag ){ |
| 492 | 516 | file_delete(zName); |
| 493 | 517 | } |
| 494 | 518 | } |
| 519 | + glob_free(pIgnore); | |
| 495 | 520 | glob_free(pClean); |
| 496 | 521 | db_finalize(&q); |
| 497 | 522 | } |
| 498 | 523 | |
| 499 | 524 | /* |
| 500 | 525 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -236,11 +236,11 @@ | |
| 236 | int verboseFlag; |
| 237 | int showAge; |
| 238 | char *zOrderBy = "pathname"; |
| 239 | |
| 240 | verboseFlag = find_option("verbose","v", 0)!=0; |
| 241 | if(!verboseFlag){ |
| 242 | verboseFlag = find_option("l","l", 0)!=0; /* deprecated */ |
| 243 | } |
| 244 | showAge = find_option("age",0,0)!=0; |
| 245 | db_must_be_within_tree(); |
| 246 | vid = db_lget_int("checkout", 0); |
| @@ -381,11 +381,13 @@ | |
| 381 | ** COMMAND: clean |
| 382 | ** Usage: %fossil clean ?OPTIONS? |
| 383 | ** |
| 384 | ** Delete all "extra" files in the source tree. "Extra" files are |
| 385 | ** files that are not officially part of the checkout. This operation |
| 386 | ** cannot be undone. |
| 387 | ** |
| 388 | ** You will be prompted before removing each eligible file unless the |
| 389 | ** --force flag is in use or it matches the --clean option. The |
| 390 | ** GLOBPATTERN specified by the "ignore-glob" setting is used if the |
| 391 | ** --ignore option is omitted, the same with "clean-glob" and --clean |
| @@ -399,49 +401,63 @@ | |
| 399 | ** normally kept. They are handled if the "--dotfiles" option |
| 400 | ** is used. |
| 401 | ** |
| 402 | ** Options: |
| 403 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 404 | ** --dotfiles include files beginning with a dot (".") |
| 405 | ** -f|--force Remove files without prompting |
| 406 | ** --clean <CSG> never prompt for files matching this |
| 407 | ** comma separated list of glob patterns. |
| 408 | ** --ignore <CSG> ignore files matching patterns from the |
| 409 | ** comma separated list of glob patterns. |
| 410 | ** --keep <CSG> keep files matching this comma separated |
| 411 | ** list of glob patterns. |
| 412 | ** -n|--dry-run If given, display instead of run actions |
| 413 | ** --temp Remove only Fossil-generated temporary files |
| 414 | ** -v|--verbose Show all files as they are removed |
| 415 | ** |
| 416 | ** See also: addremove, extra, status |
| 417 | */ |
| 418 | void clean_cmd(void){ |
| 419 | int allFlag; |
| 420 | unsigned scanFlags = 0; |
| 421 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; |
| 422 | Blob path, repo; |
| 423 | Stmt q; |
| 424 | int n; |
| 425 | Glob *pIgnore, *pKeep, *pClean; |
| 426 | int dryRunFlag = 0; |
| 427 | int verboseFlag; |
| 428 | |
| 429 | allFlag = find_option("force","f",0)!=0; |
| 430 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 431 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 432 | zIgnoreFlag = find_option("ignore",0,1); |
| 433 | verboseFlag = find_option("verbose","v",0)!=0; |
| 434 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 435 | if( !dryRunFlag ){ |
| 436 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 437 | } |
| 438 | zKeepFlag = find_option("keep",0,1); |
| 439 | zCleanFlag = find_option("clean",0,1); |
| 440 | capture_case_sensitive_option(); |
| 441 | db_must_be_within_tree(); |
| 442 | if( zIgnoreFlag==0 ){ |
| 443 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 444 | } |
| 445 | if( zKeepFlag==0 ){ |
| 446 | zKeepFlag = db_get("keep-glob", 0); |
| 447 | } |
| @@ -451,16 +467,22 @@ | |
| 451 | verify_all_options(); |
| 452 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", |
| 453 | filename_collation()); |
| 454 | n = strlen(g.zLocalRoot); |
| 455 | blob_init(&path, g.zLocalRoot, n-1); |
| 456 | pIgnore = glob_create(zIgnoreFlag); |
| 457 | pKeep = glob_create(zKeepFlag); |
| 458 | pClean = glob_create(zCleanFlag); |
| 459 | vfile_scan2(&path, blob_size(&path), scanFlags, pIgnore, pKeep); |
| 460 | glob_free(pKeep); |
| 461 | glob_free(pIgnore); |
| 462 | db_prepare(&q, |
| 463 | "SELECT %Q || x FROM sfile" |
| 464 | " WHERE x NOT IN (%s)" |
| 465 | " ORDER BY 1", |
| 466 | g.zLocalRoot, fossil_all_reserved_names(0) |
| @@ -469,11 +491,13 @@ | |
| 469 | db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); |
| 470 | } |
| 471 | db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); |
| 472 | while( db_step(&q)==SQLITE_ROW ){ |
| 473 | const char *zName = db_column_text(&q, 0); |
| 474 | if( !allFlag && !dryRunFlag && !glob_match(pClean, zName+n) ){ |
| 475 | Blob ans; |
| 476 | char cReply; |
| 477 | char *prompt = mprintf("remove unmanaged file \"%s\" (a=all/y/N)? ", |
| 478 | zName+n); |
| 479 | blob_zero(&ans); |
| @@ -490,10 +514,11 @@ | |
| 490 | } |
| 491 | if( !dryRunFlag ){ |
| 492 | file_delete(zName); |
| 493 | } |
| 494 | } |
| 495 | glob_free(pClean); |
| 496 | db_finalize(&q); |
| 497 | } |
| 498 | |
| 499 | /* |
| 500 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -236,11 +236,11 @@ | |
| 236 | int verboseFlag; |
| 237 | int showAge; |
| 238 | char *zOrderBy = "pathname"; |
| 239 | |
| 240 | verboseFlag = find_option("verbose","v", 0)!=0; |
| 241 | if( !verboseFlag ){ |
| 242 | verboseFlag = find_option("l","l", 0)!=0; /* deprecated */ |
| 243 | } |
| 244 | showAge = find_option("age",0,0)!=0; |
| 245 | db_must_be_within_tree(); |
| 246 | vid = db_lget_int("checkout", 0); |
| @@ -381,11 +381,13 @@ | |
| 381 | ** COMMAND: clean |
| 382 | ** Usage: %fossil clean ?OPTIONS? |
| 383 | ** |
| 384 | ** Delete all "extra" files in the source tree. "Extra" files are |
| 385 | ** files that are not officially part of the checkout. This operation |
| 386 | ** cannot be undone. Normally, only files unknown to fossil are |
| 387 | ** removed, but if the -x option is specified, ignored files are |
| 388 | ** removed as well. |
| 389 | ** |
| 390 | ** You will be prompted before removing each eligible file unless the |
| 391 | ** --force flag is in use or it matches the --clean option. The |
| 392 | ** GLOBPATTERN specified by the "ignore-glob" setting is used if the |
| 393 | ** --ignore option is omitted, the same with "clean-glob" and --clean |
| @@ -399,49 +401,63 @@ | |
| 401 | ** normally kept. They are handled if the "--dotfiles" option |
| 402 | ** is used. |
| 403 | ** |
| 404 | ** Options: |
| 405 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 406 | ** --dotfiles include files beginning with a dot (".") |
| 407 | ** -f|--force Remove files without prompting |
| 408 | ** --clean <CSG> never prompt for files matching this |
| 409 | ** comma separated list of glob patterns. |
| 410 | ** --ignore <CSG> ignore files matching patterns from the |
| 411 | ** comma separated list of glob patterns. |
| 412 | ** -e|--exclude <CSG> In addition to those found in --ignore or |
| 413 | ** "ignore-glob", consider these patterns to |
| 414 | ** be in the set of the ignore rules as well. |
| 415 | ** --keep <CSG> keep files matching this comma separated |
| 416 | ** list of glob patterns, even with -x/-X. |
| 417 | ** -n|--dry-run If given, display instead of run actions |
| 418 | ** --temp Remove only Fossil-generated temporary files |
| 419 | ** -v|--verbose Show all files as they are removed |
| 420 | ** -x Don't use "ignore-glob" as default for |
| 421 | ** --ignore. For compatibility with git. |
| 422 | ** -X Remove only files ignored by fossil. This |
| 423 | ** may be useful to rebuild everything from |
| 424 | ** scratch, but keep manually created files. |
| 425 | ** Compatible with "git clean -X" |
| 426 | ** |
| 427 | ** See also: addremove, extra, status |
| 428 | */ |
| 429 | void clean_cmd(void){ |
| 430 | int allFlag; |
| 431 | unsigned scanFlags = 0; |
| 432 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag, *zExcludeFlag; |
| 433 | Blob path, repo; |
| 434 | Stmt q; |
| 435 | int n; |
| 436 | Glob *pIgnore, *pKeep, *pClean; |
| 437 | int xflag, capXflag = 0, dryRunFlag; |
| 438 | int verboseFlag; |
| 439 | |
| 440 | allFlag = find_option("force","f",0)!=0; |
| 441 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 442 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 443 | zIgnoreFlag = find_option("ignore",0,1); |
| 444 | zExcludeFlag = find_option("exclude","e",1); |
| 445 | verboseFlag = find_option("verbose","v",0)!=0; |
| 446 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 447 | xflag = find_option("x","x",0)!=0; |
| 448 | if ( !xflag ){ |
| 449 | capXflag = find_option("X","X",0)!=0; |
| 450 | } |
| 451 | if( !dryRunFlag ){ |
| 452 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 453 | } |
| 454 | zKeepFlag = find_option("keep",0,1); |
| 455 | zCleanFlag = find_option("clean",0,1); |
| 456 | capture_case_sensitive_option(); |
| 457 | db_must_be_within_tree(); |
| 458 | if( zIgnoreFlag==0 && !xflag){ |
| 459 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 460 | } |
| 461 | if( zKeepFlag==0 ){ |
| 462 | zKeepFlag = db_get("keep-glob", 0); |
| 463 | } |
| @@ -451,16 +467,22 @@ | |
| 467 | verify_all_options(); |
| 468 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", |
| 469 | filename_collation()); |
| 470 | n = strlen(g.zLocalRoot); |
| 471 | blob_init(&path, g.zLocalRoot, n-1); |
| 472 | if( zExcludeFlag ){ |
| 473 | if( zIgnoreFlag ){ |
| 474 | zIgnoreFlag = mprintf("%s,%s", zIgnoreFlag, zExcludeFlag); |
| 475 | }else{ |
| 476 | zIgnoreFlag = zExcludeFlag; |
| 477 | } |
| 478 | } |
| 479 | pIgnore = glob_create(zIgnoreFlag); |
| 480 | pKeep = glob_create(zKeepFlag); |
| 481 | pClean = glob_create(zCleanFlag); |
| 482 | vfile_scan2(&path, blob_size(&path), scanFlags, capXflag?0:pIgnore, pKeep); |
| 483 | glob_free(pKeep); |
| 484 | db_prepare(&q, |
| 485 | "SELECT %Q || x FROM sfile" |
| 486 | " WHERE x NOT IN (%s)" |
| 487 | " ORDER BY 1", |
| 488 | g.zLocalRoot, fossil_all_reserved_names(0) |
| @@ -469,11 +491,13 @@ | |
| 491 | db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); |
| 492 | } |
| 493 | db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); |
| 494 | while( db_step(&q)==SQLITE_ROW ){ |
| 495 | const char *zName = db_column_text(&q, 0); |
| 496 | if( capXflag ){ |
| 497 | if ( !glob_match(pIgnore, zName+n) ) continue; |
| 498 | }else if( !allFlag && !dryRunFlag && !glob_match(pClean, zName+n) ){ |
| 499 | Blob ans; |
| 500 | char cReply; |
| 501 | char *prompt = mprintf("remove unmanaged file \"%s\" (a=all/y/N)? ", |
| 502 | zName+n); |
| 503 | blob_zero(&ans); |
| @@ -490,10 +514,11 @@ | |
| 514 | } |
| 515 | if( !dryRunFlag ){ |
| 516 | file_delete(zName); |
| 517 | } |
| 518 | } |
| 519 | glob_free(pIgnore); |
| 520 | glob_free(pClean); |
| 521 | db_finalize(&q); |
| 522 | } |
| 523 | |
| 524 | /* |
| 525 |