Fossil SCM

The "purge" and "purge list" commands appear to be working.

drh 2014-11-25 01:33 UTC DBP-workflow
Commit 92de66d2199ce302553c245f03a6c4815d284d56
+1 -1
--- src/bundle.c
+++ src/bundle.c
@@ -144,11 +144,11 @@
144144
*/
145145
void bundle_cmd(void){
146146
const char *zSubcmd;
147147
const char *zBundleFile;
148148
int n;
149
- if( g.argc<4 ) usage("bundle SUBCOMMAND BUNDLE ?ARGUMENTS?");
149
+ if( g.argc<4 ) usage("SUBCOMMAND BUNDLE ?ARGUMENTS?");
150150
zSubcmd = g.argv[2];
151151
db_find_and_open_repository(0,0);
152152
zBundleFile = g.argv[3];
153153
bundle_attach_file(zBundleFile, "b1", 1);
154154
n = (int)strlen(zSubcmd);
155155
--- src/bundle.c
+++ src/bundle.c
@@ -144,11 +144,11 @@
144 */
145 void bundle_cmd(void){
146 const char *zSubcmd;
147 const char *zBundleFile;
148 int n;
149 if( g.argc<4 ) usage("bundle SUBCOMMAND BUNDLE ?ARGUMENTS?");
150 zSubcmd = g.argv[2];
151 db_find_and_open_repository(0,0);
152 zBundleFile = g.argv[3];
153 bundle_attach_file(zBundleFile, "b1", 1);
154 n = (int)strlen(zSubcmd);
155
--- src/bundle.c
+++ src/bundle.c
@@ -144,11 +144,11 @@
144 */
145 void bundle_cmd(void){
146 const char *zSubcmd;
147 const char *zBundleFile;
148 int n;
149 if( g.argc<4 ) usage("SUBCOMMAND BUNDLE ?ARGUMENTS?");
150 zSubcmd = g.argv[2];
151 db_find_and_open_repository(0,0);
152 zBundleFile = g.argv[3];
153 bundle_attach_file(zBundleFile, "b1", 1);
154 n = (int)strlen(zSubcmd);
155
--- src/descendants.c
+++ src/descendants.c
@@ -289,14 +289,14 @@
289289
}
290290
291291
/*
292292
** COMMAND: descendants*
293293
**
294
-** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS?
294
+** Usage: %fossil descendants ?CHECKIN? ?OPTIONS?
295295
**
296
-** Find all leaf descendants of the baseline specified or if the argument
297
-** is omitted, of the baseline currently checked out.
296
+** Find all leaf descendants of the checkin specified or if the argument
297
+** is omitted, of the checkin currently checked out.
298298
**
299299
** Options:
300300
** -R|--repository FILE Extract info from repository FILE
301301
** -W|--width <num> Width of lines (default is to auto-detect).
302302
** Must be >20 or 0 (= no limit, resulting in a
@@ -334,11 +334,11 @@
334334
"%s"
335335
" AND event.objid IN (SELECT rid FROM leaves)"
336336
" ORDER BY event.mtime DESC",
337337
timeline_query_for_tty()
338338
);
339
- print_timeline(&q, -20, width, 0);
339
+ print_timeline(&q, 0, width, 0);
340340
db_finalize(&q);
341341
}
342342
343343
/*
344344
** COMMAND: leaves*
345345
--- src/descendants.c
+++ src/descendants.c
@@ -289,14 +289,14 @@
289 }
290
291 /*
292 ** COMMAND: descendants*
293 **
294 ** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS?
295 **
296 ** Find all leaf descendants of the baseline specified or if the argument
297 ** is omitted, of the baseline currently checked out.
298 **
299 ** Options:
300 ** -R|--repository FILE Extract info from repository FILE
301 ** -W|--width <num> Width of lines (default is to auto-detect).
302 ** Must be >20 or 0 (= no limit, resulting in a
@@ -334,11 +334,11 @@
334 "%s"
335 " AND event.objid IN (SELECT rid FROM leaves)"
336 " ORDER BY event.mtime DESC",
337 timeline_query_for_tty()
338 );
339 print_timeline(&q, -20, width, 0);
340 db_finalize(&q);
341 }
342
343 /*
344 ** COMMAND: leaves*
345
--- src/descendants.c
+++ src/descendants.c
@@ -289,14 +289,14 @@
289 }
290
291 /*
292 ** COMMAND: descendants*
293 **
294 ** Usage: %fossil descendants ?CHECKIN? ?OPTIONS?
295 **
296 ** Find all leaf descendants of the checkin specified or if the argument
297 ** is omitted, of the checkin currently checked out.
298 **
299 ** Options:
300 ** -R|--repository FILE Extract info from repository FILE
301 ** -W|--width <num> Width of lines (default is to auto-detect).
302 ** Must be >20 or 0 (= no limit, resulting in a
@@ -334,11 +334,11 @@
334 "%s"
335 " AND event.objid IN (SELECT rid FROM leaves)"
336 " ORDER BY event.mtime DESC",
337 timeline_query_for_tty()
338 );
339 print_timeline(&q, 0, width, 0);
340 db_finalize(&q);
341 }
342
343 /*
344 ** COMMAND: leaves*
345
+1 -1
--- src/name.c
+++ src/name.c
@@ -505,11 +505,11 @@
505505
}
506506
507507
/*
508508
** Generate a description of artifact "rid"
509509
*/
510
-static void whatis_rid(int rid, int verboseFlag){
510
+void whatis_rid(int rid, int verboseFlag){
511511
Stmt q;
512512
int cnt;
513513
514514
/* Basic information about the object. */
515515
db_prepare(&q,
516516
--- src/name.c
+++ src/name.c
@@ -505,11 +505,11 @@
505 }
506
507 /*
508 ** Generate a description of artifact "rid"
509 */
510 static void whatis_rid(int rid, int verboseFlag){
511 Stmt q;
512 int cnt;
513
514 /* Basic information about the object. */
515 db_prepare(&q,
516
--- src/name.c
+++ src/name.c
@@ -505,11 +505,11 @@
505 }
506
507 /*
508 ** Generate a description of artifact "rid"
509 */
510 void whatis_rid(int rid, int verboseFlag){
511 Stmt q;
512 int cnt;
513
514 /* Basic information about the object. */
515 db_prepare(&q,
516
+194 -6
--- src/purge.c
+++ src/purge.c
@@ -37,11 +37,11 @@
3737
*/
3838
static const char zPurgeInit[] =
3939
@ CREATE TABLE IF NOT EXISTS "%w".purgeevent(
4040
@ peid INTEGER PRIMARY KEY, -- Unique ID for the purge event
4141
@ ctime DATETIME, -- Julian day number when purge occurred
42
-@ pnotes TEXT, -- Human-readable notes about the purge event
42
+@ pnotes TEXT -- Human-readable notes about the purge event
4343
@ );
4444
@ CREATE TABLE IF NOT EXISTS "%w".purgeitem(
4545
@ peid INTEGER REFERENCES purgeevent ON DELETE CASCADE, -- Purge event
4646
@ uuid TEXT NOT NULL, -- SHA1 hash of the purged artifact
4747
@ sz INT NOT NULL, -- Uncompressed size of the purged artifact
@@ -76,26 +76,32 @@
7676
** (j) TICKETCHNG
7777
** (7) If any ticket artifacts were removed (6j) then rebuild the
7878
** corresponding ticket entries. Possibly remove entries from
7979
** the ticket table.
8080
*/
81
-void purge_artifact_list(
81
+int purge_artifact_list(
8282
const char *zTab, /* TEMP table containing list of RIDS to be purged */
8383
const char *zNote /* Text of the purgeevent.pnotes field */
8484
){
8585
int peid = 0; /* New purgeevent ID */
8686
Stmt q; /* General-use prepared statement */
8787
8888
assert( g.repositoryOpen ); /* Main database must already be open */
89
+ add_content_sql_commands(g.db);
8990
db_begin_transaction();
91
+ if( purge_baseline_out_from_under_delta(zTab) ){
92
+ fossil_fatal("attempt to purge a baseline manifest without also purging "
93
+ "all of its deltas");
94
+ }
9095
db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
9196
db_name("repository"), db_name("repository"));
9297
db_multi_exec(
9398
"INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
9499
);
95100
peid = db_last_insert_rowid();
96
- db_prepare(&q, "SELECT rid FROM delta WHERE srcid IN \"%w\"", zTab);
101
+ db_prepare(&q, "SELECT rid FROM delta WHERE srcid IN \"%w\""
102
+ " AND rid NOT IN \"%w\"", zTab, zTab);
97103
while( db_step(&q)==SQLITE_ROW ){
98104
int rid = db_column_int(&q, 0);
99105
content_undelta(rid);
100106
verify_before_commit(rid);
101107
}
@@ -133,18 +139,200 @@
133139
ticket_rebuild_entry(db_column_text(&q, 0));
134140
}
135141
db_finalize(&q);
136142
db_multi_exec("DROP TABLE \"%w_tickets\"", zTab);
137143
db_end_transaction(0);
144
+ return peid;
145
+}
146
+
147
+/*
148
+** The TEMP table named zTab contains RIDs for a set of checkins.
149
+**
150
+** Check to see if any checkin in zTab is a baseline manifest for some
151
+** delta manifest that is not in zTab. Return true if zTab contains a
152
+** baseline for a delta that is not in zTab.
153
+**
154
+** This is a database integrity preservation check. The checkins in zTab
155
+** are about to be deleted or otherwise made inaccessible. This routine
156
+** is checking to ensure that purging the checkins in zTab will not delete
157
+** a baseline manifest out from under a delta.
158
+*/
159
+int purge_baseline_out_from_under_delta(const char *zTab){
160
+ return db_int(0,
161
+ "SELECT 1 FROM plink WHERE baseid IN \"%w\" AND cid NOT IN \"%w\"",
162
+ zTab, zTab);
138163
}
164
+
139165
140166
/*
141167
** The TEMP table named zTab contains the RIDs for a set of checkin
142168
** artifacts. Expand this set (by adding new entries to zTab) to include
143
-** all other facts that are used exclusively by the set of checkins in
169
+** all other artifacts that are used exclusively by the set of checkins in
144170
** the original list.
145171
*/
146
-void purge_checkin_associates(const char *zTab){
172
+void find_checkin_associates(const char *zTab){
147173
db_begin_transaction();
148
-
174
+
175
+ /* Compute the set of files that need to be added to zTab */
176
+ db_multi_exec("CREATE TEMP TABLE \"%w_files\"(fid INTEGER PRIMARY KEY)",zTab);
177
+ db_multi_exec(
178
+ "INSERT OR IGNORE INTO \"%w_files\"(fid)"
179
+ " SELECT fid FROM mlink WHERE fid!=0 AND mid IN \"%w\"",
180
+ zTab, zTab
181
+ );
182
+ /* But take out all files that are referenced by check-ins not in zTab */
183
+ db_multi_exec(
184
+ "DELETE FROM \"%w_files\""
185
+ " WHERE fid IN (SELECT fid FROM mlink"
186
+ " WHERE fid IN \"%w_files\""
187
+ " AND mid NOT IN \"%w\")",
188
+ zTab, zTab, zTab
189
+ );
190
+
191
+ /* Compute the set of tags that need to be added to zTag */
192
+ db_multi_exec("CREATE TEMP TABLE \"%w_tags\"(tid INTEGER PRIMARY KEY)",zTab);
193
+ db_multi_exec(
194
+ "INSERT OR IGNORE INTO \"%w_tags\"(tid)"
195
+ " SELECT DISTINCT srcid FROM tagxref WHERE rid in \"%w\" AND srcid!=0",
196
+ zTab, zTab
197
+ );
198
+ /* But take out tags that references some check-ins in zTab and other
199
+ ** check-ins not in zTab. The current Fossil implementation never creates
200
+ ** such tags, so the following should usually be a no-op. But the file
201
+ ** format specification allows such tags, so we should check for them.
202
+ */
203
+ db_multi_exec(
204
+ "DELETE FROM \"%w_tags\""
205
+ " WHERE tid IN (SELECT srcid FROM tagxref"
206
+ " WHERE srcid IN \"%w_tags\""
207
+ " AND rid NOT IN \"%w\")",
208
+ zTab, zTab, zTab
209
+ );
210
+
211
+ /* Transfer the extra artifacts into zTab */
212
+ db_multi_exec(
213
+ "INSERT OR IGNORE INTO \"%w\" SELECT fid FROM \"%w_files\";"
214
+ "INSERT OR IGNORE INTO \"%w\" SELECT tid FROM \"%w_tags\";"
215
+ "DROP TABLE \"%w_files\";"
216
+ "DROP TABLE \"%w_tags\";",
217
+ zTab, zTab, zTab, zTab, zTab, zTab
218
+ );
219
+
149220
db_end_transaction(0);
150221
}
222
+
223
+/*
224
+** Display the content of a single purge event.
225
+*/
226
+static void purge_event_content(int peid){
227
+ Stmt q;
228
+ sqlite3_int64 sz1 = 0;
229
+ sqlite3_int64 sz2 = 0;
230
+ db_prepare(&q, "SELECT uuid, sz, length(data) FROM purgeitem WHERE peid=%d",
231
+ peid);
232
+ while( db_step(&q)==SQLITE_ROW ){
233
+ fossil_print(" %s %10d %10d\n",
234
+ db_column_text(&q,0),
235
+ db_column_int(&q,1),
236
+ db_column_int(&q,2));
237
+ sz1 += db_column_int(&q,1);
238
+ sz2 += db_column_int(&q,2);
239
+ }
240
+ db_finalize(&q);
241
+ fossil_print(" %40s %10lld %10lld\n", "", sz1, sz2);
242
+}
243
+
244
+/*
245
+** COMMAND: purge
246
+**
247
+** The purge command is used to remove content from a repository into a
248
+** "graveyard" and also to show manage the graveyard and optionally restored
249
+** content into the repository from the graveyard.
250
+**
251
+** fossil purge list|ls [-l]
252
+**
253
+** Show the graveyard of prior purges.
254
+**
255
+** fossil purge undo ID
256
+**
257
+** Restore the content previously removed by purge ID.
258
+**
259
+** fossil purge [checkin] TAGS... [--explain]
260
+**
261
+** Move the checkins identified by TAGS and all of their descendants
262
+** out of the repository and into the graveyard. If the --explain option
263
+** is included, then the repository and graveyard are unchanged and
264
+** an explaination of what would have happened is shown instead.
265
+**
266
+** SUMMARY:
267
+** fossil purge [checkin] TAGS... [--explain]
268
+** fossil purge list
269
+** fossil purge undo ID
270
+*/
271
+void purge_cmd(void){
272
+ const char *zSubcmd;
273
+ int n;
274
+ Stmt q;
275
+ if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?");
276
+ zSubcmd = g.argv[2];
277
+ db_find_and_open_repository(0,0);
278
+ n = (int)strlen(zSubcmd);
279
+ if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){
280
+ int showDetail = find_option("l","l",0)!=0;
281
+ if( db_int(-1,"PRAGMA table_info('purgeevent')")<0 ) return;
282
+ db_prepare(&q, "SELECT peid, datetime(ctime) FROM purgeevent");
283
+ while( db_step(&q)==SQLITE_ROW ){
284
+ fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1));
285
+ if( showDetail ){
286
+ purge_event_content(db_column_int(&q,0));
287
+ }
288
+ }
289
+ db_finalize(&q);
290
+ }else if( strncmp(zSubcmd, "undo", n)==0 ){
291
+ fossil_print("Not yet implemented...\n");
292
+ }else{
293
+ int explainOnly = find_option("explain",0,0)!=0;
294
+ int dryRun = find_option("dry-run",0,0)!=0;
295
+ const char *zTag;
296
+ int i;
297
+ int vid;
298
+ int nCkin;
299
+ int nArtifact;
300
+ verify_all_options();
301
+ db_begin_transaction();
302
+ i = strncmp(zSubcmd,"checkin",n)==0 ? 3 : 2;
303
+ if( i>=g.argc ) usage("[checkin] TAGS... [--explain]");
304
+ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
305
+ for(; i<g.argc; i++){
306
+ int r = symbolic_name_to_rid(g.argv[i], "ci");
307
+ if( r>0 ){
308
+ compute_descendants(r, 1000000000);
309
+ }else if( r==0 ){
310
+ fossil_fatal("not found: %s", g.argv[i]);
311
+ }else{
312
+ fossil_fatal("ambiguous: %s\n", g.argv[i]);
313
+ }
314
+ }
315
+ vid = db_lget_int("checkout",0);
316
+ if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){
317
+ fossil_fatal("cannot purge the current checkout");
318
+ }
319
+ nCkin = db_int(0, "SELECT count(*) FROM ok");
320
+ find_checkin_associates("ok");
321
+ nArtifact = db_int(0, "SELECT count(*) FROM ok");
322
+ if( explainOnly ){
323
+ i = 0;
324
+ db_prepare(&q, "SELECT rid FROM ok");
325
+ while( db_step(&q)==SQLITE_ROW ){
326
+ if( i++ > 0 ) fossil_print("%.78c\n",'-');
327
+ whatis_rid(db_column_int(&q,0), 0);
328
+ }
329
+ db_finalize(&q);
330
+ }else{
331
+ int peid = purge_artifact_list("ok","");
332
+ fossil_print("%d checkins and %d artifacts purged.\n", nCkin, nArtifact);
333
+ fossil_print("undoable using \"%s purge undo %d\".\n",
334
+ g.nameOfExe, peid);
335
+ }
336
+ db_end_transaction(explainOnly||dryRun);
337
+ }
338
+}
151339
--- src/purge.c
+++ src/purge.c
@@ -37,11 +37,11 @@
37 */
38 static const char zPurgeInit[] =
39 @ CREATE TABLE IF NOT EXISTS "%w".purgeevent(
40 @ peid INTEGER PRIMARY KEY, -- Unique ID for the purge event
41 @ ctime DATETIME, -- Julian day number when purge occurred
42 @ pnotes TEXT, -- Human-readable notes about the purge event
43 @ );
44 @ CREATE TABLE IF NOT EXISTS "%w".purgeitem(
45 @ peid INTEGER REFERENCES purgeevent ON DELETE CASCADE, -- Purge event
46 @ uuid TEXT NOT NULL, -- SHA1 hash of the purged artifact
47 @ sz INT NOT NULL, -- Uncompressed size of the purged artifact
@@ -76,26 +76,32 @@
76 ** (j) TICKETCHNG
77 ** (7) If any ticket artifacts were removed (6j) then rebuild the
78 ** corresponding ticket entries. Possibly remove entries from
79 ** the ticket table.
80 */
81 void purge_artifact_list(
82 const char *zTab, /* TEMP table containing list of RIDS to be purged */
83 const char *zNote /* Text of the purgeevent.pnotes field */
84 ){
85 int peid = 0; /* New purgeevent ID */
86 Stmt q; /* General-use prepared statement */
87
88 assert( g.repositoryOpen ); /* Main database must already be open */
 
89 db_begin_transaction();
 
 
 
 
90 db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
91 db_name("repository"), db_name("repository"));
92 db_multi_exec(
93 "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
94 );
95 peid = db_last_insert_rowid();
96 db_prepare(&q, "SELECT rid FROM delta WHERE srcid IN \"%w\"", zTab);
 
97 while( db_step(&q)==SQLITE_ROW ){
98 int rid = db_column_int(&q, 0);
99 content_undelta(rid);
100 verify_before_commit(rid);
101 }
@@ -133,18 +139,200 @@
133 ticket_rebuild_entry(db_column_text(&q, 0));
134 }
135 db_finalize(&q);
136 db_multi_exec("DROP TABLE \"%w_tickets\"", zTab);
137 db_end_transaction(0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138 }
 
139
140 /*
141 ** The TEMP table named zTab contains the RIDs for a set of checkin
142 ** artifacts. Expand this set (by adding new entries to zTab) to include
143 ** all other facts that are used exclusively by the set of checkins in
144 ** the original list.
145 */
146 void purge_checkin_associates(const char *zTab){
147 db_begin_transaction();
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149 db_end_transaction(0);
150 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
--- src/purge.c
+++ src/purge.c
@@ -37,11 +37,11 @@
37 */
38 static const char zPurgeInit[] =
39 @ CREATE TABLE IF NOT EXISTS "%w".purgeevent(
40 @ peid INTEGER PRIMARY KEY, -- Unique ID for the purge event
41 @ ctime DATETIME, -- Julian day number when purge occurred
42 @ pnotes TEXT -- Human-readable notes about the purge event
43 @ );
44 @ CREATE TABLE IF NOT EXISTS "%w".purgeitem(
45 @ peid INTEGER REFERENCES purgeevent ON DELETE CASCADE, -- Purge event
46 @ uuid TEXT NOT NULL, -- SHA1 hash of the purged artifact
47 @ sz INT NOT NULL, -- Uncompressed size of the purged artifact
@@ -76,26 +76,32 @@
76 ** (j) TICKETCHNG
77 ** (7) If any ticket artifacts were removed (6j) then rebuild the
78 ** corresponding ticket entries. Possibly remove entries from
79 ** the ticket table.
80 */
81 int purge_artifact_list(
82 const char *zTab, /* TEMP table containing list of RIDS to be purged */
83 const char *zNote /* Text of the purgeevent.pnotes field */
84 ){
85 int peid = 0; /* New purgeevent ID */
86 Stmt q; /* General-use prepared statement */
87
88 assert( g.repositoryOpen ); /* Main database must already be open */
89 add_content_sql_commands(g.db);
90 db_begin_transaction();
91 if( purge_baseline_out_from_under_delta(zTab) ){
92 fossil_fatal("attempt to purge a baseline manifest without also purging "
93 "all of its deltas");
94 }
95 db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
96 db_name("repository"), db_name("repository"));
97 db_multi_exec(
98 "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
99 );
100 peid = db_last_insert_rowid();
101 db_prepare(&q, "SELECT rid FROM delta WHERE srcid IN \"%w\""
102 " AND rid NOT IN \"%w\"", zTab, zTab);
103 while( db_step(&q)==SQLITE_ROW ){
104 int rid = db_column_int(&q, 0);
105 content_undelta(rid);
106 verify_before_commit(rid);
107 }
@@ -133,18 +139,200 @@
139 ticket_rebuild_entry(db_column_text(&q, 0));
140 }
141 db_finalize(&q);
142 db_multi_exec("DROP TABLE \"%w_tickets\"", zTab);
143 db_end_transaction(0);
144 return peid;
145 }
146
147 /*
148 ** The TEMP table named zTab contains RIDs for a set of checkins.
149 **
150 ** Check to see if any checkin in zTab is a baseline manifest for some
151 ** delta manifest that is not in zTab. Return true if zTab contains a
152 ** baseline for a delta that is not in zTab.
153 **
154 ** This is a database integrity preservation check. The checkins in zTab
155 ** are about to be deleted or otherwise made inaccessible. This routine
156 ** is checking to ensure that purging the checkins in zTab will not delete
157 ** a baseline manifest out from under a delta.
158 */
159 int purge_baseline_out_from_under_delta(const char *zTab){
160 return db_int(0,
161 "SELECT 1 FROM plink WHERE baseid IN \"%w\" AND cid NOT IN \"%w\"",
162 zTab, zTab);
163 }
164
165
166 /*
167 ** The TEMP table named zTab contains the RIDs for a set of checkin
168 ** artifacts. Expand this set (by adding new entries to zTab) to include
169 ** all other artifacts that are used exclusively by the set of checkins in
170 ** the original list.
171 */
172 void find_checkin_associates(const char *zTab){
173 db_begin_transaction();
174
175 /* Compute the set of files that need to be added to zTab */
176 db_multi_exec("CREATE TEMP TABLE \"%w_files\"(fid INTEGER PRIMARY KEY)",zTab);
177 db_multi_exec(
178 "INSERT OR IGNORE INTO \"%w_files\"(fid)"
179 " SELECT fid FROM mlink WHERE fid!=0 AND mid IN \"%w\"",
180 zTab, zTab
181 );
182 /* But take out all files that are referenced by check-ins not in zTab */
183 db_multi_exec(
184 "DELETE FROM \"%w_files\""
185 " WHERE fid IN (SELECT fid FROM mlink"
186 " WHERE fid IN \"%w_files\""
187 " AND mid NOT IN \"%w\")",
188 zTab, zTab, zTab
189 );
190
191 /* Compute the set of tags that need to be added to zTag */
192 db_multi_exec("CREATE TEMP TABLE \"%w_tags\"(tid INTEGER PRIMARY KEY)",zTab);
193 db_multi_exec(
194 "INSERT OR IGNORE INTO \"%w_tags\"(tid)"
195 " SELECT DISTINCT srcid FROM tagxref WHERE rid in \"%w\" AND srcid!=0",
196 zTab, zTab
197 );
198 /* But take out tags that references some check-ins in zTab and other
199 ** check-ins not in zTab. The current Fossil implementation never creates
200 ** such tags, so the following should usually be a no-op. But the file
201 ** format specification allows such tags, so we should check for them.
202 */
203 db_multi_exec(
204 "DELETE FROM \"%w_tags\""
205 " WHERE tid IN (SELECT srcid FROM tagxref"
206 " WHERE srcid IN \"%w_tags\""
207 " AND rid NOT IN \"%w\")",
208 zTab, zTab, zTab
209 );
210
211 /* Transfer the extra artifacts into zTab */
212 db_multi_exec(
213 "INSERT OR IGNORE INTO \"%w\" SELECT fid FROM \"%w_files\";"
214 "INSERT OR IGNORE INTO \"%w\" SELECT tid FROM \"%w_tags\";"
215 "DROP TABLE \"%w_files\";"
216 "DROP TABLE \"%w_tags\";",
217 zTab, zTab, zTab, zTab, zTab, zTab
218 );
219
220 db_end_transaction(0);
221 }
222
223 /*
224 ** Display the content of a single purge event.
225 */
226 static void purge_event_content(int peid){
227 Stmt q;
228 sqlite3_int64 sz1 = 0;
229 sqlite3_int64 sz2 = 0;
230 db_prepare(&q, "SELECT uuid, sz, length(data) FROM purgeitem WHERE peid=%d",
231 peid);
232 while( db_step(&q)==SQLITE_ROW ){
233 fossil_print(" %s %10d %10d\n",
234 db_column_text(&q,0),
235 db_column_int(&q,1),
236 db_column_int(&q,2));
237 sz1 += db_column_int(&q,1);
238 sz2 += db_column_int(&q,2);
239 }
240 db_finalize(&q);
241 fossil_print(" %40s %10lld %10lld\n", "", sz1, sz2);
242 }
243
244 /*
245 ** COMMAND: purge
246 **
247 ** The purge command is used to remove content from a repository into a
248 ** "graveyard" and also to show manage the graveyard and optionally restored
249 ** content into the repository from the graveyard.
250 **
251 ** fossil purge list|ls [-l]
252 **
253 ** Show the graveyard of prior purges.
254 **
255 ** fossil purge undo ID
256 **
257 ** Restore the content previously removed by purge ID.
258 **
259 ** fossil purge [checkin] TAGS... [--explain]
260 **
261 ** Move the checkins identified by TAGS and all of their descendants
262 ** out of the repository and into the graveyard. If the --explain option
263 ** is included, then the repository and graveyard are unchanged and
264 ** an explaination of what would have happened is shown instead.
265 **
266 ** SUMMARY:
267 ** fossil purge [checkin] TAGS... [--explain]
268 ** fossil purge list
269 ** fossil purge undo ID
270 */
271 void purge_cmd(void){
272 const char *zSubcmd;
273 int n;
274 Stmt q;
275 if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?");
276 zSubcmd = g.argv[2];
277 db_find_and_open_repository(0,0);
278 n = (int)strlen(zSubcmd);
279 if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){
280 int showDetail = find_option("l","l",0)!=0;
281 if( db_int(-1,"PRAGMA table_info('purgeevent')")<0 ) return;
282 db_prepare(&q, "SELECT peid, datetime(ctime) FROM purgeevent");
283 while( db_step(&q)==SQLITE_ROW ){
284 fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1));
285 if( showDetail ){
286 purge_event_content(db_column_int(&q,0));
287 }
288 }
289 db_finalize(&q);
290 }else if( strncmp(zSubcmd, "undo", n)==0 ){
291 fossil_print("Not yet implemented...\n");
292 }else{
293 int explainOnly = find_option("explain",0,0)!=0;
294 int dryRun = find_option("dry-run",0,0)!=0;
295 const char *zTag;
296 int i;
297 int vid;
298 int nCkin;
299 int nArtifact;
300 verify_all_options();
301 db_begin_transaction();
302 i = strncmp(zSubcmd,"checkin",n)==0 ? 3 : 2;
303 if( i>=g.argc ) usage("[checkin] TAGS... [--explain]");
304 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
305 for(; i<g.argc; i++){
306 int r = symbolic_name_to_rid(g.argv[i], "ci");
307 if( r>0 ){
308 compute_descendants(r, 1000000000);
309 }else if( r==0 ){
310 fossil_fatal("not found: %s", g.argv[i]);
311 }else{
312 fossil_fatal("ambiguous: %s\n", g.argv[i]);
313 }
314 }
315 vid = db_lget_int("checkout",0);
316 if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){
317 fossil_fatal("cannot purge the current checkout");
318 }
319 nCkin = db_int(0, "SELECT count(*) FROM ok");
320 find_checkin_associates("ok");
321 nArtifact = db_int(0, "SELECT count(*) FROM ok");
322 if( explainOnly ){
323 i = 0;
324 db_prepare(&q, "SELECT rid FROM ok");
325 while( db_step(&q)==SQLITE_ROW ){
326 if( i++ > 0 ) fossil_print("%.78c\n",'-');
327 whatis_rid(db_column_int(&q,0), 0);
328 }
329 db_finalize(&q);
330 }else{
331 int peid = purge_artifact_list("ok","");
332 fossil_print("%d checkins and %d artifacts purged.\n", nCkin, nArtifact);
333 fossil_print("undoable using \"%s purge undo %d\".\n",
334 g.nameOfExe, peid);
335 }
336 db_end_transaction(explainOnly||dryRun);
337 }
338 }
339
+14 -6
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -106,10 +106,23 @@
106106
sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
107107
}else{
108108
sqlite3_result_error(context, "input is not zlib compressed", -1);
109109
}
110110
}
111
+
112
+/*
113
+** Add the content(), compress(), and decompress() SQL functions to
114
+** database connection db.
115
+*/
116
+int add_content_sql_commands(sqlite3 *db){
117
+ sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0,
118
+ sqlcmd_content, 0, 0);
119
+ sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
120
+ sqlcmd_compress, 0, 0);
121
+ sqlite3_create_function(db, "decompress", 1, SQLITE_UTF8, 0,
122
+ sqlcmd_decompress, 0, 0);
123
+}
111124
112125
/*
113126
** This is the "automatic extension" initializer that runs right after
114127
** the connection to the repository database is opened. Set up the
115128
** database connection to be more useful to the human operator.
@@ -117,16 +130,11 @@
117130
static int sqlcmd_autoinit(
118131
sqlite3 *db,
119132
const char **pzErrMsg,
120133
const void *notUsed
121134
){
122
- sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0,
123
- sqlcmd_content, 0, 0);
124
- sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
125
- sqlcmd_compress, 0, 0);
126
- sqlite3_create_function(db, "decompress", 1, SQLITE_UTF8, 0,
127
- sqlcmd_decompress, 0, 0);
135
+ add_content_sql_commands(db);
128136
re_add_sql_func(db);
129137
g.repositoryOpen = 1;
130138
g.db = db;
131139
return SQLITE_OK;
132140
}
133141
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -106,10 +106,23 @@
106 sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
107 }else{
108 sqlite3_result_error(context, "input is not zlib compressed", -1);
109 }
110 }
 
 
 
 
 
 
 
 
 
 
 
 
 
111
112 /*
113 ** This is the "automatic extension" initializer that runs right after
114 ** the connection to the repository database is opened. Set up the
115 ** database connection to be more useful to the human operator.
@@ -117,16 +130,11 @@
117 static int sqlcmd_autoinit(
118 sqlite3 *db,
119 const char **pzErrMsg,
120 const void *notUsed
121 ){
122 sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0,
123 sqlcmd_content, 0, 0);
124 sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
125 sqlcmd_compress, 0, 0);
126 sqlite3_create_function(db, "decompress", 1, SQLITE_UTF8, 0,
127 sqlcmd_decompress, 0, 0);
128 re_add_sql_func(db);
129 g.repositoryOpen = 1;
130 g.db = db;
131 return SQLITE_OK;
132 }
133
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -106,10 +106,23 @@
106 sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
107 }else{
108 sqlite3_result_error(context, "input is not zlib compressed", -1);
109 }
110 }
111
112 /*
113 ** Add the content(), compress(), and decompress() SQL functions to
114 ** database connection db.
115 */
116 int add_content_sql_commands(sqlite3 *db){
117 sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0,
118 sqlcmd_content, 0, 0);
119 sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
120 sqlcmd_compress, 0, 0);
121 sqlite3_create_function(db, "decompress", 1, SQLITE_UTF8, 0,
122 sqlcmd_decompress, 0, 0);
123 }
124
125 /*
126 ** This is the "automatic extension" initializer that runs right after
127 ** the connection to the repository database is opened. Set up the
128 ** database connection to be more useful to the human operator.
@@ -117,16 +130,11 @@
130 static int sqlcmd_autoinit(
131 sqlite3 *db,
132 const char **pzErrMsg,
133 const void *notUsed
134 ){
135 add_content_sql_commands(db);
 
 
 
 
 
136 re_add_sql_func(db);
137 g.repositoryOpen = 1;
138 g.db = db;
139 return SQLITE_OK;
140 }
141

Keyboard Shortcuts

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