Fossil SCM
Add the "fossil purge cat" command for extracting individual artifacts from the graveyard.
Commit
4b902843fc797b49cdbb85a30aab62adbf1f1c08
Parent
916cd4b885eb137…
1 file changed
+76
-2
+76
-2
| --- src/purge.c | ||
| +++ src/purge.c | ||
| @@ -243,11 +243,11 @@ | ||
| 243 | 243 | } |
| 244 | 244 | |
| 245 | 245 | /* |
| 246 | 246 | ** Display the content of a single purge event. |
| 247 | 247 | */ |
| 248 | -static void purge_event_content(int peid){ | |
| 248 | +static void purge_list_event_content(int peid){ | |
| 249 | 249 | Stmt q; |
| 250 | 250 | sqlite3_int64 sz1 = 0; |
| 251 | 251 | sqlite3_int64 sz2 = 0; |
| 252 | 252 | db_prepare(&q, "SELECT piid, substr(uuid,1,16), srcid, isPrivate," |
| 253 | 253 | " sz, length(data)" |
| @@ -264,10 +264,67 @@ | ||
| 264 | 264 | sz2 += db_column_int(&q,5); |
| 265 | 265 | } |
| 266 | 266 | db_finalize(&q); |
| 267 | 267 | fossil_print("%.11c%16s%.8c%10lld %10lld\n", ' ', "Total:", ' ', sz1, sz2); |
| 268 | 268 | } |
| 269 | + | |
| 270 | +/* | |
| 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); | |
| 308 | + } | |
| 309 | + bag_remove(&busy, piid); | |
| 310 | + blob_zero(&h1); | |
| 311 | + db_column_blob(&q, 0, &h1); | |
| 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 | +} | |
| 269 | 326 | |
| 270 | 327 | /* |
| 271 | 328 | ** COMMAND: purge |
| 272 | 329 | ** |
| 273 | 330 | ** The purge command is used to remove content from a repository into a |
| @@ -281,10 +338,14 @@ | ||
| 281 | 338 | ** |
| 282 | 339 | ** fossil purge undo ID |
| 283 | 340 | ** |
| 284 | 341 | ** Restore the content previously removed by purge ID. |
| 285 | 342 | ** |
| 343 | +** fossil purge cat UUID ?FILENAME? | |
| 344 | +** | |
| 345 | +** Whow the content of artifact UUID from the graveyard | |
| 346 | +** | |
| 286 | 347 | ** fossil purge [checkin] TAGS... [--explain] |
| 287 | 348 | ** |
| 288 | 349 | ** Move the checkins identified by TAGS and all of their descendants |
| 289 | 350 | ** out of the repository and into the graveyard. If a TAG is a branch |
| 290 | 351 | ** name then it means all the checkins on that branch. If the --explain |
| @@ -293,10 +354,11 @@ | ||
| 293 | 354 | ** |
| 294 | 355 | ** SUMMARY: |
| 295 | 356 | ** fossil purge [checkin] TAGS... [--explain] |
| 296 | 357 | ** fossil purge list |
| 297 | 358 | ** fossil purge undo ID |
| 359 | +** fossil purge cat UUID [FILENAME] | |
| 298 | 360 | */ |
| 299 | 361 | void purge_cmd(void){ |
| 300 | 362 | const char *zSubcmd; |
| 301 | 363 | int n; |
| 302 | 364 | Stmt q; |
| @@ -310,16 +372,28 @@ | ||
| 310 | 372 | db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch','localtime')" |
| 311 | 373 | " FROM purgeevent"); |
| 312 | 374 | while( db_step(&q)==SQLITE_ROW ){ |
| 313 | 375 | fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1)); |
| 314 | 376 | if( showDetail ){ |
| 315 | - purge_event_content(db_column_int(&q,0)); | |
| 377 | + purge_list_event_content(db_column_int(&q,0)); | |
| 316 | 378 | } |
| 317 | 379 | } |
| 318 | 380 | db_finalize(&q); |
| 319 | 381 | }else if( strncmp(zSubcmd, "undo", n)==0 ){ |
| 320 | 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); | |
| 321 | 395 | }else{ |
| 322 | 396 | int explainOnly = find_option("explain",0,0)!=0; |
| 323 | 397 | int dryRun = find_option("dry-run",0,0)!=0; |
| 324 | 398 | const char *zTag; |
| 325 | 399 | int i; |
| 326 | 400 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -243,11 +243,11 @@ | |
| 243 | } |
| 244 | |
| 245 | /* |
| 246 | ** Display the content of a single purge event. |
| 247 | */ |
| 248 | static void purge_event_content(int peid){ |
| 249 | Stmt q; |
| 250 | sqlite3_int64 sz1 = 0; |
| 251 | sqlite3_int64 sz2 = 0; |
| 252 | db_prepare(&q, "SELECT piid, substr(uuid,1,16), srcid, isPrivate," |
| 253 | " sz, length(data)" |
| @@ -264,10 +264,67 @@ | |
| 264 | sz2 += db_column_int(&q,5); |
| 265 | } |
| 266 | db_finalize(&q); |
| 267 | fossil_print("%.11c%16s%.8c%10lld %10lld\n", ' ', "Total:", ' ', sz1, sz2); |
| 268 | } |
| 269 | |
| 270 | /* |
| 271 | ** COMMAND: purge |
| 272 | ** |
| 273 | ** The purge command is used to remove content from a repository into a |
| @@ -281,10 +338,14 @@ | |
| 281 | ** |
| 282 | ** fossil purge undo ID |
| 283 | ** |
| 284 | ** Restore the content previously removed by purge ID. |
| 285 | ** |
| 286 | ** fossil purge [checkin] TAGS... [--explain] |
| 287 | ** |
| 288 | ** Move the checkins identified by TAGS and all of their descendants |
| 289 | ** out of the repository and into the graveyard. If a TAG is a branch |
| 290 | ** name then it means all the checkins on that branch. If the --explain |
| @@ -293,10 +354,11 @@ | |
| 293 | ** |
| 294 | ** SUMMARY: |
| 295 | ** fossil purge [checkin] TAGS... [--explain] |
| 296 | ** fossil purge list |
| 297 | ** fossil purge undo ID |
| 298 | */ |
| 299 | void purge_cmd(void){ |
| 300 | const char *zSubcmd; |
| 301 | int n; |
| 302 | Stmt q; |
| @@ -310,16 +372,28 @@ | |
| 310 | db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch','localtime')" |
| 311 | " FROM purgeevent"); |
| 312 | while( db_step(&q)==SQLITE_ROW ){ |
| 313 | fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1)); |
| 314 | if( showDetail ){ |
| 315 | purge_event_content(db_column_int(&q,0)); |
| 316 | } |
| 317 | } |
| 318 | db_finalize(&q); |
| 319 | }else if( strncmp(zSubcmd, "undo", n)==0 ){ |
| 320 | fossil_print("Not yet implemented...\n"); |
| 321 | }else{ |
| 322 | int explainOnly = find_option("explain",0,0)!=0; |
| 323 | int dryRun = find_option("dry-run",0,0)!=0; |
| 324 | const char *zTag; |
| 325 | int i; |
| 326 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -243,11 +243,11 @@ | |
| 243 | } |
| 244 | |
| 245 | /* |
| 246 | ** Display the content of a single purge event. |
| 247 | */ |
| 248 | static void purge_list_event_content(int peid){ |
| 249 | Stmt q; |
| 250 | sqlite3_int64 sz1 = 0; |
| 251 | sqlite3_int64 sz2 = 0; |
| 252 | db_prepare(&q, "SELECT piid, substr(uuid,1,16), srcid, isPrivate," |
| 253 | " sz, length(data)" |
| @@ -264,10 +264,67 @@ | |
| 264 | sz2 += db_column_int(&q,5); |
| 265 | } |
| 266 | db_finalize(&q); |
| 267 | fossil_print("%.11c%16s%.8c%10lld %10lld\n", ' ', "Total:", ' ', sz1, sz2); |
| 268 | } |
| 269 | |
| 270 | /* |
| 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); |
| 308 | } |
| 309 | bag_remove(&busy, piid); |
| 310 | blob_zero(&h1); |
| 311 | db_column_blob(&q, 0, &h1); |
| 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 |
| @@ -281,10 +338,14 @@ | |
| 338 | ** |
| 339 | ** fossil purge undo ID |
| 340 | ** |
| 341 | ** Restore the content previously removed by purge ID. |
| 342 | ** |
| 343 | ** fossil purge cat UUID ?FILENAME? |
| 344 | ** |
| 345 | ** Whow the content of artifact UUID from the graveyard |
| 346 | ** |
| 347 | ** fossil purge [checkin] TAGS... [--explain] |
| 348 | ** |
| 349 | ** Move the checkins identified by TAGS and all of their descendants |
| 350 | ** out of the repository and into the graveyard. If a TAG is a branch |
| 351 | ** name then it means all the checkins on that branch. If the --explain |
| @@ -293,10 +354,11 @@ | |
| 354 | ** |
| 355 | ** SUMMARY: |
| 356 | ** fossil purge [checkin] TAGS... [--explain] |
| 357 | ** fossil purge list |
| 358 | ** fossil purge undo ID |
| 359 | ** fossil purge cat UUID [FILENAME] |
| 360 | */ |
| 361 | void purge_cmd(void){ |
| 362 | const char *zSubcmd; |
| 363 | int n; |
| 364 | Stmt q; |
| @@ -310,16 +372,28 @@ | |
| 372 | db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch','localtime')" |
| 373 | " FROM purgeevent"); |
| 374 | while( db_step(&q)==SQLITE_ROW ){ |
| 375 | fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1)); |
| 376 | if( showDetail ){ |
| 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 | const char *zTag; |
| 399 | int i; |
| 400 |