Fossil

Check-in [eb36d28a]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Implement the "fossil purge undo" command.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | DBP-workflow
Files: files | file ages | folders
SHA1: eb36d28acdec4c485a3f8e4022f2ac1dbdce839d
User & Date: drh 2014-11-25 16:07:06
Context
2014-11-25
20:49
Work on the logic for parsing command-line options to the "bundle export". The same routine might well be useful for other routines, like "timeline". check-in: 8a57413e user: drh tags: DBP-workflow
16:07
Implement the "fossil purge undo" command. check-in: eb36d28a user: drh tags: DBP-workflow
14:00
Add the "fossil purge cat" command for extracting individual artifacts from the graveyard. check-in: 4b902843 user: drh tags: DBP-workflow
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/purge.c.

269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
...
310
311
312
313
314
315
316






































317
318


319







320








321

322

323
324

325
326
327
328
329
330
331
332
...
375
376
377
378
379
380
381
382



















383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

/*
** Extract the content for purgeitem number piid into a Blob.  Return
** the number of errors.
*/
static int purge_extract_item(
  int piid,            /* ID of the item to extract */
  Blob *pOut,          /* Write the content into this blob */
  Blob *pHash,         /* If not NULL, write the hash into this blob */
  int *pIsPrivate      /* If not NULL, write the isPrivate flag here */
){
  Stmt q;
  int srcid;
  Blob h1, h2, x;
  static Bag busy;

  db_prepare(&q, "SELECT uuid, srcid, isPrivate, data FROM purgeitem"
                 " WHERE piid=%d", piid);
  if( db_step(&q)!=SQLITE_ROW ){
    db_finalize(&q);
    fossil_fatal("missing purge-item %d", piid);
  }
  if( bag_find(&busy, piid) ) return 1;
  if( pIsPrivate ) *pIsPrivate = db_column_int(&q, 2);
  srcid = db_column_int(&q, 1);
  blob_zero(pOut);
  blob_zero(&x);
  db_column_blob(&q, 3, &x);
  blob_uncompress(&x, pOut);
  blob_reset(&x);
  if( srcid>0 ){
    Blob baseline, out;
    bag_insert(&busy, piid);
    purge_extract_item(srcid, &baseline, 0, 0);
    blob_zero(&out);
    blob_delta_apply(&baseline, pOut, &out);
    blob_reset(pOut);
    *pOut = out;
    blob_reset(&baseline);
  }
  bag_remove(&busy, piid);
................................................................................
  blob_zero(&h1);
  db_column_blob(&q, 0, &h1);
  sha1sum_blob(pOut, &h2);
  if( blob_compare(&h1, &h2)!=0 ){
    fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
                 blob_str(&h1), blob_str(&h2));
  }






































  if( pHash ){
    *pHash = h1;


  }else{







    blob_reset(&h1);








  }

  blob_reset(&h2);

  db_finalize(&q);
  return 0;

}

/*
** COMMAND: purge
**
** The purge command is used to remove content from a repository into a
** "graveyard" and also to show manage the graveyard and optionally restored
** content into the repository from the graveyard.
................................................................................
      fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1));
      if( showDetail ){
        purge_list_event_content(db_column_int(&q,0));
      }
    }
    db_finalize(&q);
  }else if( strncmp(zSubcmd, "undo", n)==0 ){
    fossil_print("Not yet implemented...\n");



















  }else if( strncmp(zSubcmd, "cat", n)==0 ){
    const char *zOutFile;
    int piid;
    Blob content;
    if( g.argc!=4 && g.argc!=5 ) usage("cat UUID [FILENAME]");
    zOutFile = g.argc==5 ? g.argv[4] : "-";
    piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
                     g.argv[3]);
    if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
    purge_extract_item(piid, &content, 0, 0);
    blob_write_to_file(&content, zOutFile);
    blob_reset(&content);
  }else{
    int explainOnly = find_option("explain",0,0)!=0;
    int dryRun = find_option("dry-run",0,0)!=0;
    const char *zTag;
    int i;







|
<
<






|






<



|





|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
>
>
|
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
|
>
|
>

<
>








 







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









|







269
270
271
272
273
274
275
276


277
278
279
280
281
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
...
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

377
378
379
380
381
382
383
384
385
...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471

/*
** Extract the content for purgeitem number piid into a Blob.  Return
** the number of errors.
*/
static int purge_extract_item(
  int piid,            /* ID of the item to extract */
  Blob *pOut           /* Write the content into this blob */


){
  Stmt q;
  int srcid;
  Blob h1, h2, x;
  static Bag busy;

  db_prepare(&q, "SELECT uuid, srcid, data FROM purgeitem"
                 " WHERE piid=%d", piid);
  if( db_step(&q)!=SQLITE_ROW ){
    db_finalize(&q);
    fossil_fatal("missing purge-item %d", piid);
  }
  if( bag_find(&busy, piid) ) return 1;

  srcid = db_column_int(&q, 1);
  blob_zero(pOut);
  blob_zero(&x);
  db_column_blob(&q, 2, &x);
  blob_uncompress(&x, pOut);
  blob_reset(&x);
  if( srcid>0 ){
    Blob baseline, out;
    bag_insert(&busy, piid);
    purge_extract_item(srcid, &baseline);
    blob_zero(&out);
    blob_delta_apply(&baseline, pOut, &out);
    blob_reset(pOut);
    *pOut = out;
    blob_reset(&baseline);
  }
  bag_remove(&busy, piid);
................................................................................
  blob_zero(&h1);
  db_column_blob(&q, 0, &h1);
  sha1sum_blob(pOut, &h2);
  if( blob_compare(&h1, &h2)!=0 ){
    fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
                 blob_str(&h1), blob_str(&h2));
  }
  blob_reset(&h1);
  blob_reset(&h2);
  db_finalize(&q);
  return 0;
}

/*
** There is a TEMP table ix(piid,srcid) containing a set of purgeitems
** that need to be transferred to the BLOB table.  This routine does
** all items that have srcid=iSrc.  The pBasis blob holds the content
** of the source document if iSrc>0.
*/
static void purge_item_resurrect(int iSrc, Blob *pBasis){
  Stmt q;
  static Bag busy;
  assert( pBasis!=0 || iSrc==0 );
  if( iSrc>0 ){
    if( bag_find(&busy, iSrc) ){
      fossil_fatal("delta loop while uncompressing purged artifacts");
    }
    bag_insert(&busy, iSrc);
  }
  db_prepare(&q, 
     "SELECT uuid, data, isPrivate, ix.piid"
     "  FROM ix, purgeitem"
     " WHERE ix.srcid=%d"
     "   AND ix.piid=purgeitem.piid;",
     iSrc
  );
  while( db_step(&q)==SQLITE_ROW ){
    Blob h1, h2, c1, c2;
    int isPriv, rid;
    blob_zero(&h1);
    db_column_blob(&q, 0, &h1);
    blob_zero(&c1);
    db_column_blob(&q, 1, &c1);
    blob_uncompress(&c1, &c1);
    blob_zero(&c2);
    if( pBasis ){

      blob_delta_apply(pBasis, &c1, &c2);
      blob_reset(&c1);
    }else{
      c2 = c1;
    }
    sha1sum_blob(&c2, &h2);
    if( blob_compare(&h1, &h2)!=0 ){
      fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
                   blob_str(&h1), blob_str(&h2));
    }
    blob_reset(&h2);
    isPriv = db_column_int(&q, 2);
    rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv);
    if( rid==0 ){
      fossil_fatal("%s", g.zErrMsg);
    }else{
      if( !isPriv ) content_make_public(rid);
      content_get(rid, &c1);
      manifest_crosslink(rid, &c1, MC_NO_ERRORS);
    }
    purge_item_resurrect(db_column_int(&q,3), &c2);
    blob_reset(&c2);
  }
  db_finalize(&q);

  if( iSrc>0 ) bag_remove(&busy, iSrc);
}

/*
** COMMAND: purge
**
** The purge command is used to remove content from a repository into a
** "graveyard" and also to show manage the graveyard and optionally restored
** content into the repository from the graveyard.
................................................................................
      fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1));
      if( showDetail ){
        purge_list_event_content(db_column_int(&q,0));
      }
    }
    db_finalize(&q);
  }else if( strncmp(zSubcmd, "undo", n)==0 ){
    int peid;
    if( g.argc!=4 ) usage("undo ID");
    peid = atoi(g.argv[3]);
    db_begin_transaction();
    db_multi_exec(
      "CREATE TEMP TABLE ix("
      "  piid INTEGER PRIMARY KEY,"
      "  srcid INTEGER"
      ");"
      "CREATE INDEX ixsrcid ON ix(srcid);"
      "INSERT INTO ix(piid,srcid) "
      "  SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
      peid
    );
    manifest_crosslink_begin();
    purge_item_resurrect(0, 0);
    manifest_crosslink_end(0);
    db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
    db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
    db_end_transaction(0);
  }else if( strncmp(zSubcmd, "cat", n)==0 ){
    const char *zOutFile;
    int piid;
    Blob content;
    if( g.argc!=4 && g.argc!=5 ) usage("cat UUID [FILENAME]");
    zOutFile = g.argc==5 ? g.argv[4] : "-";
    piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
                     g.argv[3]);
    if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
    purge_extract_item(piid, &content);
    blob_write_to_file(&content, zOutFile);
    blob_reset(&content);
  }else{
    int explainOnly = find_option("explain",0,0)!=0;
    int dryRun = find_option("dry-run",0,0)!=0;
    const char *zTag;
    int i;