Fossil SCM

Merge the "purge" command onto trunk, even though it is incomplete.

drh 2016-08-02 15:18 trunk merge
Commit b78a32f43d5d0430ab8d64528516dd4a20d34a43
3 files changed +8 -3 +173 -76 +173 -76
+8 -3
--- src/name.c
+++ src/name.c
@@ -934,16 +934,21 @@
934934
"UPDATE description SET isPrivate=1 WHERE rid IN private"
935935
);
936936
}
937937
938938
/*
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.
940945
*/
941946
int describe_artifacts_to_stdout(const char *zWhere, const char *zLabel){
942947
Stmt q;
943948
int cnt = 0;
944
- describe_artifacts(zWhere);
949
+ if( zWhere!=0 ) describe_artifacts(zWhere);
945950
db_prepare(&q,
946951
"SELECT uuid, summary, isPrivate\n"
947952
" FROM description\n"
948953
" ORDER BY ctime, type;"
949954
);
@@ -956,11 +961,11 @@
956961
if( db_column_int(&q,2) ) fossil_print(" (unpublished)");
957962
fossil_print("\n");
958963
cnt++;
959964
}
960965
db_finalize(&q);
961
- db_multi_exec("DELETE FROM description;");
966
+ if( zWhere!=0 ) db_multi_exec("DELETE FROM description;");
962967
return cnt;
963968
}
964969
965970
/*
966971
** COMMAND: test-describe-artifacts
967972
--- 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
+173 -76
--- src/purge.c
+++ src/purge.c
@@ -22,11 +22,11 @@
2222
#include "config.h"
2323
#include "purge.h"
2424
#include <assert.h>
2525
2626
/*
27
-** SQL code used to initialize the schema of a bundle.
27
+** SQL code used to initialize the schema of the graveyard.
2828
**
2929
** The purgeevent table contains one entry for each purge event. For each
3030
** purge event, multiple artifacts might have been removed. Each removed
3131
** artifact is stored as an entry in the purgeitem table.
3232
**
@@ -52,10 +52,19 @@
5252
@ desc TEXT, -- Brief description of this artifact
5353
@ data BLOB -- Compressed artifact content
5454
@ );
5555
;
5656
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
+
5766
/*
5867
** This routine purges multiple artifacts from the repository, transfering
5968
** those artifacts into the PURGEITEM table.
6069
**
6170
** Prior to invoking this routine, the caller must create a (TEMP) table
@@ -81,17 +90,17 @@
8190
** (j) TICKETCHNG
8291
** (7) If any ticket artifacts were removed (6j) then rebuild the
8392
** corresponding ticket entries. Possibly remove entries from
8493
** the ticket table.
8594
**
86
-** Stops 1-4 (saving the purged artifacts into the graveyard) are only
95
+** Steps 1-4 (saving the purged artifacts into the graveyard) are only
8796
** undertaken if the moveToGraveyard flag is true.
8897
*/
8998
int purge_artifact_list(
9099
const char *zTab, /* TEMP table containing list of RIDS to be purged */
91100
const char *zNote, /* Text of the purgeevent.pnotes field */
92
- int moveToGraveyard /* Move purged artifacts into the graveyard */
101
+ unsigned purgeFlags /* zero or more PURGE_* flags */
93102
){
94103
int peid = 0; /* New purgeevent ID */
95104
Stmt q; /* General-use prepared statement */
96105
char *z;
97106
@@ -98,10 +107,20 @@
98107
assert( g.repositoryOpen ); /* Main database must already be open */
99108
db_begin_transaction();
100109
z = sqlite3_mprintf("IN \"%w\"", zTab);
101110
describe_artifacts(z);
102111
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
+ }
103122
104123
/* Make sure we are not removing a manifest that is the baseline of some
105124
** manifest that is being left behind. This step is not strictly necessary.
106125
** is is just a safety check. */
107126
if( purge_baseline_out_from_under_delta(zTab) ){
@@ -121,11 +140,11 @@
121140
}
122141
db_finalize(&q);
123142
124143
/* Construct the graveyard and copy the artifacts to be purged into the
125144
** graveyard */
126
- if( moveToGraveyard ){
145
+ if( purgeFlags & PURGE_MOVETO_GRAVEYARD ){
127146
db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
128147
db_name("repository"), db_name("repository"));
129148
db_multi_exec(
130149
"INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
131150
);
@@ -189,10 +208,17 @@
189208
db_finalize(&q);
190209
/* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */
191210
192211
/* Mission accomplished */
193212
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
+ }
194220
return peid;
195221
}
196222
197223
/*
198224
** The TEMP table named zTab contains RIDs for a set of check-ins.
@@ -220,11 +246,11 @@
220246
221247
222248
/*
223249
** The TEMP table named zTab contains the RIDs for a set of check-in
224250
** 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
251
+** all other artifacts that are used by the check-ins in
226252
** the original list.
227253
**
228254
** If the bExclusive flag is true, then the set is only expanded by
229255
** artifacts that are used exclusively by the check-ins in the set.
230256
** When bExclusive is false, then all artifacts used by the check-ins
@@ -422,64 +448,113 @@
422448
db_finalize(&q);
423449
if( iSrc>0 ) bag_remove(&busy, iSrc);
424450
}
425451
426452
/*
427
-** COMMAND: purge
453
+** COMMAND: purge*
428454
**
429455
** The purge command removes content from a repository and stores that content
430456
** in a "graveyard". The graveyard exists so that content can be recovered
431
-** using the "fossil purge undo" command.
457
+** using the "fossil purge undo" command. The "fossil purge obliterate"
458
+** command empties the graveyard, making the content unrecoverable.
459
+**
460
+** ==== WARNING: This command can potentially destroy historical data and ====
461
+** ==== leave your repository in a goofy state. Know what you are doing! ====
462
+** ==== Make a backup of your repository before using this command! ====
463
+**
464
+** ==== FURTHER WARNING: This command is a work-in-progress and may yet ====
465
+** ==== contains bugs. ====
466
+**
467
+** fossil purge artifacts UUID... ?OPTIONS?
468
+**
469
+** Move arbitrary artifacts identified by the UUID list into the
470
+** graveyard.
432471
**
433472
** fossil purge cat UUID...
434473
**
435474
** Write the content of one or more artifacts in the graveyard onto
436475
** standard output.
437476
**
438
-** fossil purge ?checkins? TAGS... ?OPTIONS?
477
+** fossil purge checkins TAGS... ?OPTIONS?
439478
**
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
-**
479
+** Move the check-ins or branches identified by TAGS and all of
480
+** their descendants out of the repository and into the graveyard.
445481
** If TAGS includes a branch name then it means all the check-ins
446482
** on the most recent occurrence of that branch.
447483
**
448
-** --explain Make no changes, but show what would happen.
449
-** --dry-run Make no changes.
484
+** fossil purge files NAME ... ?OPTIONS?
485
+**
486
+** Move all instances of files called NAME into the graveyard.
487
+** NAME should be the name of the file relative to the root of the
488
+** repository. If NAME is a directory, then all files within that
489
+** directory are moved.
450490
**
451491
** fossil purge list|ls ?-l?
452492
**
453493
** Show the graveyard of prior purges. The -l option gives more
454494
** detail in the output.
455495
**
456
-** fossil purge obliterate ID...
496
+** fossil purge obliterate ID... ?--force?
457497
**
458498
** Remove one or more purge events from the graveyard. Once a purge
459
-** event is obliterated, it can no longer be undone.
499
+** event is obliterated, it can no longer be undone. The --force
500
+** option suppresses the confirmation prompt.
501
+**
502
+** fossil purge tickets NAME ... ?OPTIONS?
503
+**
504
+** TBD...
460505
**
461506
** fossil purge undo ID
462507
**
463508
** Restore the content previously removed by purge ID.
464509
**
510
+** fossil purge wiki NAME ... ?OPTIONS?
511
+**
512
+** TBD...
513
+**
514
+** COMMON OPTIONS:
515
+**
516
+** --explain Make no changes, but show what would happen.
517
+** --dry-run An alias for --explain
518
+**
465519
** SUMMARY:
520
+** fossil purge artifacts UUID.. [OPTIONS]
466521
** fossil purge cat UUID...
467
-** fossil purge [checkins] TAGS... [--explain]
522
+** fossil purge checkins TAGS... [OPTIONS]
523
+** fossil purge files FILENAME... [OPTIONS]
468524
** fossil purge list
469525
** fossil purge obliterate ID...
526
+** fossil purge tickets NAME... [OPTIONS]
470527
** fossil purge undo ID
528
+** fossil purge wiki NAME... [OPTIONS]
471529
*/
472530
void purge_cmd(void){
531
+ int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
473532
const char *zSubcmd;
474533
int n;
534
+ int i;
475535
Stmt q;
536
+
476537
if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?");
477538
zSubcmd = g.argv[2];
478539
db_find_and_open_repository(0,0);
479540
n = (int)strlen(zSubcmd);
480
- if( strncmp(zSubcmd, "cat", n)==0 ){
541
+ if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
542
+ purgeFlags |= PURGE_EXPLAIN_ONLY;
543
+ }
544
+ if( strncmp(zSubcmd, "artifacts", n)==0 ){
545
+ verify_all_options();
546
+ db_begin_transaction();
547
+ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
548
+ for(i=3; i<g.argc; i++){
549
+ int r = name_to_typed_rid(g.argv[i], "");
550
+ db_multi_exec("INSERT OR IGNORE INTO ok(rid) VALUES(%d);", r);
551
+ }
552
+ describe_artifacts_to_stdout("IN ok", 0);
553
+ purge_artifact_list("ok", "", purgeFlags);
554
+ db_end_transaction(0);
555
+ }else if( strncmp(zSubcmd, "cat", n)==0 ){
481556
int i, piid;
482557
Blob content;
483558
if( g.argc<4 ) usage("cat UUID...");
484559
for(i=3; i<g.argc; i++){
485560
piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
@@ -487,12 +562,45 @@
487562
if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
488563
purge_extract_item(piid, &content);
489564
blob_write_to_file(&content, "-");
490565
blob_reset(&content);
491566
}
492
- /* The "checkins" subcommand goes here in alphabetical order, but it must
493
- ** be moved to the end since it is the default case */
567
+ }else if( strncmp(zSubcmd, "checkins", n)==0 ){
568
+ int vid;
569
+ if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
570
+ purgeFlags |= PURGE_EXPLAIN_ONLY;
571
+ }
572
+ verify_all_options();
573
+ db_begin_transaction();
574
+ if( g.argc<=3 ) usage("checkins TAGS... [OPTIONS]");
575
+ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
576
+ for(i=3; i<g.argc; i++){
577
+ int r = name_to_typed_rid(g.argv[i], "br");
578
+ compute_descendants(r, 1000000000);
579
+ }
580
+ vid = db_lget_int("checkout",0);
581
+ if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){
582
+ fossil_fatal("cannot purge the current checkout");
583
+ }
584
+ find_checkin_associates("ok", 1);
585
+ purge_artifact_list("ok", "", purgeFlags);
586
+ db_end_transaction(0);
587
+ }else if( strncmp(zSubcmd, "files", n)==0 ){
588
+ verify_all_options();
589
+ db_begin_transaction();
590
+ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
591
+ for(i=3; i<g.argc; i++){
592
+ db_multi_exec(
593
+ "INSERT OR IGNORE INTO ok(rid) "
594
+ " SELECT fid FROM mlink, filename"
595
+ " WHERE mlink.fnid=filename.fnid"
596
+ " AND (filename.name=%Q OR filename.name GLOB '%q/*')",
597
+ g.argv[i], g.argv[i]
598
+ );
599
+ }
600
+ purge_artifact_list("ok", "", purgeFlags);
601
+ db_end_transaction(0);
494602
}else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){
495603
int showDetail = find_option("l","l",0)!=0;
496604
if( !db_table_exists("repository","purgeevent") ) return;
497605
db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())"
498606
" FROM purgeevent");
@@ -503,11 +611,23 @@
503611
}
504612
}
505613
db_finalize(&q);
506614
}else if( strncmp(zSubcmd, "obliterate", n)==0 ){
507615
int i;
616
+ int bForce = find_option("force","f",0)!=0;
508617
if( g.argc<4 ) usage("obliterate ID...");
618
+ if( !bForce ){
619
+ Blob ans;
620
+ char cReply;
621
+ prompt_user(
622
+ "Obliterating the graveyard will permanently delete information.\n"
623
+ "Changes cannot be undone. Continue (y/N)? ", &ans);
624
+ cReply = blob_str(&ans)[0];
625
+ if( cReply!='y' && cReply!='Y' ){
626
+ fossil_exit(1);
627
+ }
628
+ }
509629
db_begin_transaction();
510630
for(i=3; i<g.argc; i++){
511631
int peid = atoi(g.argv[i]);
512632
if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){
513633
fossil_fatal("no such purge event: %s", g.argv[i]);
@@ -517,66 +637,43 @@
517637
"DELETE FROM purgeitem WHERE peid=%d;",
518638
peid, peid
519639
);
520640
}
521641
db_end_transaction(0);
642
+ }else if( strncmp(zSubcmd, "tickets", n)==0 ){
643
+ fossil_fatal("not yet implemented....");
522644
}else if( strncmp(zSubcmd, "undo", n)==0 ){
523645
int peid;
524646
if( g.argc!=4 ) usage("undo ID");
525647
peid = atoi(g.argv[3]);
526
- db_begin_transaction();
527
- db_multi_exec(
528
- "CREATE TEMP TABLE ix("
529
- " piid INTEGER PRIMARY KEY,"
530
- " srcid INTEGER"
531
- ");"
532
- "CREATE INDEX ixsrcid ON ix(srcid);"
533
- "INSERT INTO ix(piid,srcid) "
534
- " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
535
- peid
536
- );
537
- db_multi_exec(
538
- "DELETE FROM shun"
539
- " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
540
- peid
541
- );
542
- manifest_crosslink_begin();
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);
648
+ if( (purgeFlags & PURGE_EXPLAIN_ONLY)==0 ){
649
+ db_begin_transaction();
650
+ db_multi_exec(
651
+ "CREATE TEMP TABLE ix("
652
+ " piid INTEGER PRIMARY KEY,"
653
+ " srcid INTEGER"
654
+ ");"
655
+ "CREATE INDEX ixsrcid ON ix(srcid);"
656
+ "INSERT INTO ix(piid,srcid) "
657
+ " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
658
+ peid
659
+ );
660
+ db_multi_exec(
661
+ "DELETE FROM shun"
662
+ " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
663
+ peid
664
+ );
665
+ manifest_crosslink_begin();
666
+ purge_item_resurrect(0, 0);
667
+ manifest_crosslink_end(0);
668
+ db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
669
+ db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
670
+ db_end_transaction(0);
671
+ }
672
+ }else if( strncmp(zSubcmd, "wiki", n)==0 ){
673
+ fossil_fatal("not yet implemented....");
674
+ }else{
675
+ fossil_fatal("unknown subcommand \"%s\".\n"
676
+ "should be one of: cat, checkins, files, list, obliterate,"
677
+ " tickets, undo, wiki", zSubcmd);
581678
}
582679
}
583680
--- 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,19 @@
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 +90,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
@@ -98,10 +107,20 @@
98 assert( g.repositoryOpen ); /* Main database must already be open */
99 db_begin_transaction();
100 z = sqlite3_mprintf("IN \"%w\"", zTab);
101 describe_artifacts(z);
102 sqlite3_free(z);
 
 
 
 
 
 
 
 
 
 
103
104 /* Make sure we are not removing a manifest that is the baseline of some
105 ** manifest that is being left behind. This step is not strictly necessary.
106 ** is is just a safety check. */
107 if( purge_baseline_out_from_under_delta(zTab) ){
@@ -121,11 +140,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 );
@@ -189,10 +208,17 @@
189 db_finalize(&q);
190 /* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */
191
192 /* Mission accomplished */
193 db_end_transaction(0);
 
 
 
 
 
 
 
194 return peid;
195 }
196
197 /*
198 ** The TEMP table named zTab contains RIDs for a set of check-ins.
@@ -220,11 +246,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
@@ -422,64 +448,113 @@
422 db_finalize(&q);
423 if( iSrc>0 ) bag_remove(&busy, iSrc);
424 }
425
426 /*
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 **
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 +562,45 @@
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");
@@ -503,11 +611,23 @@
503 }
504 }
505 db_finalize(&q);
506 }else if( strncmp(zSubcmd, "obliterate", n)==0 ){
507 int i;
 
508 if( g.argc<4 ) usage("obliterate ID...");
 
 
 
 
 
 
 
 
 
 
 
509 db_begin_transaction();
510 for(i=3; i<g.argc; i++){
511 int peid = atoi(g.argv[i]);
512 if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){
513 fossil_fatal("no such purge event: %s", g.argv[i]);
@@ -517,66 +637,43 @@
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();
527 db_multi_exec(
528 "CREATE TEMP TABLE ix("
529 " piid INTEGER PRIMARY KEY,"
530 " srcid INTEGER"
531 ");"
532 "CREATE INDEX ixsrcid ON ix(srcid);"
533 "INSERT INTO ix(piid,srcid) "
534 " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
535 peid
536 );
537 db_multi_exec(
538 "DELETE FROM shun"
539 " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
540 peid
541 );
542 manifest_crosslink_begin();
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,19 @@
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 #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.
69 **
70 ** Prior to invoking this routine, the caller must create a (TEMP) table
@@ -81,17 +90,17 @@
90 ** (j) TICKETCHNG
91 ** (7) If any ticket artifacts were removed (6j) then rebuild the
92 ** corresponding ticket entries. Possibly remove entries from
93 ** the ticket table.
94 **
95 ** Steps 1-4 (saving the purged artifacts into the graveyard) are only
96 ** undertaken if the moveToGraveyard flag is true.
97 */
98 int purge_artifact_list(
99 const char *zTab, /* TEMP table containing list of RIDS to be purged */
100 const char *zNote, /* Text of the purgeevent.pnotes field */
101 unsigned purgeFlags /* zero or more PURGE_* flags */
102 ){
103 int peid = 0; /* New purgeevent ID */
104 Stmt q; /* General-use prepared statement */
105 char *z;
106
@@ -98,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) ){
@@ -121,11 +140,11 @@
140 }
141 db_finalize(&q);
142
143 /* Construct the graveyard and copy the artifacts to be purged into the
144 ** graveyard */
145 if( purgeFlags & PURGE_MOVETO_GRAVEYARD ){
146 db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
147 db_name("repository"), db_name("repository"));
148 db_multi_exec(
149 "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
150 );
@@ -189,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.
@@ -220,11 +246,11 @@
246
247
248 /*
249 ** The TEMP table named zTab contains the RIDs for a set of check-in
250 ** artifacts. Expand this set (by adding new entries to zTab) to include
251 ** all other artifacts that are used by the check-ins in
252 ** the original list.
253 **
254 ** If the bExclusive flag is true, then the set is only expanded by
255 ** artifacts that are used exclusively by the check-ins in the set.
256 ** When bExclusive is false, then all artifacts used by the check-ins
@@ -422,64 +448,113 @@
448 db_finalize(&q);
449 if( iSrc>0 ) bag_remove(&busy, iSrc);
450 }
451
452 /*
453 ** COMMAND: purge*
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. The "fossil purge obliterate"
458 ** command empties the graveyard, making the content unrecoverable.
459 **
460 ** ==== WARNING: This command can potentially destroy historical data and ====
461 ** ==== leave your repository in a goofy state. Know what you are doing! ====
462 ** ==== Make a backup of your repository before using this command! ====
463 **
464 ** ==== FURTHER WARNING: This command is a work-in-progress and may yet ====
465 ** ==== contains bugs. ====
466 **
467 ** fossil purge artifacts UUID... ?OPTIONS?
468 **
469 ** Move arbitrary artifacts identified by the UUID list into the
470 ** graveyard.
471 **
472 ** fossil purge cat UUID...
473 **
474 ** Write the content of one or more artifacts in the graveyard onto
475 ** standard output.
476 **
477 ** fossil purge checkins TAGS... ?OPTIONS?
478 **
479 ** Move the check-ins or branches identified by TAGS and all of
480 ** their descendants out of the repository and into the graveyard.
 
 
 
481 ** If TAGS includes a branch name then it means all the check-ins
482 ** on the most recent occurrence of that branch.
483 **
484 ** fossil purge files NAME ... ?OPTIONS?
485 **
486 ** Move all instances of files called NAME into the graveyard.
487 ** NAME should be the name of the file relative to the root of the
488 ** repository. If NAME is a directory, then all files within that
489 ** directory are moved.
490 **
491 ** fossil purge list|ls ?-l?
492 **
493 ** Show the graveyard of prior purges. The -l option gives more
494 ** detail in the output.
495 **
496 ** fossil purge obliterate ID... ?--force?
497 **
498 ** Remove one or more purge events from the graveyard. Once a purge
499 ** event is obliterated, it can no longer be undone. The --force
500 ** option suppresses the confirmation prompt.
501 **
502 ** fossil purge tickets NAME ... ?OPTIONS?
503 **
504 ** TBD...
505 **
506 ** fossil purge undo ID
507 **
508 ** Restore the content previously removed by purge ID.
509 **
510 ** fossil purge wiki NAME ... ?OPTIONS?
511 **
512 ** TBD...
513 **
514 ** COMMON OPTIONS:
515 **
516 ** --explain Make no changes, but show what would happen.
517 ** --dry-run An alias for --explain
518 **
519 ** SUMMARY:
520 ** fossil purge artifacts UUID.. [OPTIONS]
521 ** fossil purge cat UUID...
522 ** fossil purge checkins TAGS... [OPTIONS]
523 ** fossil purge files FILENAME... [OPTIONS]
524 ** fossil purge list
525 ** fossil purge obliterate ID...
526 ** fossil purge tickets NAME... [OPTIONS]
527 ** fossil purge undo ID
528 ** fossil purge wiki NAME... [OPTIONS]
529 */
530 void purge_cmd(void){
531 int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
532 const char *zSubcmd;
533 int n;
534 int i;
535 Stmt q;
536
537 if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?");
538 zSubcmd = g.argv[2];
539 db_find_and_open_repository(0,0);
540 n = (int)strlen(zSubcmd);
541 if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
542 purgeFlags |= PURGE_EXPLAIN_ONLY;
543 }
544 if( strncmp(zSubcmd, "artifacts", n)==0 ){
545 verify_all_options();
546 db_begin_transaction();
547 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
548 for(i=3; i<g.argc; i++){
549 int r = name_to_typed_rid(g.argv[i], "");
550 db_multi_exec("INSERT OR IGNORE INTO ok(rid) VALUES(%d);", r);
551 }
552 describe_artifacts_to_stdout("IN ok", 0);
553 purge_artifact_list("ok", "", purgeFlags);
554 db_end_transaction(0);
555 }else if( strncmp(zSubcmd, "cat", n)==0 ){
556 int i, piid;
557 Blob content;
558 if( g.argc<4 ) usage("cat UUID...");
559 for(i=3; i<g.argc; i++){
560 piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
@@ -487,12 +562,45 @@
562 if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
563 purge_extract_item(piid, &content);
564 blob_write_to_file(&content, "-");
565 blob_reset(&content);
566 }
567 }else if( strncmp(zSubcmd, "checkins", n)==0 ){
568 int vid;
569 if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
570 purgeFlags |= PURGE_EXPLAIN_ONLY;
571 }
572 verify_all_options();
573 db_begin_transaction();
574 if( g.argc<=3 ) usage("checkins TAGS... [OPTIONS]");
575 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
576 for(i=3; i<g.argc; i++){
577 int r = name_to_typed_rid(g.argv[i], "br");
578 compute_descendants(r, 1000000000);
579 }
580 vid = db_lget_int("checkout",0);
581 if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){
582 fossil_fatal("cannot purge the current checkout");
583 }
584 find_checkin_associates("ok", 1);
585 purge_artifact_list("ok", "", purgeFlags);
586 db_end_transaction(0);
587 }else if( strncmp(zSubcmd, "files", n)==0 ){
588 verify_all_options();
589 db_begin_transaction();
590 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
591 for(i=3; i<g.argc; i++){
592 db_multi_exec(
593 "INSERT OR IGNORE INTO ok(rid) "
594 " SELECT fid FROM mlink, filename"
595 " WHERE mlink.fnid=filename.fnid"
596 " AND (filename.name=%Q OR filename.name GLOB '%q/*')",
597 g.argv[i], g.argv[i]
598 );
599 }
600 purge_artifact_list("ok", "", purgeFlags);
601 db_end_transaction(0);
602 }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){
603 int showDetail = find_option("l","l",0)!=0;
604 if( !db_table_exists("repository","purgeevent") ) return;
605 db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())"
606 " FROM purgeevent");
@@ -503,11 +611,23 @@
611 }
612 }
613 db_finalize(&q);
614 }else if( strncmp(zSubcmd, "obliterate", n)==0 ){
615 int i;
616 int bForce = find_option("force","f",0)!=0;
617 if( g.argc<4 ) usage("obliterate ID...");
618 if( !bForce ){
619 Blob ans;
620 char cReply;
621 prompt_user(
622 "Obliterating the graveyard will permanently delete information.\n"
623 "Changes cannot be undone. Continue (y/N)? ", &ans);
624 cReply = blob_str(&ans)[0];
625 if( cReply!='y' && cReply!='Y' ){
626 fossil_exit(1);
627 }
628 }
629 db_begin_transaction();
630 for(i=3; i<g.argc; i++){
631 int peid = atoi(g.argv[i]);
632 if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){
633 fossil_fatal("no such purge event: %s", g.argv[i]);
@@ -517,66 +637,43 @@
637 "DELETE FROM purgeitem WHERE peid=%d;",
638 peid, peid
639 );
640 }
641 db_end_transaction(0);
642 }else if( strncmp(zSubcmd, "tickets", n)==0 ){
643 fossil_fatal("not yet implemented....");
644 }else if( strncmp(zSubcmd, "undo", n)==0 ){
645 int peid;
646 if( g.argc!=4 ) usage("undo ID");
647 peid = atoi(g.argv[3]);
648 if( (purgeFlags & PURGE_EXPLAIN_ONLY)==0 ){
649 db_begin_transaction();
650 db_multi_exec(
651 "CREATE TEMP TABLE ix("
652 " piid INTEGER PRIMARY KEY,"
653 " srcid INTEGER"
654 ");"
655 "CREATE INDEX ixsrcid ON ix(srcid);"
656 "INSERT INTO ix(piid,srcid) "
657 " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
658 peid
659 );
660 db_multi_exec(
661 "DELETE FROM shun"
662 " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
663 peid
664 );
665 manifest_crosslink_begin();
666 purge_item_resurrect(0, 0);
667 manifest_crosslink_end(0);
668 db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
669 db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
670 db_end_transaction(0);
671 }
672 }else if( strncmp(zSubcmd, "wiki", n)==0 ){
673 fossil_fatal("not yet implemented....");
674 }else{
675 fossil_fatal("unknown subcommand \"%s\".\n"
676 "should be one of: cat, checkins, files, list, obliterate,"
677 " tickets, undo, wiki", zSubcmd);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
678 }
679 }
680
+173 -76
--- src/purge.c
+++ src/purge.c
@@ -22,11 +22,11 @@
2222
#include "config.h"
2323
#include "purge.h"
2424
#include <assert.h>
2525
2626
/*
27
-** SQL code used to initialize the schema of a bundle.
27
+** SQL code used to initialize the schema of the graveyard.
2828
**
2929
** The purgeevent table contains one entry for each purge event. For each
3030
** purge event, multiple artifacts might have been removed. Each removed
3131
** artifact is stored as an entry in the purgeitem table.
3232
**
@@ -52,10 +52,19 @@
5252
@ desc TEXT, -- Brief description of this artifact
5353
@ data BLOB -- Compressed artifact content
5454
@ );
5555
;
5656
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
+
5766
/*
5867
** This routine purges multiple artifacts from the repository, transfering
5968
** those artifacts into the PURGEITEM table.
6069
**
6170
** Prior to invoking this routine, the caller must create a (TEMP) table
@@ -81,17 +90,17 @@
8190
** (j) TICKETCHNG
8291
** (7) If any ticket artifacts were removed (6j) then rebuild the
8392
** corresponding ticket entries. Possibly remove entries from
8493
** the ticket table.
8594
**
86
-** Stops 1-4 (saving the purged artifacts into the graveyard) are only
95
+** Steps 1-4 (saving the purged artifacts into the graveyard) are only
8796
** undertaken if the moveToGraveyard flag is true.
8897
*/
8998
int purge_artifact_list(
9099
const char *zTab, /* TEMP table containing list of RIDS to be purged */
91100
const char *zNote, /* Text of the purgeevent.pnotes field */
92
- int moveToGraveyard /* Move purged artifacts into the graveyard */
101
+ unsigned purgeFlags /* zero or more PURGE_* flags */
93102
){
94103
int peid = 0; /* New purgeevent ID */
95104
Stmt q; /* General-use prepared statement */
96105
char *z;
97106
@@ -98,10 +107,20 @@
98107
assert( g.repositoryOpen ); /* Main database must already be open */
99108
db_begin_transaction();
100109
z = sqlite3_mprintf("IN \"%w\"", zTab);
101110
describe_artifacts(z);
102111
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
+ }
103122
104123
/* Make sure we are not removing a manifest that is the baseline of some
105124
** manifest that is being left behind. This step is not strictly necessary.
106125
** is is just a safety check. */
107126
if( purge_baseline_out_from_under_delta(zTab) ){
@@ -121,11 +140,11 @@
121140
}
122141
db_finalize(&q);
123142
124143
/* Construct the graveyard and copy the artifacts to be purged into the
125144
** graveyard */
126
- if( moveToGraveyard ){
145
+ if( purgeFlags & PURGE_MOVETO_GRAVEYARD ){
127146
db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
128147
db_name("repository"), db_name("repository"));
129148
db_multi_exec(
130149
"INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
131150
);
@@ -189,10 +208,17 @@
189208
db_finalize(&q);
190209
/* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */
191210
192211
/* Mission accomplished */
193212
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
+ }
194220
return peid;
195221
}
196222
197223
/*
198224
** The TEMP table named zTab contains RIDs for a set of check-ins.
@@ -220,11 +246,11 @@
220246
221247
222248
/*
223249
** The TEMP table named zTab contains the RIDs for a set of check-in
224250
** 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
251
+** all other artifacts that are used by the check-ins in
226252
** the original list.
227253
**
228254
** If the bExclusive flag is true, then the set is only expanded by
229255
** artifacts that are used exclusively by the check-ins in the set.
230256
** When bExclusive is false, then all artifacts used by the check-ins
@@ -422,64 +448,113 @@
422448
db_finalize(&q);
423449
if( iSrc>0 ) bag_remove(&busy, iSrc);
424450
}
425451
426452
/*
427
-** COMMAND: purge
453
+** COMMAND: purge*
428454
**
429455
** The purge command removes content from a repository and stores that content
430456
** in a "graveyard". The graveyard exists so that content can be recovered
431
-** using the "fossil purge undo" command.
457
+** using the "fossil purge undo" command. The "fossil purge obliterate"
458
+** command empties the graveyard, making the content unrecoverable.
459
+**
460
+** ==== WARNING: This command can potentially destroy historical data and ====
461
+** ==== leave your repository in a goofy state. Know what you are doing! ====
462
+** ==== Make a backup of your repository before using this command! ====
463
+**
464
+** ==== FURTHER WARNING: This command is a work-in-progress and may yet ====
465
+** ==== contains bugs. ====
466
+**
467
+** fossil purge artifacts UUID... ?OPTIONS?
468
+**
469
+** Move arbitrary artifacts identified by the UUID list into the
470
+** graveyard.
432471
**
433472
** fossil purge cat UUID...
434473
**
435474
** Write the content of one or more artifacts in the graveyard onto
436475
** standard output.
437476
**
438
-** fossil purge ?checkins? TAGS... ?OPTIONS?
477
+** fossil purge checkins TAGS... ?OPTIONS?
439478
**
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
-**
479
+** Move the check-ins or branches identified by TAGS and all of
480
+** their descendants out of the repository and into the graveyard.
445481
** If TAGS includes a branch name then it means all the check-ins
446482
** on the most recent occurrence of that branch.
447483
**
448
-** --explain Make no changes, but show what would happen.
449
-** --dry-run Make no changes.
484
+** fossil purge files NAME ... ?OPTIONS?
485
+**
486
+** Move all instances of files called NAME into the graveyard.
487
+** NAME should be the name of the file relative to the root of the
488
+** repository. If NAME is a directory, then all files within that
489
+** directory are moved.
450490
**
451491
** fossil purge list|ls ?-l?
452492
**
453493
** Show the graveyard of prior purges. The -l option gives more
454494
** detail in the output.
455495
**
456
-** fossil purge obliterate ID...
496
+** fossil purge obliterate ID... ?--force?
457497
**
458498
** Remove one or more purge events from the graveyard. Once a purge
459
-** event is obliterated, it can no longer be undone.
499
+** event is obliterated, it can no longer be undone. The --force
500
+** option suppresses the confirmation prompt.
501
+**
502
+** fossil purge tickets NAME ... ?OPTIONS?
503
+**
504
+** TBD...
460505
**
461506
** fossil purge undo ID
462507
**
463508
** Restore the content previously removed by purge ID.
464509
**
510
+** fossil purge wiki NAME ... ?OPTIONS?
511
+**
512
+** TBD...
513
+**
514
+** COMMON OPTIONS:
515
+**
516
+** --explain Make no changes, but show what would happen.
517
+** --dry-run An alias for --explain
518
+**
465519
** SUMMARY:
520
+** fossil purge artifacts UUID.. [OPTIONS]
466521
** fossil purge cat UUID...
467
-** fossil purge [checkins] TAGS... [--explain]
522
+** fossil purge checkins TAGS... [OPTIONS]
523
+** fossil purge files FILENAME... [OPTIONS]
468524
** fossil purge list
469525
** fossil purge obliterate ID...
526
+** fossil purge tickets NAME... [OPTIONS]
470527
** fossil purge undo ID
528
+** fossil purge wiki NAME... [OPTIONS]
471529
*/
472530
void purge_cmd(void){
531
+ int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
473532
const char *zSubcmd;
474533
int n;
534
+ int i;
475535
Stmt q;
536
+
476537
if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?");
477538
zSubcmd = g.argv[2];
478539
db_find_and_open_repository(0,0);
479540
n = (int)strlen(zSubcmd);
480
- if( strncmp(zSubcmd, "cat", n)==0 ){
541
+ if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
542
+ purgeFlags |= PURGE_EXPLAIN_ONLY;
543
+ }
544
+ if( strncmp(zSubcmd, "artifacts", n)==0 ){
545
+ verify_all_options();
546
+ db_begin_transaction();
547
+ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
548
+ for(i=3; i<g.argc; i++){
549
+ int r = name_to_typed_rid(g.argv[i], "");
550
+ db_multi_exec("INSERT OR IGNORE INTO ok(rid) VALUES(%d);", r);
551
+ }
552
+ describe_artifacts_to_stdout("IN ok", 0);
553
+ purge_artifact_list("ok", "", purgeFlags);
554
+ db_end_transaction(0);
555
+ }else if( strncmp(zSubcmd, "cat", n)==0 ){
481556
int i, piid;
482557
Blob content;
483558
if( g.argc<4 ) usage("cat UUID...");
484559
for(i=3; i<g.argc; i++){
485560
piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
@@ -487,12 +562,45 @@
487562
if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
488563
purge_extract_item(piid, &content);
489564
blob_write_to_file(&content, "-");
490565
blob_reset(&content);
491566
}
492
- /* The "checkins" subcommand goes here in alphabetical order, but it must
493
- ** be moved to the end since it is the default case */
567
+ }else if( strncmp(zSubcmd, "checkins", n)==0 ){
568
+ int vid;
569
+ if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
570
+ purgeFlags |= PURGE_EXPLAIN_ONLY;
571
+ }
572
+ verify_all_options();
573
+ db_begin_transaction();
574
+ if( g.argc<=3 ) usage("checkins TAGS... [OPTIONS]");
575
+ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
576
+ for(i=3; i<g.argc; i++){
577
+ int r = name_to_typed_rid(g.argv[i], "br");
578
+ compute_descendants(r, 1000000000);
579
+ }
580
+ vid = db_lget_int("checkout",0);
581
+ if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){
582
+ fossil_fatal("cannot purge the current checkout");
583
+ }
584
+ find_checkin_associates("ok", 1);
585
+ purge_artifact_list("ok", "", purgeFlags);
586
+ db_end_transaction(0);
587
+ }else if( strncmp(zSubcmd, "files", n)==0 ){
588
+ verify_all_options();
589
+ db_begin_transaction();
590
+ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
591
+ for(i=3; i<g.argc; i++){
592
+ db_multi_exec(
593
+ "INSERT OR IGNORE INTO ok(rid) "
594
+ " SELECT fid FROM mlink, filename"
595
+ " WHERE mlink.fnid=filename.fnid"
596
+ " AND (filename.name=%Q OR filename.name GLOB '%q/*')",
597
+ g.argv[i], g.argv[i]
598
+ );
599
+ }
600
+ purge_artifact_list("ok", "", purgeFlags);
601
+ db_end_transaction(0);
494602
}else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){
495603
int showDetail = find_option("l","l",0)!=0;
496604
if( !db_table_exists("repository","purgeevent") ) return;
497605
db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())"
498606
" FROM purgeevent");
@@ -503,11 +611,23 @@
503611
}
504612
}
505613
db_finalize(&q);
506614
}else if( strncmp(zSubcmd, "obliterate", n)==0 ){
507615
int i;
616
+ int bForce = find_option("force","f",0)!=0;
508617
if( g.argc<4 ) usage("obliterate ID...");
618
+ if( !bForce ){
619
+ Blob ans;
620
+ char cReply;
621
+ prompt_user(
622
+ "Obliterating the graveyard will permanently delete information.\n"
623
+ "Changes cannot be undone. Continue (y/N)? ", &ans);
624
+ cReply = blob_str(&ans)[0];
625
+ if( cReply!='y' && cReply!='Y' ){
626
+ fossil_exit(1);
627
+ }
628
+ }
509629
db_begin_transaction();
510630
for(i=3; i<g.argc; i++){
511631
int peid = atoi(g.argv[i]);
512632
if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){
513633
fossil_fatal("no such purge event: %s", g.argv[i]);
@@ -517,66 +637,43 @@
517637
"DELETE FROM purgeitem WHERE peid=%d;",
518638
peid, peid
519639
);
520640
}
521641
db_end_transaction(0);
642
+ }else if( strncmp(zSubcmd, "tickets", n)==0 ){
643
+ fossil_fatal("not yet implemented....");
522644
}else if( strncmp(zSubcmd, "undo", n)==0 ){
523645
int peid;
524646
if( g.argc!=4 ) usage("undo ID");
525647
peid = atoi(g.argv[3]);
526
- db_begin_transaction();
527
- db_multi_exec(
528
- "CREATE TEMP TABLE ix("
529
- " piid INTEGER PRIMARY KEY,"
530
- " srcid INTEGER"
531
- ");"
532
- "CREATE INDEX ixsrcid ON ix(srcid);"
533
- "INSERT INTO ix(piid,srcid) "
534
- " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
535
- peid
536
- );
537
- db_multi_exec(
538
- "DELETE FROM shun"
539
- " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
540
- peid
541
- );
542
- manifest_crosslink_begin();
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);
648
+ if( (purgeFlags & PURGE_EXPLAIN_ONLY)==0 ){
649
+ db_begin_transaction();
650
+ db_multi_exec(
651
+ "CREATE TEMP TABLE ix("
652
+ " piid INTEGER PRIMARY KEY,"
653
+ " srcid INTEGER"
654
+ ");"
655
+ "CREATE INDEX ixsrcid ON ix(srcid);"
656
+ "INSERT INTO ix(piid,srcid) "
657
+ " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
658
+ peid
659
+ );
660
+ db_multi_exec(
661
+ "DELETE FROM shun"
662
+ " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
663
+ peid
664
+ );
665
+ manifest_crosslink_begin();
666
+ purge_item_resurrect(0, 0);
667
+ manifest_crosslink_end(0);
668
+ db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
669
+ db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
670
+ db_end_transaction(0);
671
+ }
672
+ }else if( strncmp(zSubcmd, "wiki", n)==0 ){
673
+ fossil_fatal("not yet implemented....");
674
+ }else{
675
+ fossil_fatal("unknown subcommand \"%s\".\n"
676
+ "should be one of: cat, checkins, files, list, obliterate,"
677
+ " tickets, undo, wiki", zSubcmd);
581678
}
582679
}
583680
--- 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,19 @@
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 +90,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
@@ -98,10 +107,20 @@
98 assert( g.repositoryOpen ); /* Main database must already be open */
99 db_begin_transaction();
100 z = sqlite3_mprintf("IN \"%w\"", zTab);
101 describe_artifacts(z);
102 sqlite3_free(z);
 
 
 
 
 
 
 
 
 
 
103
104 /* Make sure we are not removing a manifest that is the baseline of some
105 ** manifest that is being left behind. This step is not strictly necessary.
106 ** is is just a safety check. */
107 if( purge_baseline_out_from_under_delta(zTab) ){
@@ -121,11 +140,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 );
@@ -189,10 +208,17 @@
189 db_finalize(&q);
190 /* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */
191
192 /* Mission accomplished */
193 db_end_transaction(0);
 
 
 
 
 
 
 
194 return peid;
195 }
196
197 /*
198 ** The TEMP table named zTab contains RIDs for a set of check-ins.
@@ -220,11 +246,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
@@ -422,64 +448,113 @@
422 db_finalize(&q);
423 if( iSrc>0 ) bag_remove(&busy, iSrc);
424 }
425
426 /*
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 **
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 +562,45 @@
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");
@@ -503,11 +611,23 @@
503 }
504 }
505 db_finalize(&q);
506 }else if( strncmp(zSubcmd, "obliterate", n)==0 ){
507 int i;
 
508 if( g.argc<4 ) usage("obliterate ID...");
 
 
 
 
 
 
 
 
 
 
 
509 db_begin_transaction();
510 for(i=3; i<g.argc; i++){
511 int peid = atoi(g.argv[i]);
512 if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){
513 fossil_fatal("no such purge event: %s", g.argv[i]);
@@ -517,66 +637,43 @@
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();
527 db_multi_exec(
528 "CREATE TEMP TABLE ix("
529 " piid INTEGER PRIMARY KEY,"
530 " srcid INTEGER"
531 ");"
532 "CREATE INDEX ixsrcid ON ix(srcid);"
533 "INSERT INTO ix(piid,srcid) "
534 " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
535 peid
536 );
537 db_multi_exec(
538 "DELETE FROM shun"
539 " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
540 peid
541 );
542 manifest_crosslink_begin();
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,19 @@
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 #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.
69 **
70 ** Prior to invoking this routine, the caller must create a (TEMP) table
@@ -81,17 +90,17 @@
90 ** (j) TICKETCHNG
91 ** (7) If any ticket artifacts were removed (6j) then rebuild the
92 ** corresponding ticket entries. Possibly remove entries from
93 ** the ticket table.
94 **
95 ** Steps 1-4 (saving the purged artifacts into the graveyard) are only
96 ** undertaken if the moveToGraveyard flag is true.
97 */
98 int purge_artifact_list(
99 const char *zTab, /* TEMP table containing list of RIDS to be purged */
100 const char *zNote, /* Text of the purgeevent.pnotes field */
101 unsigned purgeFlags /* zero or more PURGE_* flags */
102 ){
103 int peid = 0; /* New purgeevent ID */
104 Stmt q; /* General-use prepared statement */
105 char *z;
106
@@ -98,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) ){
@@ -121,11 +140,11 @@
140 }
141 db_finalize(&q);
142
143 /* Construct the graveyard and copy the artifacts to be purged into the
144 ** graveyard */
145 if( purgeFlags & PURGE_MOVETO_GRAVEYARD ){
146 db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
147 db_name("repository"), db_name("repository"));
148 db_multi_exec(
149 "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
150 );
@@ -189,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.
@@ -220,11 +246,11 @@
246
247
248 /*
249 ** The TEMP table named zTab contains the RIDs for a set of check-in
250 ** artifacts. Expand this set (by adding new entries to zTab) to include
251 ** all other artifacts that are used by the check-ins in
252 ** the original list.
253 **
254 ** If the bExclusive flag is true, then the set is only expanded by
255 ** artifacts that are used exclusively by the check-ins in the set.
256 ** When bExclusive is false, then all artifacts used by the check-ins
@@ -422,64 +448,113 @@
448 db_finalize(&q);
449 if( iSrc>0 ) bag_remove(&busy, iSrc);
450 }
451
452 /*
453 ** COMMAND: purge*
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. The "fossil purge obliterate"
458 ** command empties the graveyard, making the content unrecoverable.
459 **
460 ** ==== WARNING: This command can potentially destroy historical data and ====
461 ** ==== leave your repository in a goofy state. Know what you are doing! ====
462 ** ==== Make a backup of your repository before using this command! ====
463 **
464 ** ==== FURTHER WARNING: This command is a work-in-progress and may yet ====
465 ** ==== contains bugs. ====
466 **
467 ** fossil purge artifacts UUID... ?OPTIONS?
468 **
469 ** Move arbitrary artifacts identified by the UUID list into the
470 ** graveyard.
471 **
472 ** fossil purge cat UUID...
473 **
474 ** Write the content of one or more artifacts in the graveyard onto
475 ** standard output.
476 **
477 ** fossil purge checkins TAGS... ?OPTIONS?
478 **
479 ** Move the check-ins or branches identified by TAGS and all of
480 ** their descendants out of the repository and into the graveyard.
 
 
 
481 ** If TAGS includes a branch name then it means all the check-ins
482 ** on the most recent occurrence of that branch.
483 **
484 ** fossil purge files NAME ... ?OPTIONS?
485 **
486 ** Move all instances of files called NAME into the graveyard.
487 ** NAME should be the name of the file relative to the root of the
488 ** repository. If NAME is a directory, then all files within that
489 ** directory are moved.
490 **
491 ** fossil purge list|ls ?-l?
492 **
493 ** Show the graveyard of prior purges. The -l option gives more
494 ** detail in the output.
495 **
496 ** fossil purge obliterate ID... ?--force?
497 **
498 ** Remove one or more purge events from the graveyard. Once a purge
499 ** event is obliterated, it can no longer be undone. The --force
500 ** option suppresses the confirmation prompt.
501 **
502 ** fossil purge tickets NAME ... ?OPTIONS?
503 **
504 ** TBD...
505 **
506 ** fossil purge undo ID
507 **
508 ** Restore the content previously removed by purge ID.
509 **
510 ** fossil purge wiki NAME ... ?OPTIONS?
511 **
512 ** TBD...
513 **
514 ** COMMON OPTIONS:
515 **
516 ** --explain Make no changes, but show what would happen.
517 ** --dry-run An alias for --explain
518 **
519 ** SUMMARY:
520 ** fossil purge artifacts UUID.. [OPTIONS]
521 ** fossil purge cat UUID...
522 ** fossil purge checkins TAGS... [OPTIONS]
523 ** fossil purge files FILENAME... [OPTIONS]
524 ** fossil purge list
525 ** fossil purge obliterate ID...
526 ** fossil purge tickets NAME... [OPTIONS]
527 ** fossil purge undo ID
528 ** fossil purge wiki NAME... [OPTIONS]
529 */
530 void purge_cmd(void){
531 int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
532 const char *zSubcmd;
533 int n;
534 int i;
535 Stmt q;
536
537 if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?");
538 zSubcmd = g.argv[2];
539 db_find_and_open_repository(0,0);
540 n = (int)strlen(zSubcmd);
541 if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
542 purgeFlags |= PURGE_EXPLAIN_ONLY;
543 }
544 if( strncmp(zSubcmd, "artifacts", n)==0 ){
545 verify_all_options();
546 db_begin_transaction();
547 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
548 for(i=3; i<g.argc; i++){
549 int r = name_to_typed_rid(g.argv[i], "");
550 db_multi_exec("INSERT OR IGNORE INTO ok(rid) VALUES(%d);", r);
551 }
552 describe_artifacts_to_stdout("IN ok", 0);
553 purge_artifact_list("ok", "", purgeFlags);
554 db_end_transaction(0);
555 }else if( strncmp(zSubcmd, "cat", n)==0 ){
556 int i, piid;
557 Blob content;
558 if( g.argc<4 ) usage("cat UUID...");
559 for(i=3; i<g.argc; i++){
560 piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
@@ -487,12 +562,45 @@
562 if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
563 purge_extract_item(piid, &content);
564 blob_write_to_file(&content, "-");
565 blob_reset(&content);
566 }
567 }else if( strncmp(zSubcmd, "checkins", n)==0 ){
568 int vid;
569 if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
570 purgeFlags |= PURGE_EXPLAIN_ONLY;
571 }
572 verify_all_options();
573 db_begin_transaction();
574 if( g.argc<=3 ) usage("checkins TAGS... [OPTIONS]");
575 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
576 for(i=3; i<g.argc; i++){
577 int r = name_to_typed_rid(g.argv[i], "br");
578 compute_descendants(r, 1000000000);
579 }
580 vid = db_lget_int("checkout",0);
581 if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){
582 fossil_fatal("cannot purge the current checkout");
583 }
584 find_checkin_associates("ok", 1);
585 purge_artifact_list("ok", "", purgeFlags);
586 db_end_transaction(0);
587 }else if( strncmp(zSubcmd, "files", n)==0 ){
588 verify_all_options();
589 db_begin_transaction();
590 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
591 for(i=3; i<g.argc; i++){
592 db_multi_exec(
593 "INSERT OR IGNORE INTO ok(rid) "
594 " SELECT fid FROM mlink, filename"
595 " WHERE mlink.fnid=filename.fnid"
596 " AND (filename.name=%Q OR filename.name GLOB '%q/*')",
597 g.argv[i], g.argv[i]
598 );
599 }
600 purge_artifact_list("ok", "", purgeFlags);
601 db_end_transaction(0);
602 }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){
603 int showDetail = find_option("l","l",0)!=0;
604 if( !db_table_exists("repository","purgeevent") ) return;
605 db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())"
606 " FROM purgeevent");
@@ -503,11 +611,23 @@
611 }
612 }
613 db_finalize(&q);
614 }else if( strncmp(zSubcmd, "obliterate", n)==0 ){
615 int i;
616 int bForce = find_option("force","f",0)!=0;
617 if( g.argc<4 ) usage("obliterate ID...");
618 if( !bForce ){
619 Blob ans;
620 char cReply;
621 prompt_user(
622 "Obliterating the graveyard will permanently delete information.\n"
623 "Changes cannot be undone. Continue (y/N)? ", &ans);
624 cReply = blob_str(&ans)[0];
625 if( cReply!='y' && cReply!='Y' ){
626 fossil_exit(1);
627 }
628 }
629 db_begin_transaction();
630 for(i=3; i<g.argc; i++){
631 int peid = atoi(g.argv[i]);
632 if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){
633 fossil_fatal("no such purge event: %s", g.argv[i]);
@@ -517,66 +637,43 @@
637 "DELETE FROM purgeitem WHERE peid=%d;",
638 peid, peid
639 );
640 }
641 db_end_transaction(0);
642 }else if( strncmp(zSubcmd, "tickets", n)==0 ){
643 fossil_fatal("not yet implemented....");
644 }else if( strncmp(zSubcmd, "undo", n)==0 ){
645 int peid;
646 if( g.argc!=4 ) usage("undo ID");
647 peid = atoi(g.argv[3]);
648 if( (purgeFlags & PURGE_EXPLAIN_ONLY)==0 ){
649 db_begin_transaction();
650 db_multi_exec(
651 "CREATE TEMP TABLE ix("
652 " piid INTEGER PRIMARY KEY,"
653 " srcid INTEGER"
654 ");"
655 "CREATE INDEX ixsrcid ON ix(srcid);"
656 "INSERT INTO ix(piid,srcid) "
657 " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
658 peid
659 );
660 db_multi_exec(
661 "DELETE FROM shun"
662 " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
663 peid
664 );
665 manifest_crosslink_begin();
666 purge_item_resurrect(0, 0);
667 manifest_crosslink_end(0);
668 db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
669 db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
670 db_end_transaction(0);
671 }
672 }else if( strncmp(zSubcmd, "wiki", n)==0 ){
673 fossil_fatal("not yet implemented....");
674 }else{
675 fossil_fatal("unknown subcommand \"%s\".\n"
676 "should be one of: cat, checkins, files, list, obliterate,"
677 " tickets, undo, wiki", zSubcmd);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
678 }
679 }
680

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button