Fossil SCM

Implement the "fossil purge undo" command.

drh 2014-11-25 16:07 UTC DBP-workflow
Commit eb36d28acdec4c485a3f8e4022f2ac1dbdce839d
1 file changed +86 -14
+86 -14
--- src/purge.c
+++ src/purge.c
@@ -271,37 +271,34 @@
271271
** Extract the content for purgeitem number piid into a Blob. Return
272272
** the number of errors.
273273
*/
274274
static int purge_extract_item(
275275
int piid, /* ID of the item to extract */
276
- Blob *pOut, /* Write the content into this blob */
277
- Blob *pHash, /* If not NULL, write the hash into this blob */
278
- int *pIsPrivate /* If not NULL, write the isPrivate flag here */
276
+ Blob *pOut /* Write the content into this blob */
279277
){
280278
Stmt q;
281279
int srcid;
282280
Blob h1, h2, x;
283281
static Bag busy;
284282
285
- db_prepare(&q, "SELECT uuid, srcid, isPrivate, data FROM purgeitem"
283
+ db_prepare(&q, "SELECT uuid, srcid, data FROM purgeitem"
286284
" WHERE piid=%d", piid);
287285
if( db_step(&q)!=SQLITE_ROW ){
288286
db_finalize(&q);
289287
fossil_fatal("missing purge-item %d", piid);
290288
}
291289
if( bag_find(&busy, piid) ) return 1;
292
- if( pIsPrivate ) *pIsPrivate = db_column_int(&q, 2);
293290
srcid = db_column_int(&q, 1);
294291
blob_zero(pOut);
295292
blob_zero(&x);
296
- db_column_blob(&q, 3, &x);
293
+ db_column_blob(&q, 2, &x);
297294
blob_uncompress(&x, pOut);
298295
blob_reset(&x);
299296
if( srcid>0 ){
300297
Blob baseline, out;
301298
bag_insert(&busy, piid);
302
- purge_extract_item(srcid, &baseline, 0, 0);
299
+ purge_extract_item(srcid, &baseline);
303300
blob_zero(&out);
304301
blob_delta_apply(&baseline, pOut, &out);
305302
blob_reset(pOut);
306303
*pOut = out;
307304
blob_reset(&baseline);
@@ -312,19 +309,75 @@
312309
sha1sum_blob(pOut, &h2);
313310
if( blob_compare(&h1, &h2)!=0 ){
314311
fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
315312
blob_str(&h1), blob_str(&h2));
316313
}
317
- if( pHash ){
318
- *pHash = h1;
319
- }else{
320
- blob_reset(&h1);
321
- }
314
+ blob_reset(&h1);
322315
blob_reset(&h2);
323316
db_finalize(&q);
324317
return 0;
325318
}
319
+
320
+/*
321
+** There is a TEMP table ix(piid,srcid) containing a set of purgeitems
322
+** that need to be transferred to the BLOB table. This routine does
323
+** all items that have srcid=iSrc. The pBasis blob holds the content
324
+** of the source document if iSrc>0.
325
+*/
326
+static void purge_item_resurrect(int iSrc, Blob *pBasis){
327
+ Stmt q;
328
+ static Bag busy;
329
+ assert( pBasis!=0 || iSrc==0 );
330
+ if( iSrc>0 ){
331
+ if( bag_find(&busy, iSrc) ){
332
+ fossil_fatal("delta loop while uncompressing purged artifacts");
333
+ }
334
+ bag_insert(&busy, iSrc);
335
+ }
336
+ db_prepare(&q,
337
+ "SELECT uuid, data, isPrivate, ix.piid"
338
+ " FROM ix, purgeitem"
339
+ " WHERE ix.srcid=%d"
340
+ " AND ix.piid=purgeitem.piid;",
341
+ iSrc
342
+ );
343
+ while( db_step(&q)==SQLITE_ROW ){
344
+ Blob h1, h2, c1, c2;
345
+ int isPriv, rid;
346
+ blob_zero(&h1);
347
+ db_column_blob(&q, 0, &h1);
348
+ blob_zero(&c1);
349
+ db_column_blob(&q, 1, &c1);
350
+ blob_uncompress(&c1, &c1);
351
+ blob_zero(&c2);
352
+ if( pBasis ){
353
+ blob_delta_apply(pBasis, &c1, &c2);
354
+ blob_reset(&c1);
355
+ }else{
356
+ c2 = c1;
357
+ }
358
+ sha1sum_blob(&c2, &h2);
359
+ if( blob_compare(&h1, &h2)!=0 ){
360
+ fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
361
+ blob_str(&h1), blob_str(&h2));
362
+ }
363
+ blob_reset(&h2);
364
+ isPriv = db_column_int(&q, 2);
365
+ rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv);
366
+ if( rid==0 ){
367
+ fossil_fatal("%s", g.zErrMsg);
368
+ }else{
369
+ if( !isPriv ) content_make_public(rid);
370
+ content_get(rid, &c1);
371
+ manifest_crosslink(rid, &c1, MC_NO_ERRORS);
372
+ }
373
+ purge_item_resurrect(db_column_int(&q,3), &c2);
374
+ blob_reset(&c2);
375
+ }
376
+ db_finalize(&q);
377
+ if( iSrc>0 ) bag_remove(&busy, iSrc);
378
+}
326379
327380
/*
328381
** COMMAND: purge
329382
**
330383
** The purge command is used to remove content from a repository into a
@@ -377,21 +430,40 @@
377430
purge_list_event_content(db_column_int(&q,0));
378431
}
379432
}
380433
db_finalize(&q);
381434
}else if( strncmp(zSubcmd, "undo", n)==0 ){
382
- fossil_print("Not yet implemented...\n");
435
+ int peid;
436
+ if( g.argc!=4 ) usage("undo ID");
437
+ peid = atoi(g.argv[3]);
438
+ db_begin_transaction();
439
+ db_multi_exec(
440
+ "CREATE TEMP TABLE ix("
441
+ " piid INTEGER PRIMARY KEY,"
442
+ " srcid INTEGER"
443
+ ");"
444
+ "CREATE INDEX ixsrcid ON ix(srcid);"
445
+ "INSERT INTO ix(piid,srcid) "
446
+ " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
447
+ peid
448
+ );
449
+ manifest_crosslink_begin();
450
+ purge_item_resurrect(0, 0);
451
+ manifest_crosslink_end(0);
452
+ db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
453
+ db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
454
+ db_end_transaction(0);
383455
}else if( strncmp(zSubcmd, "cat", n)==0 ){
384456
const char *zOutFile;
385457
int piid;
386458
Blob content;
387459
if( g.argc!=4 && g.argc!=5 ) usage("cat UUID [FILENAME]");
388460
zOutFile = g.argc==5 ? g.argv[4] : "-";
389461
piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
390462
g.argv[3]);
391463
if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
392
- purge_extract_item(piid, &content, 0, 0);
464
+ purge_extract_item(piid, &content);
393465
blob_write_to_file(&content, zOutFile);
394466
blob_reset(&content);
395467
}else{
396468
int explainOnly = find_option("explain",0,0)!=0;
397469
int dryRun = find_option("dry-run",0,0)!=0;
398470
--- src/purge.c
+++ src/purge.c
@@ -271,37 +271,34 @@
271 ** Extract the content for purgeitem number piid into a Blob. Return
272 ** the number of errors.
273 */
274 static int purge_extract_item(
275 int piid, /* ID of the item to extract */
276 Blob *pOut, /* Write the content into this blob */
277 Blob *pHash, /* If not NULL, write the hash into this blob */
278 int *pIsPrivate /* If not NULL, write the isPrivate flag here */
279 ){
280 Stmt q;
281 int srcid;
282 Blob h1, h2, x;
283 static Bag busy;
284
285 db_prepare(&q, "SELECT uuid, srcid, isPrivate, data FROM purgeitem"
286 " WHERE piid=%d", piid);
287 if( db_step(&q)!=SQLITE_ROW ){
288 db_finalize(&q);
289 fossil_fatal("missing purge-item %d", piid);
290 }
291 if( bag_find(&busy, piid) ) return 1;
292 if( pIsPrivate ) *pIsPrivate = db_column_int(&q, 2);
293 srcid = db_column_int(&q, 1);
294 blob_zero(pOut);
295 blob_zero(&x);
296 db_column_blob(&q, 3, &x);
297 blob_uncompress(&x, pOut);
298 blob_reset(&x);
299 if( srcid>0 ){
300 Blob baseline, out;
301 bag_insert(&busy, piid);
302 purge_extract_item(srcid, &baseline, 0, 0);
303 blob_zero(&out);
304 blob_delta_apply(&baseline, pOut, &out);
305 blob_reset(pOut);
306 *pOut = out;
307 blob_reset(&baseline);
@@ -312,19 +309,75 @@
312 sha1sum_blob(pOut, &h2);
313 if( blob_compare(&h1, &h2)!=0 ){
314 fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
315 blob_str(&h1), blob_str(&h2));
316 }
317 if( pHash ){
318 *pHash = h1;
319 }else{
320 blob_reset(&h1);
321 }
322 blob_reset(&h2);
323 db_finalize(&q);
324 return 0;
325 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
327 /*
328 ** COMMAND: purge
329 **
330 ** The purge command is used to remove content from a repository into a
@@ -377,21 +430,40 @@
377 purge_list_event_content(db_column_int(&q,0));
378 }
379 }
380 db_finalize(&q);
381 }else if( strncmp(zSubcmd, "undo", n)==0 ){
382 fossil_print("Not yet implemented...\n");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383 }else if( strncmp(zSubcmd, "cat", n)==0 ){
384 const char *zOutFile;
385 int piid;
386 Blob content;
387 if( g.argc!=4 && g.argc!=5 ) usage("cat UUID [FILENAME]");
388 zOutFile = g.argc==5 ? g.argv[4] : "-";
389 piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
390 g.argv[3]);
391 if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
392 purge_extract_item(piid, &content, 0, 0);
393 blob_write_to_file(&content, zOutFile);
394 blob_reset(&content);
395 }else{
396 int explainOnly = find_option("explain",0,0)!=0;
397 int dryRun = find_option("dry-run",0,0)!=0;
398
--- src/purge.c
+++ src/purge.c
@@ -271,37 +271,34 @@
271 ** Extract the content for purgeitem number piid into a Blob. Return
272 ** the number of errors.
273 */
274 static int purge_extract_item(
275 int piid, /* ID of the item to extract */
276 Blob *pOut /* Write the content into this blob */
 
 
277 ){
278 Stmt q;
279 int srcid;
280 Blob h1, h2, x;
281 static Bag busy;
282
283 db_prepare(&q, "SELECT uuid, srcid, data FROM purgeitem"
284 " WHERE piid=%d", piid);
285 if( db_step(&q)!=SQLITE_ROW ){
286 db_finalize(&q);
287 fossil_fatal("missing purge-item %d", piid);
288 }
289 if( bag_find(&busy, piid) ) return 1;
 
290 srcid = db_column_int(&q, 1);
291 blob_zero(pOut);
292 blob_zero(&x);
293 db_column_blob(&q, 2, &x);
294 blob_uncompress(&x, pOut);
295 blob_reset(&x);
296 if( srcid>0 ){
297 Blob baseline, out;
298 bag_insert(&busy, piid);
299 purge_extract_item(srcid, &baseline);
300 blob_zero(&out);
301 blob_delta_apply(&baseline, pOut, &out);
302 blob_reset(pOut);
303 *pOut = out;
304 blob_reset(&baseline);
@@ -312,19 +309,75 @@
309 sha1sum_blob(pOut, &h2);
310 if( blob_compare(&h1, &h2)!=0 ){
311 fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
312 blob_str(&h1), blob_str(&h2));
313 }
314 blob_reset(&h1);
 
 
 
 
315 blob_reset(&h2);
316 db_finalize(&q);
317 return 0;
318 }
319
320 /*
321 ** There is a TEMP table ix(piid,srcid) containing a set of purgeitems
322 ** that need to be transferred to the BLOB table. This routine does
323 ** all items that have srcid=iSrc. The pBasis blob holds the content
324 ** of the source document if iSrc>0.
325 */
326 static void purge_item_resurrect(int iSrc, Blob *pBasis){
327 Stmt q;
328 static Bag busy;
329 assert( pBasis!=0 || iSrc==0 );
330 if( iSrc>0 ){
331 if( bag_find(&busy, iSrc) ){
332 fossil_fatal("delta loop while uncompressing purged artifacts");
333 }
334 bag_insert(&busy, iSrc);
335 }
336 db_prepare(&q,
337 "SELECT uuid, data, isPrivate, ix.piid"
338 " FROM ix, purgeitem"
339 " WHERE ix.srcid=%d"
340 " AND ix.piid=purgeitem.piid;",
341 iSrc
342 );
343 while( db_step(&q)==SQLITE_ROW ){
344 Blob h1, h2, c1, c2;
345 int isPriv, rid;
346 blob_zero(&h1);
347 db_column_blob(&q, 0, &h1);
348 blob_zero(&c1);
349 db_column_blob(&q, 1, &c1);
350 blob_uncompress(&c1, &c1);
351 blob_zero(&c2);
352 if( pBasis ){
353 blob_delta_apply(pBasis, &c1, &c2);
354 blob_reset(&c1);
355 }else{
356 c2 = c1;
357 }
358 sha1sum_blob(&c2, &h2);
359 if( blob_compare(&h1, &h2)!=0 ){
360 fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
361 blob_str(&h1), blob_str(&h2));
362 }
363 blob_reset(&h2);
364 isPriv = db_column_int(&q, 2);
365 rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv);
366 if( rid==0 ){
367 fossil_fatal("%s", g.zErrMsg);
368 }else{
369 if( !isPriv ) content_make_public(rid);
370 content_get(rid, &c1);
371 manifest_crosslink(rid, &c1, MC_NO_ERRORS);
372 }
373 purge_item_resurrect(db_column_int(&q,3), &c2);
374 blob_reset(&c2);
375 }
376 db_finalize(&q);
377 if( iSrc>0 ) bag_remove(&busy, iSrc);
378 }
379
380 /*
381 ** COMMAND: purge
382 **
383 ** The purge command is used to remove content from a repository into a
@@ -377,21 +430,40 @@
430 purge_list_event_content(db_column_int(&q,0));
431 }
432 }
433 db_finalize(&q);
434 }else if( strncmp(zSubcmd, "undo", n)==0 ){
435 int peid;
436 if( g.argc!=4 ) usage("undo ID");
437 peid = atoi(g.argv[3]);
438 db_begin_transaction();
439 db_multi_exec(
440 "CREATE TEMP TABLE ix("
441 " piid INTEGER PRIMARY KEY,"
442 " srcid INTEGER"
443 ");"
444 "CREATE INDEX ixsrcid ON ix(srcid);"
445 "INSERT INTO ix(piid,srcid) "
446 " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
447 peid
448 );
449 manifest_crosslink_begin();
450 purge_item_resurrect(0, 0);
451 manifest_crosslink_end(0);
452 db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
453 db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
454 db_end_transaction(0);
455 }else if( strncmp(zSubcmd, "cat", n)==0 ){
456 const char *zOutFile;
457 int piid;
458 Blob content;
459 if( g.argc!=4 && g.argc!=5 ) usage("cat UUID [FILENAME]");
460 zOutFile = g.argc==5 ? g.argv[4] : "-";
461 piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
462 g.argv[3]);
463 if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
464 purge_extract_item(piid, &content);
465 blob_write_to_file(&content, zOutFile);
466 blob_reset(&content);
467 }else{
468 int explainOnly = find_option("explain",0,0)!=0;
469 int dryRun = find_option("dry-run",0,0)!=0;
470

Keyboard Shortcuts

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