@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Allow structured destruction of Differential Revisions

Summary:
Ref T4749. Ref T3265. Ref T4909.

- Remove old "destroy revision" script.
- Move to structured `bin/remove` destruction.
- Fix some edge issues.
- Add transaction destruction support.

Test Plan:
- Destroyed a bunch of revisions.
- Saw diffs, changesets, hunks, transactions, edges, and inlines also get wiped out.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4749, T4909, T3265

Differential Revision: https://secure.phabricator.com/D8943

+161 -118
-51
scripts/differential/destroy_revision.php
··· 1 - #!/usr/bin/env php 2 - <?php 3 - 4 - $root = dirname(dirname(dirname(__FILE__))); 5 - require_once $root.'/scripts/__init_script__.php'; 6 - 7 - $args = new PhutilArgumentParser($argv); 8 - $args->setTagline('permanently destroy a Differential Revision'); 9 - $args->setSynopsis(<<<EOHELP 10 - **destroy_revision.php** __D123__ 11 - Permanently destroy the specified Differential Revision (for example, 12 - because it contains secrets that the world is not ready to know). 13 - 14 - Normally, you can just "Abandon" unwanted revisions, but in dire 15 - circumstances this script can be used to completely destroy a 16 - revision. Destroying a revision may cause some glitches in 17 - linked objects. 18 - 19 - The revision is utterly destroyed and can not be recovered unless you 20 - have backups. 21 - EOHELP 22 - ); 23 - $args->parseStandardArguments(); 24 - $args->parse( 25 - array( 26 - array( 27 - 'name' => 'revision', 28 - 'wildcard' => true, 29 - ), 30 - )); 31 - 32 - $revisions = $args->getArg('revision'); 33 - if (count($revisions) != 1) { 34 - $args->printHelpAndExit(); 35 - } 36 - 37 - $id = trim(strtolower(head($revisions)), 'd '); 38 - $revision = id(new DifferentialRevision())->load($id); 39 - 40 - if (!$revision) { 41 - throw new Exception("No revision '{$id}' exists!"); 42 - } 43 - 44 - $title = $revision->getTitle(); 45 - $ok = phutil_console_confirm("Really destroy 'D{$id}: {$title}' forever?"); 46 - if (!$ok) { 47 - throw new Exception("User aborted workflow."); 48 - } 49 - 50 - $revision->delete(); 51 - echo "OK, destroyed revision.\n";
+4
src/__phutil_library_map__.php
··· 2982 2982 1 => 'PhabricatorPolicyInterface', 2983 2983 2 => 'HarbormasterBuildableInterface', 2984 2984 3 => 'PhabricatorApplicationTransactionInterface', 2985 + 4 => 'PhabricatorDestructableInterface', 2985 2986 ), 2986 2987 'DifferentialDiffCreateController' => 'DifferentialController', 2987 2988 'DifferentialDiffProperty' => 'DifferentialDAO', ··· 3047 3048 6 => 'PhabricatorSubscribableInterface', 3048 3049 7 => 'PhabricatorCustomFieldInterface', 3049 3050 8 => 'PhabricatorApplicationTransactionInterface', 3051 + 9 => 'PhabricatorDestructableInterface', 3050 3052 ), 3051 3053 'DifferentialRevisionDetailView' => 'AphrontView', 3052 3054 'DifferentialRevisionEditController' => 'DifferentialController', ··· 3915 3917 array( 3916 3918 0 => 'PhabricatorLiskDAO', 3917 3919 1 => 'PhabricatorPolicyInterface', 3920 + 2 => 'PhabricatorDestructableInterface', 3918 3921 ), 3919 3922 'PhabricatorApplicationTransactionComment' => 3920 3923 array( 3921 3924 0 => 'PhabricatorLiskDAO', 3922 3925 1 => 'PhabricatorMarkupInterface', 3923 3926 2 => 'PhabricatorPolicyInterface', 3927 + 3 => 'PhabricatorDestructableInterface', 3924 3928 ), 3925 3929 'PhabricatorApplicationTransactionCommentEditController' => 'PhabricatorApplicationTransactionController', 3926 3930 'PhabricatorApplicationTransactionCommentEditor' => 'PhabricatorEditor',
+26 -19
src/applications/differential/storage/DifferentialDiff.php
··· 5 5 implements 6 6 PhabricatorPolicyInterface, 7 7 HarbormasterBuildableInterface, 8 - PhabricatorApplicationTransactionInterface { 8 + PhabricatorApplicationTransactionInterface, 9 + PhabricatorDestructableInterface { 9 10 10 11 protected $revisionID; 11 12 protected $authorPHID; ··· 103 104 $changeset->setDiffID($this->getID()); 104 105 $changeset->save(); 105 106 } 106 - $this->saveTransaction(); 107 - return $ret; 108 - } 109 - 110 - public function delete() { 111 - $this->openTransaction(); 112 - foreach ($this->loadChangesets() as $changeset) { 113 - $changeset->delete(); 114 - } 115 - 116 - $properties = id(new DifferentialDiffProperty())->loadAllWhere( 117 - 'diffID = %d', 118 - $this->getID()); 119 - foreach ($properties as $prop) { 120 - $prop->delete(); 121 - } 122 - 123 - $ret = parent::delete(); 124 107 $this->saveTransaction(); 125 108 return $ret; 126 109 } ··· 374 357 return null; 375 358 } 376 359 return $this->getRevision()->getApplicationTransactionTemplate(); 360 + } 361 + 362 + 363 + /* -( PhabricatorDestructableInterface )----------------------------------- */ 364 + 365 + 366 + public function destroyObjectPermanently( 367 + PhabricatorDestructionEngine $engine) { 368 + 369 + $this->openTransaction(); 370 + $this->delete(); 371 + 372 + foreach ($this->loadChangesets() as $changeset) { 373 + $changeset->delete(); 374 + } 375 + 376 + $properties = id(new DifferentialDiffProperty())->loadAllWhere( 377 + 'diffID = %d', 378 + $this->getID()); 379 + foreach ($properties as $prop) { 380 + $prop->delete(); 381 + } 382 + 383 + $this->saveTransaction(); 377 384 } 378 385 379 386 }
+51 -40
src/applications/differential/storage/DifferentialRevision.php
··· 9 9 HarbormasterBuildableInterface, 10 10 PhabricatorSubscribableInterface, 11 11 PhabricatorCustomFieldInterface, 12 - PhabricatorApplicationTransactionInterface { 12 + PhabricatorApplicationTransactionInterface, 13 + PhabricatorDestructableInterface { 13 14 14 15 protected $title = ''; 15 16 protected $originalTitle; ··· 172 173 $this->mailKey = Filesystem::readRandomCharacters(40); 173 174 } 174 175 return parent::save(); 175 - } 176 - 177 - public function delete() { 178 - $this->openTransaction(); 179 - $diffs = id(new DifferentialDiffQuery()) 180 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 181 - ->withRevisionIDs(array($this->getID())) 182 - ->execute(); 183 - foreach ($diffs as $diff) { 184 - $diff->delete(); 185 - } 186 - 187 - $conn_w = $this->establishConnection('w'); 188 - 189 - queryfx( 190 - $conn_w, 191 - 'DELETE FROM %T WHERE revisionID = %d', 192 - self::TABLE_COMMIT, 193 - $this->getID()); 194 - 195 - $inlines = id(new DifferentialInlineCommentQuery()) 196 - ->withRevisionIDs(array($this->getID())) 197 - ->execute(); 198 - foreach ($inlines as $inline) { 199 - $inline->delete(); 200 - } 201 - 202 - // we have to do paths a little differentally as they do not have 203 - // an id or phid column for delete() to act on 204 - $dummy_path = new DifferentialAffectedPath(); 205 - queryfx( 206 - $conn_w, 207 - 'DELETE FROM %T WHERE revisionID = %d', 208 - $dummy_path->getTableName(), 209 - $this->getID()); 210 - 211 - $result = parent::delete(); 212 - $this->saveTransaction(); 213 - return $result; 214 176 } 215 177 216 178 public function loadRelationships() { ··· 475 437 476 438 public function getApplicationTransactionTemplate() { 477 439 return new DifferentialTransaction(); 440 + } 441 + 442 + 443 + /* -( PhabricatorDestructableInterface )----------------------------------- */ 444 + 445 + 446 + public function destroyObjectPermanently( 447 + PhabricatorDestructionEngine $engine) { 448 + 449 + $this->openTransaction(); 450 + $diffs = id(new DifferentialDiffQuery()) 451 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 452 + ->withRevisionIDs(array($this->getID())) 453 + ->execute(); 454 + foreach ($diffs as $diff) { 455 + $engine->destroyObject($diff); 456 + } 457 + 458 + $conn_w = $this->establishConnection('w'); 459 + 460 + queryfx( 461 + $conn_w, 462 + 'DELETE FROM %T WHERE revisionID = %d', 463 + self::TABLE_COMMIT, 464 + $this->getID()); 465 + 466 + try { 467 + $inlines = id(new DifferentialInlineCommentQuery()) 468 + ->withRevisionIDs(array($this->getID())) 469 + ->execute(); 470 + foreach ($inlines as $inline) { 471 + $inline->delete(); 472 + } 473 + } catch (PhabricatorEmptyQueryException $ex) { 474 + // TODO: There's still some funky legacy wrapping going on here, and 475 + // we might catch a raw query exception. 476 + } 477 + 478 + // we have to do paths a little differentally as they do not have 479 + // an id or phid column for delete() to act on 480 + $dummy_path = new DifferentialAffectedPath(); 481 + queryfx( 482 + $conn_w, 483 + 'DELETE FROM %T WHERE revisionID = %d', 484 + $dummy_path->getTableName(), 485 + $this->getID()); 486 + 487 + $this->delete(); 488 + $this->saveTransaction(); 478 489 } 479 490 480 491 }
+33 -5
src/applications/system/engine/PhabricatorDestructionEngine.php
··· 41 41 42 42 if ($object_phid) { 43 43 $this->destroyEdges($object_phid); 44 + 45 + if ($object instanceof PhabricatorApplicationTransactionInterface) { 46 + $template = $object->getApplicationTransactionTemplate(); 47 + $this->destroyTransactions($template, $object_phid); 48 + } 44 49 } 50 + 51 + // TODO: PhabricatorFlaggableInterface 52 + // TODO: PhabricatorTokenReceiverInterface 45 53 } 46 54 47 55 private function destroyEdges($src_phid) { 48 - $edges = id(new PhabricatorEdgeQuery()) 49 - ->withSourcePHIDs(array($src_phid)) 50 - ->execute(); 56 + try { 57 + $edges = id(new PhabricatorEdgeQuery()) 58 + ->withSourcePHIDs(array($src_phid)) 59 + ->execute(); 60 + } catch (Exception $ex) { 61 + // This is (presumably) a "no edges for this PHID type" exception. 62 + return; 63 + } 51 64 52 65 $editor = id(new PhabricatorEdgeEditor()) 53 66 ->setSuppressEvents(true); 54 - foreach ($edges as $edge) { 55 - $editor->removeEdge($edge['src'], $edge['type'], $edge['dst']); 67 + foreach ($edges as $type => $type_edges) { 68 + foreach ($type_edges as $src => $src_edges) { 69 + foreach ($src_edges as $dst => $edge) { 70 + $editor->removeEdge($edge['src'], $edge['type'], $edge['dst']); 71 + } 72 + } 56 73 } 57 74 $editor->save(); 75 + } 76 + 77 + private function destroyTransactions( 78 + PhabricatorApplicationTransaction $template, 79 + $object_phid) { 80 + 81 + $xactions = $template->loadAllWhere('objectPHID = %s', $object_phid); 82 + foreach ($xactions as $xaction) { 83 + $this->destroyObject($xaction); 84 + } 85 + 58 86 } 59 87 60 88 }
+2 -1
src/applications/system/management/PhabricatorSystemRemoveLogWorkflow.php
··· 17 17 $table = new PhabricatorSystemDestructionLog(); 18 18 foreach (new LiskMigrationIterator($table) as $row) { 19 19 $console->writeOut( 20 - "[%s]\t%s\t%s\t%s\n", 20 + "[%s]\t%s %s\t%s\t%s\n", 21 21 phabricator_datetime($row->getEpoch(), $this->getViewer()), 22 + ($row->getRootLogID() ? ' ' : '*'), 22 23 $row->getObjectClass(), 23 24 $row->getObjectPHID(), 24 25 $row->getObjectMonogram());
+31 -1
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 2 2 3 3 abstract class PhabricatorApplicationTransaction 4 4 extends PhabricatorLiskDAO 5 - implements PhabricatorPolicyInterface { 5 + implements 6 + PhabricatorPolicyInterface, 7 + PhabricatorDestructableInterface { 6 8 7 9 const TARGET_TEXT = 'text'; 8 10 const TARGET_HTML = 'html'; ··· 939 941 public function describeAutomaticCapability($capability) { 940 942 // TODO: (T603) Exact policies are unclear here. 941 943 return null; 944 + } 945 + 946 + 947 + /* -( PhabricatorDestructableInterface )----------------------------------- */ 948 + 949 + 950 + public function destroyObjectPermanently( 951 + PhabricatorDestructionEngine $engine) { 952 + 953 + $this->openTransaction(); 954 + $comment_template = null; 955 + try { 956 + $comment_template = $this->getApplicationTransactionCommentObject(); 957 + } catch (Exception $ex) { 958 + // Continue; no comments for these transactions. 959 + } 960 + 961 + if ($comment_template) { 962 + $comments = $comment_template->loadAllWhere( 963 + 'transactionPHID = %s', 964 + $this->getPHID()); 965 + foreach ($comments as $comment) { 966 + $engine->destroyObject($comment); 967 + } 968 + } 969 + 970 + $this->delete(); 971 + $this->saveTransaction(); 942 972 } 943 973 944 974
+14 -1
src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php
··· 2 2 3 3 abstract class PhabricatorApplicationTransactionComment 4 4 extends PhabricatorLiskDAO 5 - implements PhabricatorMarkupInterface, PhabricatorPolicyInterface { 5 + implements 6 + PhabricatorMarkupInterface, 7 + PhabricatorPolicyInterface, 8 + PhabricatorDestructableInterface { 6 9 7 10 const MARKUP_FIELD_COMMENT = 'markup:comment'; 8 11 ··· 116 119 public function describeAutomaticCapability($capability) { 117 120 // TODO: (T603) Policies are murky. 118 121 return null; 122 + } 123 + 124 + 125 + /* -( PhabricatorDestructableInterface )----------------------------------- */ 126 + 127 + public function destroyObjectPermanently( 128 + PhabricatorDestructionEngine $engine) { 129 + $this->openTransaction(); 130 + $this->delete(); 131 + $this->saveTransaction(); 119 132 } 120 133 121 134 }