Fossil SCM
Begin enhancing the "fossil purge" command to be much more flexible and powerful. Plans are to provide the ability to purge specific files, or tickets, or wiki pages, with options to limit the timeframe of the purge.
Commit
45fcac621f1e3c4425cb7caaaa9cd043f49afdc8
Parent
f0d84833388b119…
1 file changed
+79
-48
+79
-48
| --- src/purge.c | ||
| +++ src/purge.c | ||
| @@ -22,11 +22,11 @@ | ||
| 22 | 22 | #include "config.h" |
| 23 | 23 | #include "purge.h" |
| 24 | 24 | #include <assert.h> |
| 25 | 25 | |
| 26 | 26 | /* |
| 27 | -** SQL code used to initialize the schema of a bundle. | |
| 27 | +** SQL code used to initialize the schema of the graveyard. | |
| 28 | 28 | ** |
| 29 | 29 | ** The purgeevent table contains one entry for each purge event. For each |
| 30 | 30 | ** purge event, multiple artifacts might have been removed. Each removed |
| 31 | 31 | ** artifact is stored as an entry in the purgeitem table. |
| 32 | 32 | ** |
| @@ -52,10 +52,17 @@ | ||
| 52 | 52 | @ desc TEXT, -- Brief description of this artifact |
| 53 | 53 | @ data BLOB -- Compressed artifact content |
| 54 | 54 | @ ); |
| 55 | 55 | ; |
| 56 | 56 | |
| 57 | +/* | |
| 58 | +** Flags for the purge_artifact_list() function. | |
| 59 | +*/ | |
| 60 | +#if INTERFACE | |
| 61 | +#define PURGE_MOVETO_GRAVEYARD 0x0001 /* Move artifacts in graveyard */ | |
| 62 | +#endif | |
| 63 | + | |
| 57 | 64 | /* |
| 58 | 65 | ** This routine purges multiple artifacts from the repository, transfering |
| 59 | 66 | ** those artifacts into the PURGEITEM table. |
| 60 | 67 | ** |
| 61 | 68 | ** Prior to invoking this routine, the caller must create a (TEMP) table |
| @@ -81,17 +88,17 @@ | ||
| 81 | 88 | ** (j) TICKETCHNG |
| 82 | 89 | ** (7) If any ticket artifacts were removed (6j) then rebuild the |
| 83 | 90 | ** corresponding ticket entries. Possibly remove entries from |
| 84 | 91 | ** the ticket table. |
| 85 | 92 | ** |
| 86 | -** Stops 1-4 (saving the purged artifacts into the graveyard) are only | |
| 93 | +** Steps 1-4 (saving the purged artifacts into the graveyard) are only | |
| 87 | 94 | ** undertaken if the moveToGraveyard flag is true. |
| 88 | 95 | */ |
| 89 | 96 | int purge_artifact_list( |
| 90 | 97 | const char *zTab, /* TEMP table containing list of RIDS to be purged */ |
| 91 | 98 | const char *zNote, /* Text of the purgeevent.pnotes field */ |
| 92 | - int moveToGraveyard /* Move purged artifacts into the graveyard */ | |
| 99 | + unsigned purgeFlags /* zero or more PURGE_* flags */ | |
| 93 | 100 | ){ |
| 94 | 101 | int peid = 0; /* New purgeevent ID */ |
| 95 | 102 | Stmt q; /* General-use prepared statement */ |
| 96 | 103 | char *z; |
| 97 | 104 | |
| @@ -121,11 +128,11 @@ | ||
| 121 | 128 | } |
| 122 | 129 | db_finalize(&q); |
| 123 | 130 | |
| 124 | 131 | /* Construct the graveyard and copy the artifacts to be purged into the |
| 125 | 132 | ** graveyard */ |
| 126 | - if( moveToGraveyard ){ | |
| 133 | + if( purgeFlags & PURGE_MOVETO_GRAVEYARD ){ | |
| 127 | 134 | db_multi_exec(zPurgeInit /*works-like:"%w%w"*/, |
| 128 | 135 | db_name("repository"), db_name("repository")); |
| 129 | 136 | db_multi_exec( |
| 130 | 137 | "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote |
| 131 | 138 | ); |
| @@ -220,11 +227,11 @@ | ||
| 220 | 227 | |
| 221 | 228 | |
| 222 | 229 | /* |
| 223 | 230 | ** The TEMP table named zTab contains the RIDs for a set of check-in |
| 224 | 231 | ** artifacts. Expand this set (by adding new entries to zTab) to include |
| 225 | -** all other artifacts that are used the set of check-ins in | |
| 232 | +** all other artifacts that are used by the check-ins in | |
| 226 | 233 | ** the original list. |
| 227 | 234 | ** |
| 228 | 235 | ** If the bExclusive flag is true, then the set is only expanded by |
| 229 | 236 | ** artifacts that are used exclusively by the check-ins in the set. |
| 230 | 237 | ** When bExclusive is false, then all artifacts used by the check-ins |
| @@ -427,28 +434,33 @@ | ||
| 427 | 434 | ** COMMAND: purge |
| 428 | 435 | ** |
| 429 | 436 | ** The purge command removes content from a repository and stores that content |
| 430 | 437 | ** in a "graveyard". The graveyard exists so that content can be recovered |
| 431 | 438 | ** using the "fossil purge undo" command. |
| 439 | +** | |
| 440 | +** fossil purge artifact UUID... ?OPTIONS? | |
| 441 | +** | |
| 442 | +** TBD... | |
| 432 | 443 | ** |
| 433 | 444 | ** fossil purge cat UUID... |
| 434 | 445 | ** |
| 435 | 446 | ** Write the content of one or more artifacts in the graveyard onto |
| 436 | 447 | ** standard output. |
| 437 | 448 | ** |
| 438 | -** fossil purge ?checkins? TAGS... ?OPTIONS? | |
| 449 | +** fossil purge checkins TAGS... ?OPTIONS? | |
| 439 | 450 | ** |
| 440 | -** Move the check-ins identified by TAGS and all of their descendants | |
| 441 | -** out of the repository and into the graveyard. The "checkins" | |
| 442 | -** subcommand keyword is optional and can be omitted as long as TAGS | |
| 443 | -** does not conflict with any other subcommand. | |
| 444 | -** | |
| 451 | +** Move the check-ins or branches identified by TAGS and all of | |
| 452 | +** their descendants out of the repository and into the graveyard. | |
| 445 | 453 | ** If TAGS includes a branch name then it means all the check-ins |
| 446 | 454 | ** on the most recent occurrence of that branch. |
| 447 | 455 | ** |
| 448 | 456 | ** --explain Make no changes, but show what would happen. |
| 449 | 457 | ** --dry-run Make no changes. |
| 458 | +** | |
| 459 | +** fossil purge files NAME ... ?OPTIONS? | |
| 460 | +** | |
| 461 | +** TBD... | |
| 450 | 462 | ** |
| 451 | 463 | ** fossil purge list|ls ?-l? |
| 452 | 464 | ** |
| 453 | 465 | ** Show the graveyard of prior purges. The -l option gives more |
| 454 | 466 | ** detail in the output. |
| @@ -455,31 +467,45 @@ | ||
| 455 | 467 | ** |
| 456 | 468 | ** fossil purge obliterate ID... |
| 457 | 469 | ** |
| 458 | 470 | ** Remove one or more purge events from the graveyard. Once a purge |
| 459 | 471 | ** event is obliterated, it can no longer be undone. |
| 472 | +** | |
| 473 | +** fossil purge tickets NAME ... ?OPTIONS? | |
| 474 | +** | |
| 475 | +** TBD... | |
| 460 | 476 | ** |
| 461 | 477 | ** fossil purge undo ID |
| 462 | 478 | ** |
| 463 | 479 | ** Restore the content previously removed by purge ID. |
| 464 | 480 | ** |
| 481 | +** fossil purge wiki NAME ... ?OPTIONS? | |
| 482 | +** | |
| 483 | +** TBD... | |
| 484 | +** | |
| 465 | 485 | ** SUMMARY: |
| 486 | +** fossil purge artifacts UUID.. [OPTIONS] | |
| 466 | 487 | ** fossil purge cat UUID... |
| 467 | -** fossil purge [checkins] TAGS... [--explain] | |
| 488 | +** fossil purge checkins TAGS... [OPTIONS] | |
| 489 | +** fossil purge files FILENAME... [OPTIONS] | |
| 468 | 490 | ** fossil purge list |
| 469 | 491 | ** fossil purge obliterate ID... |
| 492 | +** fossil purge tickets NAME... [OPTIONS] | |
| 470 | 493 | ** fossil purge undo ID |
| 494 | +** fossil purge wiki NAME... [OPTIONS] | |
| 471 | 495 | */ |
| 472 | 496 | void purge_cmd(void){ |
| 473 | 497 | const char *zSubcmd; |
| 474 | 498 | int n; |
| 475 | 499 | Stmt q; |
| 476 | 500 | if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?"); |
| 477 | 501 | zSubcmd = g.argv[2]; |
| 478 | 502 | db_find_and_open_repository(0,0); |
| 479 | 503 | n = (int)strlen(zSubcmd); |
| 480 | - if( strncmp(zSubcmd, "cat", n)==0 ){ | |
| 504 | + if( strncmp(zSubcmd, "artifacts", n)==0 ){ | |
| 505 | + fossil_fatal("not yet implemented...."); | |
| 506 | + }else if( strncmp(zSubcmd, "cat", n)==0 ){ | |
| 481 | 507 | int i, piid; |
| 482 | 508 | Blob content; |
| 483 | 509 | if( g.argc<4 ) usage("cat UUID..."); |
| 484 | 510 | for(i=3; i<g.argc; i++){ |
| 485 | 511 | piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'", |
| @@ -487,12 +513,42 @@ | ||
| 487 | 513 | if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]); |
| 488 | 514 | purge_extract_item(piid, &content); |
| 489 | 515 | blob_write_to_file(&content, "-"); |
| 490 | 516 | blob_reset(&content); |
| 491 | 517 | } |
| 492 | - /* The "checkins" subcommand goes here in alphabetical order, but it must | |
| 493 | - ** be moved to the end since it is the default case */ | |
| 518 | + }else if( strncmp(zSubcmd, "checkins", n)==0 ){ | |
| 519 | + int explainOnly = find_option("explain",0,0)!=0; | |
| 520 | + int dryRun = find_option("dry-run",0,0)!=0; | |
| 521 | + int i; | |
| 522 | + int vid; | |
| 523 | + int nCkin; | |
| 524 | + int nArtifact; | |
| 525 | + verify_all_options(); | |
| 526 | + db_begin_transaction(); | |
| 527 | + if( g.argc<=3 ) usage("checkin TAGS... [OPTIONS]"); | |
| 528 | + db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); | |
| 529 | + for(i=3; i<g.argc; i++){ | |
| 530 | + int r = name_to_typed_rid(g.argv[i], "br"); | |
| 531 | + compute_descendants(r, 1000000000); | |
| 532 | + } | |
| 533 | + vid = db_lget_int("checkout",0); | |
| 534 | + if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){ | |
| 535 | + fossil_fatal("cannot purge the current checkout"); | |
| 536 | + } | |
| 537 | + nCkin = db_int(0, "SELECT count(*) FROM ok"); | |
| 538 | + find_checkin_associates("ok", 1); | |
| 539 | + nArtifact = db_int(0, "SELECT count(*) FROM ok"); | |
| 540 | + describe_artifacts_to_stdout("IN ok", 0); | |
| 541 | + if( !explainOnly ){ | |
| 542 | + int peid = purge_artifact_list("ok","",PURGE_MOVETO_GRAVEYARD); | |
| 543 | + fossil_print("%d check-ins and %d artifacts purged.\n", nCkin, nArtifact); | |
| 544 | + fossil_print("undoable using \"%s purge undo %d\".\n", | |
| 545 | + g.nameOfExe, peid); | |
| 546 | + } | |
| 547 | + db_end_transaction(explainOnly||dryRun); | |
| 548 | + }else if( strncmp(zSubcmd, "files", n)==0 ){ | |
| 549 | + fossil_fatal("not yet implemented...."); | |
| 494 | 550 | }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){ |
| 495 | 551 | int showDetail = find_option("l","l",0)!=0; |
| 496 | 552 | if( !db_table_exists("repository","purgeevent") ) return; |
| 497 | 553 | db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())" |
| 498 | 554 | " FROM purgeevent"); |
| @@ -517,10 +573,12 @@ | ||
| 517 | 573 | "DELETE FROM purgeitem WHERE peid=%d;", |
| 518 | 574 | peid, peid |
| 519 | 575 | ); |
| 520 | 576 | } |
| 521 | 577 | db_end_transaction(0); |
| 578 | + }else if( strncmp(zSubcmd, "tickets", n)==0 ){ | |
| 579 | + fossil_fatal("not yet implemented...."); | |
| 522 | 580 | }else if( strncmp(zSubcmd, "undo", n)==0 ){ |
| 523 | 581 | int peid; |
| 524 | 582 | if( g.argc!=4 ) usage("undo ID"); |
| 525 | 583 | peid = atoi(g.argv[3]); |
| 526 | 584 | db_begin_transaction(); |
| @@ -543,40 +601,13 @@ | ||
| 543 | 601 | purge_item_resurrect(0, 0); |
| 544 | 602 | manifest_crosslink_end(0); |
| 545 | 603 | db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid); |
| 546 | 604 | db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid); |
| 547 | 605 | db_end_transaction(0); |
| 548 | - }else{ | |
| 549 | - /* The "checkins" command is the default and so must occur last */ | |
| 550 | - int explainOnly = find_option("explain",0,0)!=0; | |
| 551 | - int dryRun = find_option("dry-run",0,0)!=0; | |
| 552 | - int i; | |
| 553 | - int vid; | |
| 554 | - int nCkin; | |
| 555 | - int nArtifact; | |
| 556 | - verify_all_options(); | |
| 557 | - db_begin_transaction(); | |
| 558 | - i = strncmp(zSubcmd,"checkins",n)==0 ? 3 : 2; | |
| 559 | - if( i>=g.argc ) usage("[checkin] TAGS... [--explain]"); | |
| 560 | - db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); | |
| 561 | - for(; i<g.argc; i++){ | |
| 562 | - int r = name_to_typed_rid(g.argv[i], "br"); | |
| 563 | - compute_descendants(r, 1000000000); | |
| 564 | - } | |
| 565 | - vid = db_lget_int("checkout",0); | |
| 566 | - if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){ | |
| 567 | - fossil_fatal("cannot purge the current checkout"); | |
| 568 | - } | |
| 569 | - nCkin = db_int(0, "SELECT count(*) FROM ok"); | |
| 570 | - find_checkin_associates("ok", 1); | |
| 571 | - nArtifact = db_int(0, "SELECT count(*) FROM ok"); | |
| 572 | - if( explainOnly ){ | |
| 573 | - describe_artifacts_to_stdout("IN ok", 0); | |
| 574 | - }else{ | |
| 575 | - int peid = purge_artifact_list("ok","",1); | |
| 576 | - fossil_print("%d check-ins and %d artifacts purged.\n", nCkin, nArtifact); | |
| 577 | - fossil_print("undoable using \"%s purge undo %d\".\n", | |
| 578 | - g.nameOfExe, peid); | |
| 579 | - } | |
| 580 | - db_end_transaction(explainOnly||dryRun); | |
| 606 | + }else if( strncmp(zSubcmd, "wiki", n)==0 ){ | |
| 607 | + fossil_fatal("not yet implemented...."); | |
| 608 | + }else{ | |
| 609 | + fossil_fatal("unknown subcommand \"%s\".\n" | |
| 610 | + "should be one of: cat, checkins, files, list, obliterate," | |
| 611 | + " tickets, undo, wiki", zSubcmd); | |
| 581 | 612 | } |
| 582 | 613 | } |
| 583 | 614 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | #include "config.h" |
| 23 | #include "purge.h" |
| 24 | #include <assert.h> |
| 25 | |
| 26 | /* |
| 27 | ** SQL code used to initialize the schema of a bundle. |
| 28 | ** |
| 29 | ** The purgeevent table contains one entry for each purge event. For each |
| 30 | ** purge event, multiple artifacts might have been removed. Each removed |
| 31 | ** artifact is stored as an entry in the purgeitem table. |
| 32 | ** |
| @@ -52,10 +52,17 @@ | |
| 52 | @ desc TEXT, -- Brief description of this artifact |
| 53 | @ data BLOB -- Compressed artifact content |
| 54 | @ ); |
| 55 | ; |
| 56 | |
| 57 | /* |
| 58 | ** This routine purges multiple artifacts from the repository, transfering |
| 59 | ** those artifacts into the PURGEITEM table. |
| 60 | ** |
| 61 | ** Prior to invoking this routine, the caller must create a (TEMP) table |
| @@ -81,17 +88,17 @@ | |
| 81 | ** (j) TICKETCHNG |
| 82 | ** (7) If any ticket artifacts were removed (6j) then rebuild the |
| 83 | ** corresponding ticket entries. Possibly remove entries from |
| 84 | ** the ticket table. |
| 85 | ** |
| 86 | ** Stops 1-4 (saving the purged artifacts into the graveyard) are only |
| 87 | ** undertaken if the moveToGraveyard flag is true. |
| 88 | */ |
| 89 | int purge_artifact_list( |
| 90 | const char *zTab, /* TEMP table containing list of RIDS to be purged */ |
| 91 | const char *zNote, /* Text of the purgeevent.pnotes field */ |
| 92 | int moveToGraveyard /* Move purged artifacts into the graveyard */ |
| 93 | ){ |
| 94 | int peid = 0; /* New purgeevent ID */ |
| 95 | Stmt q; /* General-use prepared statement */ |
| 96 | char *z; |
| 97 | |
| @@ -121,11 +128,11 @@ | |
| 121 | } |
| 122 | db_finalize(&q); |
| 123 | |
| 124 | /* Construct the graveyard and copy the artifacts to be purged into the |
| 125 | ** graveyard */ |
| 126 | if( moveToGraveyard ){ |
| 127 | db_multi_exec(zPurgeInit /*works-like:"%w%w"*/, |
| 128 | db_name("repository"), db_name("repository")); |
| 129 | db_multi_exec( |
| 130 | "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote |
| 131 | ); |
| @@ -220,11 +227,11 @@ | |
| 220 | |
| 221 | |
| 222 | /* |
| 223 | ** The TEMP table named zTab contains the RIDs for a set of check-in |
| 224 | ** artifacts. Expand this set (by adding new entries to zTab) to include |
| 225 | ** all other artifacts that are used the set of check-ins in |
| 226 | ** the original list. |
| 227 | ** |
| 228 | ** If the bExclusive flag is true, then the set is only expanded by |
| 229 | ** artifacts that are used exclusively by the check-ins in the set. |
| 230 | ** When bExclusive is false, then all artifacts used by the check-ins |
| @@ -427,28 +434,33 @@ | |
| 427 | ** COMMAND: purge |
| 428 | ** |
| 429 | ** The purge command removes content from a repository and stores that content |
| 430 | ** in a "graveyard". The graveyard exists so that content can be recovered |
| 431 | ** using the "fossil purge undo" command. |
| 432 | ** |
| 433 | ** fossil purge cat UUID... |
| 434 | ** |
| 435 | ** Write the content of one or more artifacts in the graveyard onto |
| 436 | ** standard output. |
| 437 | ** |
| 438 | ** fossil purge ?checkins? TAGS... ?OPTIONS? |
| 439 | ** |
| 440 | ** Move the check-ins identified by TAGS and all of their descendants |
| 441 | ** out of the repository and into the graveyard. The "checkins" |
| 442 | ** subcommand keyword is optional and can be omitted as long as TAGS |
| 443 | ** does not conflict with any other subcommand. |
| 444 | ** |
| 445 | ** If TAGS includes a branch name then it means all the check-ins |
| 446 | ** on the most recent occurrence of that branch. |
| 447 | ** |
| 448 | ** --explain Make no changes, but show what would happen. |
| 449 | ** --dry-run Make no changes. |
| 450 | ** |
| 451 | ** fossil purge list|ls ?-l? |
| 452 | ** |
| 453 | ** Show the graveyard of prior purges. The -l option gives more |
| 454 | ** detail in the output. |
| @@ -455,31 +467,45 @@ | |
| 455 | ** |
| 456 | ** fossil purge obliterate ID... |
| 457 | ** |
| 458 | ** Remove one or more purge events from the graveyard. Once a purge |
| 459 | ** event is obliterated, it can no longer be undone. |
| 460 | ** |
| 461 | ** fossil purge undo ID |
| 462 | ** |
| 463 | ** Restore the content previously removed by purge ID. |
| 464 | ** |
| 465 | ** SUMMARY: |
| 466 | ** fossil purge cat UUID... |
| 467 | ** fossil purge [checkins] TAGS... [--explain] |
| 468 | ** fossil purge list |
| 469 | ** fossil purge obliterate ID... |
| 470 | ** fossil purge undo ID |
| 471 | */ |
| 472 | void purge_cmd(void){ |
| 473 | const char *zSubcmd; |
| 474 | int n; |
| 475 | Stmt q; |
| 476 | if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?"); |
| 477 | zSubcmd = g.argv[2]; |
| 478 | db_find_and_open_repository(0,0); |
| 479 | n = (int)strlen(zSubcmd); |
| 480 | if( strncmp(zSubcmd, "cat", n)==0 ){ |
| 481 | int i, piid; |
| 482 | Blob content; |
| 483 | if( g.argc<4 ) usage("cat UUID..."); |
| 484 | for(i=3; i<g.argc; i++){ |
| 485 | piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'", |
| @@ -487,12 +513,42 @@ | |
| 487 | if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]); |
| 488 | purge_extract_item(piid, &content); |
| 489 | blob_write_to_file(&content, "-"); |
| 490 | blob_reset(&content); |
| 491 | } |
| 492 | /* The "checkins" subcommand goes here in alphabetical order, but it must |
| 493 | ** be moved to the end since it is the default case */ |
| 494 | }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){ |
| 495 | int showDetail = find_option("l","l",0)!=0; |
| 496 | if( !db_table_exists("repository","purgeevent") ) return; |
| 497 | db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())" |
| 498 | " FROM purgeevent"); |
| @@ -517,10 +573,12 @@ | |
| 517 | "DELETE FROM purgeitem WHERE peid=%d;", |
| 518 | peid, peid |
| 519 | ); |
| 520 | } |
| 521 | db_end_transaction(0); |
| 522 | }else if( strncmp(zSubcmd, "undo", n)==0 ){ |
| 523 | int peid; |
| 524 | if( g.argc!=4 ) usage("undo ID"); |
| 525 | peid = atoi(g.argv[3]); |
| 526 | db_begin_transaction(); |
| @@ -543,40 +601,13 @@ | |
| 543 | purge_item_resurrect(0, 0); |
| 544 | manifest_crosslink_end(0); |
| 545 | db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid); |
| 546 | db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid); |
| 547 | db_end_transaction(0); |
| 548 | }else{ |
| 549 | /* The "checkins" command is the default and so must occur last */ |
| 550 | int explainOnly = find_option("explain",0,0)!=0; |
| 551 | int dryRun = find_option("dry-run",0,0)!=0; |
| 552 | int i; |
| 553 | int vid; |
| 554 | int nCkin; |
| 555 | int nArtifact; |
| 556 | verify_all_options(); |
| 557 | db_begin_transaction(); |
| 558 | i = strncmp(zSubcmd,"checkins",n)==0 ? 3 : 2; |
| 559 | if( i>=g.argc ) usage("[checkin] TAGS... [--explain]"); |
| 560 | db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); |
| 561 | for(; i<g.argc; i++){ |
| 562 | int r = name_to_typed_rid(g.argv[i], "br"); |
| 563 | compute_descendants(r, 1000000000); |
| 564 | } |
| 565 | vid = db_lget_int("checkout",0); |
| 566 | if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){ |
| 567 | fossil_fatal("cannot purge the current checkout"); |
| 568 | } |
| 569 | nCkin = db_int(0, "SELECT count(*) FROM ok"); |
| 570 | find_checkin_associates("ok", 1); |
| 571 | nArtifact = db_int(0, "SELECT count(*) FROM ok"); |
| 572 | if( explainOnly ){ |
| 573 | describe_artifacts_to_stdout("IN ok", 0); |
| 574 | }else{ |
| 575 | int peid = purge_artifact_list("ok","",1); |
| 576 | fossil_print("%d check-ins and %d artifacts purged.\n", nCkin, nArtifact); |
| 577 | fossil_print("undoable using \"%s purge undo %d\".\n", |
| 578 | g.nameOfExe, peid); |
| 579 | } |
| 580 | db_end_transaction(explainOnly||dryRun); |
| 581 | } |
| 582 | } |
| 583 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | #include "config.h" |
| 23 | #include "purge.h" |
| 24 | #include <assert.h> |
| 25 | |
| 26 | /* |
| 27 | ** SQL code used to initialize the schema of the graveyard. |
| 28 | ** |
| 29 | ** The purgeevent table contains one entry for each purge event. For each |
| 30 | ** purge event, multiple artifacts might have been removed. Each removed |
| 31 | ** artifact is stored as an entry in the purgeitem table. |
| 32 | ** |
| @@ -52,10 +52,17 @@ | |
| 52 | @ desc TEXT, -- Brief description of this artifact |
| 53 | @ data BLOB -- Compressed artifact content |
| 54 | @ ); |
| 55 | ; |
| 56 | |
| 57 | /* |
| 58 | ** Flags for the purge_artifact_list() function. |
| 59 | */ |
| 60 | #if INTERFACE |
| 61 | #define PURGE_MOVETO_GRAVEYARD 0x0001 /* Move artifacts in graveyard */ |
| 62 | #endif |
| 63 | |
| 64 | /* |
| 65 | ** This routine purges multiple artifacts from the repository, transfering |
| 66 | ** those artifacts into the PURGEITEM table. |
| 67 | ** |
| 68 | ** Prior to invoking this routine, the caller must create a (TEMP) table |
| @@ -81,17 +88,17 @@ | |
| 88 | ** (j) TICKETCHNG |
| 89 | ** (7) If any ticket artifacts were removed (6j) then rebuild the |
| 90 | ** corresponding ticket entries. Possibly remove entries from |
| 91 | ** the ticket table. |
| 92 | ** |
| 93 | ** Steps 1-4 (saving the purged artifacts into the graveyard) are only |
| 94 | ** undertaken if the moveToGraveyard flag is true. |
| 95 | */ |
| 96 | int purge_artifact_list( |
| 97 | const char *zTab, /* TEMP table containing list of RIDS to be purged */ |
| 98 | const char *zNote, /* Text of the purgeevent.pnotes field */ |
| 99 | unsigned purgeFlags /* zero or more PURGE_* flags */ |
| 100 | ){ |
| 101 | int peid = 0; /* New purgeevent ID */ |
| 102 | Stmt q; /* General-use prepared statement */ |
| 103 | char *z; |
| 104 | |
| @@ -121,11 +128,11 @@ | |
| 128 | } |
| 129 | db_finalize(&q); |
| 130 | |
| 131 | /* Construct the graveyard and copy the artifacts to be purged into the |
| 132 | ** graveyard */ |
| 133 | if( purgeFlags & PURGE_MOVETO_GRAVEYARD ){ |
| 134 | db_multi_exec(zPurgeInit /*works-like:"%w%w"*/, |
| 135 | db_name("repository"), db_name("repository")); |
| 136 | db_multi_exec( |
| 137 | "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote |
| 138 | ); |
| @@ -220,11 +227,11 @@ | |
| 227 | |
| 228 | |
| 229 | /* |
| 230 | ** The TEMP table named zTab contains the RIDs for a set of check-in |
| 231 | ** artifacts. Expand this set (by adding new entries to zTab) to include |
| 232 | ** all other artifacts that are used by the check-ins in |
| 233 | ** the original list. |
| 234 | ** |
| 235 | ** If the bExclusive flag is true, then the set is only expanded by |
| 236 | ** artifacts that are used exclusively by the check-ins in the set. |
| 237 | ** When bExclusive is false, then all artifacts used by the check-ins |
| @@ -427,28 +434,33 @@ | |
| 434 | ** COMMAND: purge |
| 435 | ** |
| 436 | ** The purge command removes content from a repository and stores that content |
| 437 | ** in a "graveyard". The graveyard exists so that content can be recovered |
| 438 | ** using the "fossil purge undo" command. |
| 439 | ** |
| 440 | ** fossil purge artifact UUID... ?OPTIONS? |
| 441 | ** |
| 442 | ** TBD... |
| 443 | ** |
| 444 | ** fossil purge cat UUID... |
| 445 | ** |
| 446 | ** Write the content of one or more artifacts in the graveyard onto |
| 447 | ** standard output. |
| 448 | ** |
| 449 | ** fossil purge checkins TAGS... ?OPTIONS? |
| 450 | ** |
| 451 | ** Move the check-ins or branches identified by TAGS and all of |
| 452 | ** their descendants out of the repository and into the graveyard. |
| 453 | ** If TAGS includes a branch name then it means all the check-ins |
| 454 | ** on the most recent occurrence of that branch. |
| 455 | ** |
| 456 | ** --explain Make no changes, but show what would happen. |
| 457 | ** --dry-run Make no changes. |
| 458 | ** |
| 459 | ** fossil purge files NAME ... ?OPTIONS? |
| 460 | ** |
| 461 | ** TBD... |
| 462 | ** |
| 463 | ** fossil purge list|ls ?-l? |
| 464 | ** |
| 465 | ** Show the graveyard of prior purges. The -l option gives more |
| 466 | ** detail in the output. |
| @@ -455,31 +467,45 @@ | |
| 467 | ** |
| 468 | ** fossil purge obliterate ID... |
| 469 | ** |
| 470 | ** Remove one or more purge events from the graveyard. Once a purge |
| 471 | ** event is obliterated, it can no longer be undone. |
| 472 | ** |
| 473 | ** fossil purge tickets NAME ... ?OPTIONS? |
| 474 | ** |
| 475 | ** TBD... |
| 476 | ** |
| 477 | ** fossil purge undo ID |
| 478 | ** |
| 479 | ** Restore the content previously removed by purge ID. |
| 480 | ** |
| 481 | ** fossil purge wiki NAME ... ?OPTIONS? |
| 482 | ** |
| 483 | ** TBD... |
| 484 | ** |
| 485 | ** SUMMARY: |
| 486 | ** fossil purge artifacts UUID.. [OPTIONS] |
| 487 | ** fossil purge cat UUID... |
| 488 | ** fossil purge checkins TAGS... [OPTIONS] |
| 489 | ** fossil purge files FILENAME... [OPTIONS] |
| 490 | ** fossil purge list |
| 491 | ** fossil purge obliterate ID... |
| 492 | ** fossil purge tickets NAME... [OPTIONS] |
| 493 | ** fossil purge undo ID |
| 494 | ** fossil purge wiki NAME... [OPTIONS] |
| 495 | */ |
| 496 | void purge_cmd(void){ |
| 497 | const char *zSubcmd; |
| 498 | int n; |
| 499 | Stmt q; |
| 500 | if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?"); |
| 501 | zSubcmd = g.argv[2]; |
| 502 | db_find_and_open_repository(0,0); |
| 503 | n = (int)strlen(zSubcmd); |
| 504 | if( strncmp(zSubcmd, "artifacts", n)==0 ){ |
| 505 | fossil_fatal("not yet implemented...."); |
| 506 | }else if( strncmp(zSubcmd, "cat", n)==0 ){ |
| 507 | int i, piid; |
| 508 | Blob content; |
| 509 | if( g.argc<4 ) usage("cat UUID..."); |
| 510 | for(i=3; i<g.argc; i++){ |
| 511 | piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'", |
| @@ -487,12 +513,42 @@ | |
| 513 | if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]); |
| 514 | purge_extract_item(piid, &content); |
| 515 | blob_write_to_file(&content, "-"); |
| 516 | blob_reset(&content); |
| 517 | } |
| 518 | }else if( strncmp(zSubcmd, "checkins", n)==0 ){ |
| 519 | int explainOnly = find_option("explain",0,0)!=0; |
| 520 | int dryRun = find_option("dry-run",0,0)!=0; |
| 521 | int i; |
| 522 | int vid; |
| 523 | int nCkin; |
| 524 | int nArtifact; |
| 525 | verify_all_options(); |
| 526 | db_begin_transaction(); |
| 527 | if( g.argc<=3 ) usage("checkin TAGS... [OPTIONS]"); |
| 528 | db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); |
| 529 | for(i=3; i<g.argc; i++){ |
| 530 | int r = name_to_typed_rid(g.argv[i], "br"); |
| 531 | compute_descendants(r, 1000000000); |
| 532 | } |
| 533 | vid = db_lget_int("checkout",0); |
| 534 | if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){ |
| 535 | fossil_fatal("cannot purge the current checkout"); |
| 536 | } |
| 537 | nCkin = db_int(0, "SELECT count(*) FROM ok"); |
| 538 | find_checkin_associates("ok", 1); |
| 539 | nArtifact = db_int(0, "SELECT count(*) FROM ok"); |
| 540 | describe_artifacts_to_stdout("IN ok", 0); |
| 541 | if( !explainOnly ){ |
| 542 | int peid = purge_artifact_list("ok","",PURGE_MOVETO_GRAVEYARD); |
| 543 | fossil_print("%d check-ins and %d artifacts purged.\n", nCkin, nArtifact); |
| 544 | fossil_print("undoable using \"%s purge undo %d\".\n", |
| 545 | g.nameOfExe, peid); |
| 546 | } |
| 547 | db_end_transaction(explainOnly||dryRun); |
| 548 | }else if( strncmp(zSubcmd, "files", n)==0 ){ |
| 549 | fossil_fatal("not yet implemented...."); |
| 550 | }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){ |
| 551 | int showDetail = find_option("l","l",0)!=0; |
| 552 | if( !db_table_exists("repository","purgeevent") ) return; |
| 553 | db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())" |
| 554 | " FROM purgeevent"); |
| @@ -517,10 +573,12 @@ | |
| 573 | "DELETE FROM purgeitem WHERE peid=%d;", |
| 574 | peid, peid |
| 575 | ); |
| 576 | } |
| 577 | db_end_transaction(0); |
| 578 | }else if( strncmp(zSubcmd, "tickets", n)==0 ){ |
| 579 | fossil_fatal("not yet implemented...."); |
| 580 | }else if( strncmp(zSubcmd, "undo", n)==0 ){ |
| 581 | int peid; |
| 582 | if( g.argc!=4 ) usage("undo ID"); |
| 583 | peid = atoi(g.argv[3]); |
| 584 | db_begin_transaction(); |
| @@ -543,40 +601,13 @@ | |
| 601 | purge_item_resurrect(0, 0); |
| 602 | manifest_crosslink_end(0); |
| 603 | db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid); |
| 604 | db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid); |
| 605 | db_end_transaction(0); |
| 606 | }else if( strncmp(zSubcmd, "wiki", n)==0 ){ |
| 607 | fossil_fatal("not yet implemented...."); |
| 608 | }else{ |
| 609 | fossil_fatal("unknown subcommand \"%s\".\n" |
| 610 | "should be one of: cat, checkins, files, list, obliterate," |
| 611 | " tickets, undo, wiki", zSubcmd); |
| 612 | } |
| 613 | } |
| 614 |