diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2667,6 +2667,7 @@ 'PhabricatorDifferentialConfigOptions' => 'applications/differential/config/PhabricatorDifferentialConfigOptions.php', 'PhabricatorDifferentialExtractWorkflow' => 'applications/differential/management/PhabricatorDifferentialExtractWorkflow.php', 'PhabricatorDifferentialManagementWorkflow' => 'applications/differential/management/PhabricatorDifferentialManagementWorkflow.php', + 'PhabricatorDifferentialMigrateHunkWorkflow' => 'applications/differential/management/PhabricatorDifferentialMigrateHunkWorkflow.php', 'PhabricatorDifferentialRevisionTestDataGenerator' => 'applications/differential/lipsum/PhabricatorDifferentialRevisionTestDataGenerator.php', 'PhabricatorDiffusionApplication' => 'applications/diffusion/application/PhabricatorDiffusionApplication.php', 'PhabricatorDiffusionBlameSetting' => 'applications/settings/setting/PhabricatorDiffusionBlameSetting.php', @@ -5367,6 +5368,7 @@ 'DifferentialChangeset' => array( 'DifferentialDAO', 'PhabricatorPolicyInterface', + 'PhabricatorDestructibleInterface', ), 'DifferentialChangesetDetailView' => 'AphrontView', 'DifferentialChangesetFileTreeSideNavBuilder' => 'Phobject', @@ -5461,6 +5463,7 @@ 'DifferentialHunk' => array( 'DifferentialDAO', 'PhabricatorPolicyInterface', + 'PhabricatorDestructibleInterface', ), 'DifferentialHunkParser' => 'Phobject', 'DifferentialHunkParserTestCase' => 'PhabricatorTestCase', @@ -7999,6 +8002,7 @@ 'PhabricatorDifferentialConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorDifferentialExtractWorkflow' => 'PhabricatorDifferentialManagementWorkflow', 'PhabricatorDifferentialManagementWorkflow' => 'PhabricatorManagementWorkflow', + 'PhabricatorDifferentialMigrateHunkWorkflow' => 'PhabricatorDifferentialManagementWorkflow', 'PhabricatorDifferentialRevisionTestDataGenerator' => 'PhabricatorTestDataGenerator', 'PhabricatorDiffusionApplication' => 'PhabricatorApplication', 'PhabricatorDiffusionBlameSetting' => 'PhabricatorInternalSetting', diff --git a/src/applications/differential/management/PhabricatorDifferentialMigrateHunkWorkflow.php b/src/applications/differential/management/PhabricatorDifferentialMigrateHunkWorkflow.php new file mode 100644 --- /dev/null +++ b/src/applications/differential/management/PhabricatorDifferentialMigrateHunkWorkflow.php @@ -0,0 +1,86 @@ +setName('migrate-hunk') + ->setExamples('**migrate-hunk** --id __hunk__ --to __storage__') + ->setSynopsis(pht('Migrate storage engines for a hunk.')) + ->setArguments( + array( + array( + 'name' => 'id', + 'param' => 'id', + 'help' => pht('Hunk ID to migrate.'), + ), + array( + 'name' => 'to', + 'param' => 'storage', + 'help' => pht('Storage engine to migrate to.'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $id = $args->getArg('id'); + if (!$id) { + throw new PhutilArgumentUsageException( + pht('Specify a hunk to migrate with --id.')); + } + + $storage = $args->getArg('to'); + switch ($storage) { + case DifferentialModernHunk::DATATYPE_TEXT: + case DifferentialModernHunk::DATATYPE_FILE: + break; + default: + throw new PhutilArgumentUsageException( + pht('Specify a hunk storage engine with --to.')); + } + + $hunk = $this->loadHunk($id); + $old_data = $hunk->getChanges(); + + switch ($storage) { + case DifferentialModernHunk::DATATYPE_TEXT: + $hunk->saveAsText(); + $this->logOkay( + pht('TEXT'), + pht('Convereted hunk to text storage.')); + break; + case DifferentialModernHunk::DATATYPE_FILE: + $hunk->saveAsFile(); + $this->logOkay( + pht('FILE'), + pht('Convereted hunk to file storage.')); + break; + } + + $hunk = $this->loadHunk($id); + $new_data = $hunk->getChanges(); + + if ($old_data !== $new_data) { + throw new Exception( + pht( + 'Integrity check failed: new file data differs fom old data!')); + } + + return 0; + } + + private function loadHunk($id) { + $hunk = id(new DifferentialModernHunk())->load($id); + if (!$hunk) { + throw new PhutilArgumentUsageException( + pht( + 'No hunk exists with ID "%s".', + $id)); + } + + return $hunk; + } + + +} diff --git a/src/applications/differential/storage/DifferentialChangeset.php b/src/applications/differential/storage/DifferentialChangeset.php --- a/src/applications/differential/storage/DifferentialChangeset.php +++ b/src/applications/differential/storage/DifferentialChangeset.php @@ -1,7 +1,10 @@ getDiff()->hasAutomaticCapability($capability, $viewer); } + +/* -( PhabricatorDestructibleInterface )----------------------------------- */ + + + public function destroyObjectPermanently( + PhabricatorDestructionEngine $engine) { + $this->openTransaction(); + + $hunks = id(new DifferentialModernHunk())->loadAllWhere( + 'changesetID = %d', + $this->getID()); + foreach ($hunks as $hunk) { + $engine->destroyObject($hunk); + } + + $this->delete(); + + $this->saveTransaction(); + } + + } diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -727,7 +727,7 @@ $this->delete(); foreach ($this->loadChangesets() as $changeset) { - $changeset->delete(); + $engine->destroyObject($changeset); } $properties = id(new DifferentialDiffProperty())->loadAllWhere( diff --git a/src/applications/differential/storage/DifferentialHunk.php b/src/applications/differential/storage/DifferentialHunk.php --- a/src/applications/differential/storage/DifferentialHunk.php +++ b/src/applications/differential/storage/DifferentialHunk.php @@ -1,7 +1,10 @@ getChangeset()->hasAutomaticCapability($capability, $viewer); } + +/* -( PhabricatorDestructibleInterface )----------------------------------- */ + + + public function destroyObjectPermanently( + PhabricatorDestructionEngine $engine) { + $this->delete(); + } + + } diff --git a/src/applications/differential/storage/DifferentialModernHunk.php b/src/applications/differential/storage/DifferentialModernHunk.php --- a/src/applications/differential/storage/DifferentialModernHunk.php +++ b/src/applications/differential/storage/DifferentialModernHunk.php @@ -15,6 +15,7 @@ private $rawData; private $forcedEncoding; + private $fileData; public function getTableName() { return 'differential_hunk_modern'; @@ -87,6 +88,57 @@ return parent::save(); } + public function saveAsText() { + $old_type = $this->getDataType(); + $old_data = $this->getData(); + + if ($old_type == self::DATATYPE_TEXT) { + return $this; + } + + $raw_data = $this->getRawData(); + + $this->setDataType(self::DATATYPE_TEXT); + $this->setData($raw_data); + + $result = $this->save(); + + $this->destroyData($old_type, $old_data); + + return $result; + } + + public function saveAsFile() { + $old_type = $this->getDataType(); + $old_data = $this->getData(); + + if ($old_type == self::DATATYPE_FILE) { + return $this; + } + + $raw_data = $this->getRawData(); + + $file = PhabricatorFile::newFromFileData( + $raw_data, + array( + 'name' => 'differential-hunk', + 'mime-type' => 'application/octet-stream', + 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, + )); + + $this->setDataType(self::DATATYPE_FILE); + $this->setData($file->getPHID()); + + // NOTE: Because hunks don't have a PHID and we just load hunk data with + // the ominipotent viewer, we do not need to attach the file to anything. + + $result = $this->save(); + + $this->destroyData($old_type, $old_data); + + return $result; + } + private function getRawData() { if ($this->rawData === null) { $type = $this->getDataType(); @@ -98,6 +150,8 @@ $data = $data; break; case self::DATATYPE_FILE: + $data = $this->loadFileData(); + break; default: throw new Exception( pht('Hunk has unsupported data type "%s"!', $type)); @@ -123,4 +177,75 @@ return $this->rawData; } + private function loadFileData() { + if ($this->fileData === null) { + $type = $this->getDataType(); + if ($type !== self::DATATYPE_FILE) { + throw new Exception( + pht( + 'Unable to load file data for hunk with wrong data type ("%s").', + $type)); + } + + $file_phid = $this->getData(); + + $file = $this->loadRawFile($file_phid); + $data = $file->loadFileData(); + + $this->fileData = $data; + } + + return $this->fileData; + } + + private function loadRawFile($file_phid) { + $viewer = PhabricatorUser::getOmnipotentUser(); + + + $files = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($file_phid)) + ->execute(); + if (!$files) { + throw new Exception( + pht( + 'Failed to load file ("%s") with hunk data.', + $file_phid)); + } + + $file = head($files); + + return $file; + } + + + public function destroyObjectPermanently( + PhabricatorDestructionEngine $engine) { + + $type = $this->getDataType(); + $data = $this->getData(); + + $this->destroyData($type, $data, $engine); + + return parent::destroyObjectPermanently($engine); + } + + + private function destroyData( + $type, + $data, + PhabricatorDestructionEngine $engine = null) { + + if (!$engine) { + $engine = new PhabricatorDestructionEngine(); + } + + switch ($type) { + case self::DATATYPE_FILE: + $file = $this->loadRawFile($data); + $engine->destroyObject($file); + break; + } + } + }