Fossil SCM
Enhance the "fossil purge" command to allow it to specify individual artifacts and files to be purged.
Commit
263fd2dab2ec443af58b7d835be75dad2274622b
Parent
45fcac621f1e3c4…
2 files changed
+8
-3
+113
-49
+8
-3
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -934,16 +934,21 @@ | ||
| 934 | 934 | "UPDATE description SET isPrivate=1 WHERE rid IN private" |
| 935 | 935 | ); |
| 936 | 936 | } |
| 937 | 937 | |
| 938 | 938 | /* |
| 939 | -** Print the content of the description table on stdout | |
| 939 | +** Print the content of the description table on stdout. | |
| 940 | +** | |
| 941 | +** The description table is computed using the WHERE clause zWhere if | |
| 942 | +** the zWhere parameter is not NULL. If zWhere is NULL, then this | |
| 943 | +** routine assumes that the description table already exists and is | |
| 944 | +** populated and merely prints the contents. | |
| 940 | 945 | */ |
| 941 | 946 | int describe_artifacts_to_stdout(const char *zWhere, const char *zLabel){ |
| 942 | 947 | Stmt q; |
| 943 | 948 | int cnt = 0; |
| 944 | - describe_artifacts(zWhere); | |
| 949 | + if( zWhere!=0 ) describe_artifacts(zWhere); | |
| 945 | 950 | db_prepare(&q, |
| 946 | 951 | "SELECT uuid, summary, isPrivate\n" |
| 947 | 952 | " FROM description\n" |
| 948 | 953 | " ORDER BY ctime, type;" |
| 949 | 954 | ); |
| @@ -956,11 +961,11 @@ | ||
| 956 | 961 | if( db_column_int(&q,2) ) fossil_print(" (unpublished)"); |
| 957 | 962 | fossil_print("\n"); |
| 958 | 963 | cnt++; |
| 959 | 964 | } |
| 960 | 965 | db_finalize(&q); |
| 961 | - db_multi_exec("DELETE FROM description;"); | |
| 966 | + if( zWhere!=0 ) db_multi_exec("DELETE FROM description;"); | |
| 962 | 967 | return cnt; |
| 963 | 968 | } |
| 964 | 969 | |
| 965 | 970 | /* |
| 966 | 971 | ** COMMAND: test-describe-artifacts |
| 967 | 972 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -934,16 +934,21 @@ | |
| 934 | "UPDATE description SET isPrivate=1 WHERE rid IN private" |
| 935 | ); |
| 936 | } |
| 937 | |
| 938 | /* |
| 939 | ** Print the content of the description table on stdout |
| 940 | */ |
| 941 | int describe_artifacts_to_stdout(const char *zWhere, const char *zLabel){ |
| 942 | Stmt q; |
| 943 | int cnt = 0; |
| 944 | describe_artifacts(zWhere); |
| 945 | db_prepare(&q, |
| 946 | "SELECT uuid, summary, isPrivate\n" |
| 947 | " FROM description\n" |
| 948 | " ORDER BY ctime, type;" |
| 949 | ); |
| @@ -956,11 +961,11 @@ | |
| 956 | if( db_column_int(&q,2) ) fossil_print(" (unpublished)"); |
| 957 | fossil_print("\n"); |
| 958 | cnt++; |
| 959 | } |
| 960 | db_finalize(&q); |
| 961 | db_multi_exec("DELETE FROM description;"); |
| 962 | return cnt; |
| 963 | } |
| 964 | |
| 965 | /* |
| 966 | ** COMMAND: test-describe-artifacts |
| 967 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -934,16 +934,21 @@ | |
| 934 | "UPDATE description SET isPrivate=1 WHERE rid IN private" |
| 935 | ); |
| 936 | } |
| 937 | |
| 938 | /* |
| 939 | ** Print the content of the description table on stdout. |
| 940 | ** |
| 941 | ** The description table is computed using the WHERE clause zWhere if |
| 942 | ** the zWhere parameter is not NULL. If zWhere is NULL, then this |
| 943 | ** routine assumes that the description table already exists and is |
| 944 | ** populated and merely prints the contents. |
| 945 | */ |
| 946 | int describe_artifacts_to_stdout(const char *zWhere, const char *zLabel){ |
| 947 | Stmt q; |
| 948 | int cnt = 0; |
| 949 | if( zWhere!=0 ) describe_artifacts(zWhere); |
| 950 | db_prepare(&q, |
| 951 | "SELECT uuid, summary, isPrivate\n" |
| 952 | " FROM description\n" |
| 953 | " ORDER BY ctime, type;" |
| 954 | ); |
| @@ -956,11 +961,11 @@ | |
| 961 | if( db_column_int(&q,2) ) fossil_print(" (unpublished)"); |
| 962 | fossil_print("\n"); |
| 963 | cnt++; |
| 964 | } |
| 965 | db_finalize(&q); |
| 966 | if( zWhere!=0 ) db_multi_exec("DELETE FROM description;"); |
| 967 | return cnt; |
| 968 | } |
| 969 | |
| 970 | /* |
| 971 | ** COMMAND: test-describe-artifacts |
| 972 |
+113
-49
| --- src/purge.c | ||
| +++ src/purge.c | ||
| @@ -57,10 +57,12 @@ | ||
| 57 | 57 | /* |
| 58 | 58 | ** Flags for the purge_artifact_list() function. |
| 59 | 59 | */ |
| 60 | 60 | #if INTERFACE |
| 61 | 61 | #define PURGE_MOVETO_GRAVEYARD 0x0001 /* Move artifacts in graveyard */ |
| 62 | +#define PURGE_EXPLAIN_ONLY 0x0002 /* Show what would have happened */ | |
| 63 | +#define PURGE_PRINT_SUMMARY 0x0004 /* Print a summary report at end */ | |
| 62 | 64 | #endif |
| 63 | 65 | |
| 64 | 66 | /* |
| 65 | 67 | ** This routine purges multiple artifacts from the repository, transfering |
| 66 | 68 | ** those artifacts into the PURGEITEM table. |
| @@ -105,10 +107,20 @@ | ||
| 105 | 107 | assert( g.repositoryOpen ); /* Main database must already be open */ |
| 106 | 108 | db_begin_transaction(); |
| 107 | 109 | z = sqlite3_mprintf("IN \"%w\"", zTab); |
| 108 | 110 | describe_artifacts(z); |
| 109 | 111 | sqlite3_free(z); |
| 112 | + describe_artifacts_to_stdout(0, 0); | |
| 113 | + | |
| 114 | + /* The explain-only flags causes this routine to list the artifacts | |
| 115 | + ** that would have been purged but to not actually make any changes | |
| 116 | + ** to the repository. | |
| 117 | + */ | |
| 118 | + if( purgeFlags & PURGE_EXPLAIN_ONLY ){ | |
| 119 | + db_end_transaction(0); | |
| 120 | + return 0; | |
| 121 | + } | |
| 110 | 122 | |
| 111 | 123 | /* Make sure we are not removing a manifest that is the baseline of some |
| 112 | 124 | ** manifest that is being left behind. This step is not strictly necessary. |
| 113 | 125 | ** is is just a safety check. */ |
| 114 | 126 | if( purge_baseline_out_from_under_delta(zTab) ){ |
| @@ -196,10 +208,17 @@ | ||
| 196 | 208 | db_finalize(&q); |
| 197 | 209 | /* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */ |
| 198 | 210 | |
| 199 | 211 | /* Mission accomplished */ |
| 200 | 212 | db_end_transaction(0); |
| 213 | + | |
| 214 | + if( purgeFlags & PURGE_PRINT_SUMMARY ){ | |
| 215 | + fossil_print("%d artifacts purged\n", | |
| 216 | + db_int(0, "SELECT count(*) FROM \"%w\";", zTab)); | |
| 217 | + fossil_print("undoable using \"%s purge undo %d\".\n", | |
| 218 | + g.nameOfExe, peid); | |
| 219 | + } | |
| 201 | 220 | return peid; |
| 202 | 221 | } |
| 203 | 222 | |
| 204 | 223 | /* |
| 205 | 224 | ** The TEMP table named zTab contains RIDs for a set of check-ins. |
| @@ -435,13 +454,18 @@ | ||
| 435 | 454 | ** |
| 436 | 455 | ** The purge command removes content from a repository and stores that content |
| 437 | 456 | ** in a "graveyard". The graveyard exists so that content can be recovered |
| 438 | 457 | ** using the "fossil purge undo" command. |
| 439 | 458 | ** |
| 440 | -** fossil purge artifact UUID... ?OPTIONS? | |
| 459 | +** ==== WARNING: This command can potentially destroy historical data and ==== | |
| 460 | +** ==== leave your repository in a goofy state. Know what you are doing! ==== | |
| 461 | +** ==== Make a backup of your repository before using this command! ==== | |
| 462 | +** | |
| 463 | +** fossil purge artifacts UUID... ?OPTIONS? | |
| 441 | 464 | ** |
| 442 | -** TBD... | |
| 465 | +** Move arbitrary artifacts identified by the UUID list into the | |
| 466 | +** graveyard. | |
| 443 | 467 | ** |
| 444 | 468 | ** fossil purge cat UUID... |
| 445 | 469 | ** |
| 446 | 470 | ** Write the content of one or more artifacts in the graveyard onto |
| 447 | 471 | ** standard output. |
| @@ -451,26 +475,27 @@ | ||
| 451 | 475 | ** Move the check-ins or branches identified by TAGS and all of |
| 452 | 476 | ** their descendants out of the repository and into the graveyard. |
| 453 | 477 | ** If TAGS includes a branch name then it means all the check-ins |
| 454 | 478 | ** on the most recent occurrence of that branch. |
| 455 | 479 | ** |
| 456 | -** --explain Make no changes, but show what would happen. | |
| 457 | -** --dry-run Make no changes. | |
| 458 | -** | |
| 459 | 480 | ** fossil purge files NAME ... ?OPTIONS? |
| 460 | 481 | ** |
| 461 | -** TBD... | |
| 482 | +** Move all instances of files called NAME into the graveyard. | |
| 483 | +** NAME should be the name of the file relative to the root of the | |
| 484 | +** repository. If NAME is a directory, then all files within that | |
| 485 | +** directory are moved. | |
| 462 | 486 | ** |
| 463 | 487 | ** fossil purge list|ls ?-l? |
| 464 | 488 | ** |
| 465 | 489 | ** Show the graveyard of prior purges. The -l option gives more |
| 466 | 490 | ** detail in the output. |
| 467 | 491 | ** |
| 468 | -** fossil purge obliterate ID... | |
| 492 | +** fossil purge obliterate ID... ?--force? | |
| 469 | 493 | ** |
| 470 | 494 | ** Remove one or more purge events from the graveyard. Once a purge |
| 471 | -** event is obliterated, it can no longer be undone. | |
| 495 | +** event is obliterated, it can no longer be undone. The --force | |
| 496 | +** option suppresses the confirmation prompt. | |
| 472 | 497 | ** |
| 473 | 498 | ** fossil purge tickets NAME ... ?OPTIONS? |
| 474 | 499 | ** |
| 475 | 500 | ** TBD... |
| 476 | 501 | ** |
| @@ -480,10 +505,15 @@ | ||
| 480 | 505 | ** |
| 481 | 506 | ** fossil purge wiki NAME ... ?OPTIONS? |
| 482 | 507 | ** |
| 483 | 508 | ** TBD... |
| 484 | 509 | ** |
| 510 | +** COMMON OPTIONS: | |
| 511 | +** | |
| 512 | +** --explain Make no changes, but show what would happen. | |
| 513 | +** --dry-run An alias for --explain | |
| 514 | +** | |
| 485 | 515 | ** SUMMARY: |
| 486 | 516 | ** fossil purge artifacts UUID.. [OPTIONS] |
| 487 | 517 | ** fossil purge cat UUID... |
| 488 | 518 | ** fossil purge checkins TAGS... [OPTIONS] |
| 489 | 519 | ** fossil purge files FILENAME... [OPTIONS] |
| @@ -492,19 +522,34 @@ | ||
| 492 | 522 | ** fossil purge tickets NAME... [OPTIONS] |
| 493 | 523 | ** fossil purge undo ID |
| 494 | 524 | ** fossil purge wiki NAME... [OPTIONS] |
| 495 | 525 | */ |
| 496 | 526 | void purge_cmd(void){ |
| 527 | + int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY; | |
| 497 | 528 | const char *zSubcmd; |
| 498 | 529 | int n; |
| 530 | + int i; | |
| 499 | 531 | Stmt q; |
| 532 | + | |
| 500 | 533 | if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?"); |
| 501 | 534 | zSubcmd = g.argv[2]; |
| 502 | 535 | db_find_and_open_repository(0,0); |
| 503 | 536 | n = (int)strlen(zSubcmd); |
| 537 | + if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){ | |
| 538 | + purgeFlags |= PURGE_EXPLAIN_ONLY; | |
| 539 | + } | |
| 504 | 540 | if( strncmp(zSubcmd, "artifacts", n)==0 ){ |
| 505 | - fossil_fatal("not yet implemented...."); | |
| 541 | + verify_all_options(); | |
| 542 | + db_begin_transaction(); | |
| 543 | + db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); | |
| 544 | + for(i=3; i<g.argc; i++){ | |
| 545 | + int r = name_to_typed_rid(g.argv[i], ""); | |
| 546 | + db_multi_exec("INSERT OR IGNORE INTO ok(rid) VALUES(%d);", r); | |
| 547 | + } | |
| 548 | + describe_artifacts_to_stdout("IN ok", 0); | |
| 549 | + purge_artifact_list("ok", "", purgeFlags); | |
| 550 | + db_end_transaction(0); | |
| 506 | 551 | }else if( strncmp(zSubcmd, "cat", n)==0 ){ |
| 507 | 552 | int i, piid; |
| 508 | 553 | Blob content; |
| 509 | 554 | if( g.argc<4 ) usage("cat UUID..."); |
| 510 | 555 | for(i=3; i<g.argc; i++){ |
| @@ -514,41 +559,46 @@ | ||
| 514 | 559 | purge_extract_item(piid, &content); |
| 515 | 560 | blob_write_to_file(&content, "-"); |
| 516 | 561 | blob_reset(&content); |
| 517 | 562 | } |
| 518 | 563 | }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 | 564 | int vid; |
| 523 | - int nCkin; | |
| 524 | - int nArtifact; | |
| 565 | + if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){ | |
| 566 | + purgeFlags |= PURGE_EXPLAIN_ONLY; | |
| 567 | + } | |
| 525 | 568 | verify_all_options(); |
| 526 | 569 | db_begin_transaction(); |
| 527 | - if( g.argc<=3 ) usage("checkin TAGS... [OPTIONS]"); | |
| 570 | + if( g.argc<=3 ) usage("checkins TAGS... [OPTIONS]"); | |
| 528 | 571 | db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); |
| 529 | 572 | for(i=3; i<g.argc; i++){ |
| 530 | 573 | int r = name_to_typed_rid(g.argv[i], "br"); |
| 531 | 574 | compute_descendants(r, 1000000000); |
| 532 | 575 | } |
| 533 | 576 | vid = db_lget_int("checkout",0); |
| 534 | 577 | if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){ |
| 535 | 578 | fossil_fatal("cannot purge the current checkout"); |
| 536 | 579 | } |
| 537 | - nCkin = db_int(0, "SELECT count(*) FROM ok"); | |
| 538 | 580 | 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...."); | |
| 581 | + describe_artifacts_to_stdout("IN ok", 0); | |
| 582 | + purge_artifact_list("ok", "", purgeFlags); | |
| 583 | + db_end_transaction(0); | |
| 584 | + }else if( strncmp(zSubcmd, "files", n)==0 ){ | |
| 585 | + verify_all_options(); | |
| 586 | + db_begin_transaction(); | |
| 587 | + db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); | |
| 588 | + for(i=3; i<g.argc; i++){ | |
| 589 | + db_multi_exec( | |
| 590 | + "INSERT OR IGNORE INTO ok(rid) " | |
| 591 | + " SELECT fid FROM mlink, filename" | |
| 592 | + " WHERE mlink.fnid=filename.fnid" | |
| 593 | + " AND (filename.name=%Q OR filename.name GLOB '%q/*')", | |
| 594 | + g.argv[i], g.argv[i] | |
| 595 | + ); | |
| 596 | + } | |
| 597 | + describe_artifacts_to_stdout("IN ok", 0); | |
| 598 | + purge_artifact_list("ok", "", purgeFlags); | |
| 599 | + db_end_transaction(0); | |
| 550 | 600 | }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){ |
| 551 | 601 | int showDetail = find_option("l","l",0)!=0; |
| 552 | 602 | if( !db_table_exists("repository","purgeevent") ) return; |
| 553 | 603 | db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())" |
| 554 | 604 | " FROM purgeevent"); |
| @@ -559,11 +609,23 @@ | ||
| 559 | 609 | } |
| 560 | 610 | } |
| 561 | 611 | db_finalize(&q); |
| 562 | 612 | }else if( strncmp(zSubcmd, "obliterate", n)==0 ){ |
| 563 | 613 | int i; |
| 614 | + int bForce = find_option("force","f",0)!=0; | |
| 564 | 615 | if( g.argc<4 ) usage("obliterate ID..."); |
| 616 | + if( !bForce ){ | |
| 617 | + Blob ans; | |
| 618 | + char cReply; | |
| 619 | + prompt_user( | |
| 620 | + "Obliterating the graveyard will permanently delete information.\n" | |
| 621 | + "Changes cannot be undone. Continue (y/N)? ", &ans); | |
| 622 | + cReply = blob_str(&ans)[0]; | |
| 623 | + if( cReply!='y' && cReply!='Y' ){ | |
| 624 | + fossil_exit(1); | |
| 625 | + } | |
| 626 | + } | |
| 565 | 627 | db_begin_transaction(); |
| 566 | 628 | for(i=3; i<g.argc; i++){ |
| 567 | 629 | int peid = atoi(g.argv[i]); |
| 568 | 630 | if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){ |
| 569 | 631 | fossil_fatal("no such purge event: %s", g.argv[i]); |
| @@ -579,35 +641,37 @@ | ||
| 579 | 641 | fossil_fatal("not yet implemented...."); |
| 580 | 642 | }else if( strncmp(zSubcmd, "undo", n)==0 ){ |
| 581 | 643 | int peid; |
| 582 | 644 | if( g.argc!=4 ) usage("undo ID"); |
| 583 | 645 | peid = atoi(g.argv[3]); |
| 584 | - db_begin_transaction(); | |
| 585 | - db_multi_exec( | |
| 586 | - "CREATE TEMP TABLE ix(" | |
| 587 | - " piid INTEGER PRIMARY KEY," | |
| 588 | - " srcid INTEGER" | |
| 589 | - ");" | |
| 590 | - "CREATE INDEX ixsrcid ON ix(srcid);" | |
| 591 | - "INSERT INTO ix(piid,srcid) " | |
| 592 | - " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;", | |
| 593 | - peid | |
| 594 | - ); | |
| 595 | - db_multi_exec( | |
| 596 | - "DELETE FROM shun" | |
| 597 | - " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);", | |
| 598 | - peid | |
| 599 | - ); | |
| 600 | - manifest_crosslink_begin(); | |
| 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); | |
| 646 | + if( (purgeFlags & PURGE_EXPLAIN_ONLY)==0 ){ | |
| 647 | + db_begin_transaction(); | |
| 648 | + db_multi_exec( | |
| 649 | + "CREATE TEMP TABLE ix(" | |
| 650 | + " piid INTEGER PRIMARY KEY," | |
| 651 | + " srcid INTEGER" | |
| 652 | + ");" | |
| 653 | + "CREATE INDEX ixsrcid ON ix(srcid);" | |
| 654 | + "INSERT INTO ix(piid,srcid) " | |
| 655 | + " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;", | |
| 656 | + peid | |
| 657 | + ); | |
| 658 | + db_multi_exec( | |
| 659 | + "DELETE FROM shun" | |
| 660 | + " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);", | |
| 661 | + peid | |
| 662 | + ); | |
| 663 | + manifest_crosslink_begin(); | |
| 664 | + purge_item_resurrect(0, 0); | |
| 665 | + manifest_crosslink_end(0); | |
| 666 | + db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid); | |
| 667 | + db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid); | |
| 668 | + db_end_transaction(0); | |
| 669 | + } | |
| 606 | 670 | }else if( strncmp(zSubcmd, "wiki", n)==0 ){ |
| 607 | 671 | fossil_fatal("not yet implemented...."); |
| 608 | 672 | }else{ |
| 609 | 673 | fossil_fatal("unknown subcommand \"%s\".\n" |
| 610 | 674 | "should be one of: cat, checkins, files, list, obliterate," |
| 611 | 675 | " tickets, undo, wiki", zSubcmd); |
| 612 | 676 | } |
| 613 | 677 | } |
| 614 | 678 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -57,10 +57,12 @@ | |
| 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. |
| @@ -105,10 +107,20 @@ | |
| 105 | assert( g.repositoryOpen ); /* Main database must already be open */ |
| 106 | db_begin_transaction(); |
| 107 | z = sqlite3_mprintf("IN \"%w\"", zTab); |
| 108 | describe_artifacts(z); |
| 109 | sqlite3_free(z); |
| 110 | |
| 111 | /* Make sure we are not removing a manifest that is the baseline of some |
| 112 | ** manifest that is being left behind. This step is not strictly necessary. |
| 113 | ** is is just a safety check. */ |
| 114 | if( purge_baseline_out_from_under_delta(zTab) ){ |
| @@ -196,10 +208,17 @@ | |
| 196 | db_finalize(&q); |
| 197 | /* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */ |
| 198 | |
| 199 | /* Mission accomplished */ |
| 200 | db_end_transaction(0); |
| 201 | return peid; |
| 202 | } |
| 203 | |
| 204 | /* |
| 205 | ** The TEMP table named zTab contains RIDs for a set of check-ins. |
| @@ -435,13 +454,18 @@ | |
| 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. |
| @@ -451,26 +475,27 @@ | |
| 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. |
| 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 | ** |
| @@ -480,10 +505,15 @@ | |
| 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] |
| @@ -492,19 +522,34 @@ | |
| 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++){ |
| @@ -514,41 +559,46 @@ | |
| 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"); |
| @@ -559,11 +609,23 @@ | |
| 559 | } |
| 560 | } |
| 561 | db_finalize(&q); |
| 562 | }else if( strncmp(zSubcmd, "obliterate", n)==0 ){ |
| 563 | int i; |
| 564 | if( g.argc<4 ) usage("obliterate ID..."); |
| 565 | db_begin_transaction(); |
| 566 | for(i=3; i<g.argc; i++){ |
| 567 | int peid = atoi(g.argv[i]); |
| 568 | if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){ |
| 569 | fossil_fatal("no such purge event: %s", g.argv[i]); |
| @@ -579,35 +641,37 @@ | |
| 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(); |
| 585 | db_multi_exec( |
| 586 | "CREATE TEMP TABLE ix(" |
| 587 | " piid INTEGER PRIMARY KEY," |
| 588 | " srcid INTEGER" |
| 589 | ");" |
| 590 | "CREATE INDEX ixsrcid ON ix(srcid);" |
| 591 | "INSERT INTO ix(piid,srcid) " |
| 592 | " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;", |
| 593 | peid |
| 594 | ); |
| 595 | db_multi_exec( |
| 596 | "DELETE FROM shun" |
| 597 | " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);", |
| 598 | peid |
| 599 | ); |
| 600 | manifest_crosslink_begin(); |
| 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 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -57,10 +57,12 @@ | |
| 57 | /* |
| 58 | ** Flags for the purge_artifact_list() function. |
| 59 | */ |
| 60 | #if INTERFACE |
| 61 | #define PURGE_MOVETO_GRAVEYARD 0x0001 /* Move artifacts in graveyard */ |
| 62 | #define PURGE_EXPLAIN_ONLY 0x0002 /* Show what would have happened */ |
| 63 | #define PURGE_PRINT_SUMMARY 0x0004 /* Print a summary report at end */ |
| 64 | #endif |
| 65 | |
| 66 | /* |
| 67 | ** This routine purges multiple artifacts from the repository, transfering |
| 68 | ** those artifacts into the PURGEITEM table. |
| @@ -105,10 +107,20 @@ | |
| 107 | assert( g.repositoryOpen ); /* Main database must already be open */ |
| 108 | db_begin_transaction(); |
| 109 | z = sqlite3_mprintf("IN \"%w\"", zTab); |
| 110 | describe_artifacts(z); |
| 111 | sqlite3_free(z); |
| 112 | describe_artifacts_to_stdout(0, 0); |
| 113 | |
| 114 | /* The explain-only flags causes this routine to list the artifacts |
| 115 | ** that would have been purged but to not actually make any changes |
| 116 | ** to the repository. |
| 117 | */ |
| 118 | if( purgeFlags & PURGE_EXPLAIN_ONLY ){ |
| 119 | db_end_transaction(0); |
| 120 | return 0; |
| 121 | } |
| 122 | |
| 123 | /* Make sure we are not removing a manifest that is the baseline of some |
| 124 | ** manifest that is being left behind. This step is not strictly necessary. |
| 125 | ** is is just a safety check. */ |
| 126 | if( purge_baseline_out_from_under_delta(zTab) ){ |
| @@ -196,10 +208,17 @@ | |
| 208 | db_finalize(&q); |
| 209 | /* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */ |
| 210 | |
| 211 | /* Mission accomplished */ |
| 212 | db_end_transaction(0); |
| 213 | |
| 214 | if( purgeFlags & PURGE_PRINT_SUMMARY ){ |
| 215 | fossil_print("%d artifacts purged\n", |
| 216 | db_int(0, "SELECT count(*) FROM \"%w\";", zTab)); |
| 217 | fossil_print("undoable using \"%s purge undo %d\".\n", |
| 218 | g.nameOfExe, peid); |
| 219 | } |
| 220 | return peid; |
| 221 | } |
| 222 | |
| 223 | /* |
| 224 | ** The TEMP table named zTab contains RIDs for a set of check-ins. |
| @@ -435,13 +454,18 @@ | |
| 454 | ** |
| 455 | ** The purge command removes content from a repository and stores that content |
| 456 | ** in a "graveyard". The graveyard exists so that content can be recovered |
| 457 | ** using the "fossil purge undo" command. |
| 458 | ** |
| 459 | ** ==== WARNING: This command can potentially destroy historical data and ==== |
| 460 | ** ==== leave your repository in a goofy state. Know what you are doing! ==== |
| 461 | ** ==== Make a backup of your repository before using this command! ==== |
| 462 | ** |
| 463 | ** fossil purge artifacts UUID... ?OPTIONS? |
| 464 | ** |
| 465 | ** Move arbitrary artifacts identified by the UUID list into the |
| 466 | ** graveyard. |
| 467 | ** |
| 468 | ** fossil purge cat UUID... |
| 469 | ** |
| 470 | ** Write the content of one or more artifacts in the graveyard onto |
| 471 | ** standard output. |
| @@ -451,26 +475,27 @@ | |
| 475 | ** Move the check-ins or branches identified by TAGS and all of |
| 476 | ** their descendants out of the repository and into the graveyard. |
| 477 | ** If TAGS includes a branch name then it means all the check-ins |
| 478 | ** on the most recent occurrence of that branch. |
| 479 | ** |
| 480 | ** fossil purge files NAME ... ?OPTIONS? |
| 481 | ** |
| 482 | ** Move all instances of files called NAME into the graveyard. |
| 483 | ** NAME should be the name of the file relative to the root of the |
| 484 | ** repository. If NAME is a directory, then all files within that |
| 485 | ** directory are moved. |
| 486 | ** |
| 487 | ** fossil purge list|ls ?-l? |
| 488 | ** |
| 489 | ** Show the graveyard of prior purges. The -l option gives more |
| 490 | ** detail in the output. |
| 491 | ** |
| 492 | ** fossil purge obliterate ID... ?--force? |
| 493 | ** |
| 494 | ** Remove one or more purge events from the graveyard. Once a purge |
| 495 | ** event is obliterated, it can no longer be undone. The --force |
| 496 | ** option suppresses the confirmation prompt. |
| 497 | ** |
| 498 | ** fossil purge tickets NAME ... ?OPTIONS? |
| 499 | ** |
| 500 | ** TBD... |
| 501 | ** |
| @@ -480,10 +505,15 @@ | |
| 505 | ** |
| 506 | ** fossil purge wiki NAME ... ?OPTIONS? |
| 507 | ** |
| 508 | ** TBD... |
| 509 | ** |
| 510 | ** COMMON OPTIONS: |
| 511 | ** |
| 512 | ** --explain Make no changes, but show what would happen. |
| 513 | ** --dry-run An alias for --explain |
| 514 | ** |
| 515 | ** SUMMARY: |
| 516 | ** fossil purge artifacts UUID.. [OPTIONS] |
| 517 | ** fossil purge cat UUID... |
| 518 | ** fossil purge checkins TAGS... [OPTIONS] |
| 519 | ** fossil purge files FILENAME... [OPTIONS] |
| @@ -492,19 +522,34 @@ | |
| 522 | ** fossil purge tickets NAME... [OPTIONS] |
| 523 | ** fossil purge undo ID |
| 524 | ** fossil purge wiki NAME... [OPTIONS] |
| 525 | */ |
| 526 | void purge_cmd(void){ |
| 527 | int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY; |
| 528 | const char *zSubcmd; |
| 529 | int n; |
| 530 | int i; |
| 531 | Stmt q; |
| 532 | |
| 533 | if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?"); |
| 534 | zSubcmd = g.argv[2]; |
| 535 | db_find_and_open_repository(0,0); |
| 536 | n = (int)strlen(zSubcmd); |
| 537 | if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){ |
| 538 | purgeFlags |= PURGE_EXPLAIN_ONLY; |
| 539 | } |
| 540 | if( strncmp(zSubcmd, "artifacts", n)==0 ){ |
| 541 | verify_all_options(); |
| 542 | db_begin_transaction(); |
| 543 | db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); |
| 544 | for(i=3; i<g.argc; i++){ |
| 545 | int r = name_to_typed_rid(g.argv[i], ""); |
| 546 | db_multi_exec("INSERT OR IGNORE INTO ok(rid) VALUES(%d);", r); |
| 547 | } |
| 548 | describe_artifacts_to_stdout("IN ok", 0); |
| 549 | purge_artifact_list("ok", "", purgeFlags); |
| 550 | db_end_transaction(0); |
| 551 | }else if( strncmp(zSubcmd, "cat", n)==0 ){ |
| 552 | int i, piid; |
| 553 | Blob content; |
| 554 | if( g.argc<4 ) usage("cat UUID..."); |
| 555 | for(i=3; i<g.argc; i++){ |
| @@ -514,41 +559,46 @@ | |
| 559 | purge_extract_item(piid, &content); |
| 560 | blob_write_to_file(&content, "-"); |
| 561 | blob_reset(&content); |
| 562 | } |
| 563 | }else if( strncmp(zSubcmd, "checkins", n)==0 ){ |
| 564 | int vid; |
| 565 | if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){ |
| 566 | purgeFlags |= PURGE_EXPLAIN_ONLY; |
| 567 | } |
| 568 | verify_all_options(); |
| 569 | db_begin_transaction(); |
| 570 | if( g.argc<=3 ) usage("checkins TAGS... [OPTIONS]"); |
| 571 | db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); |
| 572 | for(i=3; i<g.argc; i++){ |
| 573 | int r = name_to_typed_rid(g.argv[i], "br"); |
| 574 | compute_descendants(r, 1000000000); |
| 575 | } |
| 576 | vid = db_lget_int("checkout",0); |
| 577 | if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){ |
| 578 | fossil_fatal("cannot purge the current checkout"); |
| 579 | } |
| 580 | find_checkin_associates("ok", 1); |
| 581 | describe_artifacts_to_stdout("IN ok", 0); |
| 582 | purge_artifact_list("ok", "", purgeFlags); |
| 583 | db_end_transaction(0); |
| 584 | }else if( strncmp(zSubcmd, "files", n)==0 ){ |
| 585 | verify_all_options(); |
| 586 | db_begin_transaction(); |
| 587 | db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); |
| 588 | for(i=3; i<g.argc; i++){ |
| 589 | db_multi_exec( |
| 590 | "INSERT OR IGNORE INTO ok(rid) " |
| 591 | " SELECT fid FROM mlink, filename" |
| 592 | " WHERE mlink.fnid=filename.fnid" |
| 593 | " AND (filename.name=%Q OR filename.name GLOB '%q/*')", |
| 594 | g.argv[i], g.argv[i] |
| 595 | ); |
| 596 | } |
| 597 | describe_artifacts_to_stdout("IN ok", 0); |
| 598 | purge_artifact_list("ok", "", purgeFlags); |
| 599 | db_end_transaction(0); |
| 600 | }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){ |
| 601 | int showDetail = find_option("l","l",0)!=0; |
| 602 | if( !db_table_exists("repository","purgeevent") ) return; |
| 603 | db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())" |
| 604 | " FROM purgeevent"); |
| @@ -559,11 +609,23 @@ | |
| 609 | } |
| 610 | } |
| 611 | db_finalize(&q); |
| 612 | }else if( strncmp(zSubcmd, "obliterate", n)==0 ){ |
| 613 | int i; |
| 614 | int bForce = find_option("force","f",0)!=0; |
| 615 | if( g.argc<4 ) usage("obliterate ID..."); |
| 616 | if( !bForce ){ |
| 617 | Blob ans; |
| 618 | char cReply; |
| 619 | prompt_user( |
| 620 | "Obliterating the graveyard will permanently delete information.\n" |
| 621 | "Changes cannot be undone. Continue (y/N)? ", &ans); |
| 622 | cReply = blob_str(&ans)[0]; |
| 623 | if( cReply!='y' && cReply!='Y' ){ |
| 624 | fossil_exit(1); |
| 625 | } |
| 626 | } |
| 627 | db_begin_transaction(); |
| 628 | for(i=3; i<g.argc; i++){ |
| 629 | int peid = atoi(g.argv[i]); |
| 630 | if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){ |
| 631 | fossil_fatal("no such purge event: %s", g.argv[i]); |
| @@ -579,35 +641,37 @@ | |
| 641 | fossil_fatal("not yet implemented...."); |
| 642 | }else if( strncmp(zSubcmd, "undo", n)==0 ){ |
| 643 | int peid; |
| 644 | if( g.argc!=4 ) usage("undo ID"); |
| 645 | peid = atoi(g.argv[3]); |
| 646 | if( (purgeFlags & PURGE_EXPLAIN_ONLY)==0 ){ |
| 647 | db_begin_transaction(); |
| 648 | db_multi_exec( |
| 649 | "CREATE TEMP TABLE ix(" |
| 650 | " piid INTEGER PRIMARY KEY," |
| 651 | " srcid INTEGER" |
| 652 | ");" |
| 653 | "CREATE INDEX ixsrcid ON ix(srcid);" |
| 654 | "INSERT INTO ix(piid,srcid) " |
| 655 | " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;", |
| 656 | peid |
| 657 | ); |
| 658 | db_multi_exec( |
| 659 | "DELETE FROM shun" |
| 660 | " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);", |
| 661 | peid |
| 662 | ); |
| 663 | manifest_crosslink_begin(); |
| 664 | purge_item_resurrect(0, 0); |
| 665 | manifest_crosslink_end(0); |
| 666 | db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid); |
| 667 | db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid); |
| 668 | db_end_transaction(0); |
| 669 | } |
| 670 | }else if( strncmp(zSubcmd, "wiki", n)==0 ){ |
| 671 | fossil_fatal("not yet implemented...."); |
| 672 | }else{ |
| 673 | fossil_fatal("unknown subcommand \"%s\".\n" |
| 674 | "should be one of: cat, checkins, files, list, obliterate," |
| 675 | " tickets, undo, wiki", zSubcmd); |
| 676 | } |
| 677 | } |
| 678 |