Path: blob/master/src/applications/differential/management/PhabricatorDifferentialMigrateHunkWorkflow.php
12256 views
<?php12final class PhabricatorDifferentialMigrateHunkWorkflow3extends PhabricatorDifferentialManagementWorkflow {45protected function didConstruct() {6$this7->setName('migrate-hunk')8->setExamples(9"**migrate-hunk** --id __hunk__ --to __storage__\n".10"**migrate-hunk** --all")11->setSynopsis(pht('Migrate storage engines for a hunk.'))12->setArguments(13array(14array(15'name' => 'id',16'param' => 'id',17'help' => pht('Hunk ID to migrate.'),18),19array(20'name' => 'to',21'param' => 'storage',22'help' => pht('Storage engine to migrate to.'),23),24array(25'name' => 'all',26'help' => pht('Migrate all hunks.'),27),28array(29'name' => 'auto',30'help' => pht('Select storage format automatically.'),31),32array(33'name' => 'dry-run',34'help' => pht('Show planned writes but do not perform them.'),35),36));37}3839public function execute(PhutilArgumentParser $args) {40$is_dry_run = $args->getArg('dry-run');4142$id = $args->getArg('id');43$is_all = $args->getArg('all');4445if ($is_all && $id) {46throw new PhutilArgumentUsageException(47pht(48'Options "--all" (to migrate all hunks) and "--id" (to migrate a '.49'specific hunk) are mutually exclusive.'));50} else if (!$is_all && !$id) {51throw new PhutilArgumentUsageException(52pht(53'Specify a hunk to migrate with "--id", or migrate all hunks '.54'with "--all".'));55}5657$is_auto = $args->getArg('auto');58$storage = $args->getArg('to');59if ($is_auto && $storage) {60throw new PhutilArgumentUsageException(61pht(62'Options "--to" (to choose a specific storage format) and "--auto" '.63'(to select a storage format automatically) are mutually '.64'exclusive.'));65} else if (!$is_auto && !$storage) {66throw new PhutilArgumentUsageException(67pht(68'Use "--to" to choose a storage format, or "--auto" to select a '.69'format automatically.'));70}7172$types = array(73DifferentialHunk::DATATYPE_TEXT,74DifferentialHunk::DATATYPE_FILE,75);76$types = array_fuse($types);77if (strlen($storage)) {78if (!isset($types[$storage])) {79throw new PhutilArgumentUsageException(80pht(81'Storage type "%s" is unknown. Supported types are: %s.',82$storage,83implode(', ', array_keys($types))));84}85}8687if ($id) {88$hunk = $this->loadHunk($id);89$hunks = array($hunk);90} else {91$hunks = new LiskMigrationIterator(new DifferentialHunk());92}9394foreach ($hunks as $hunk) {95try {96$this->migrateHunk($hunk, $storage, $is_auto, $is_dry_run);97} catch (Exception $ex) {98// If we're migrating a single hunk, just throw the exception. If99// we're migrating multiple hunks, warn but continue.100if ($id) {101throw $ex;102}103104$this->logWarn(105pht('WARN'),106pht(107'Failed to migrate hunk %d: %s',108$hunk->getID(),109$ex->getMessage()));110}111}112113return 0;114}115116private function loadHunk($id) {117$hunk = id(new DifferentialHunk())->load($id);118if (!$hunk) {119throw new PhutilArgumentUsageException(120pht(121'No hunk exists with ID "%s".',122$id));123}124125return $hunk;126}127128private function migrateHunk(129DifferentialHunk $hunk,130$type,131$is_auto,132$is_dry_run) {133134$old_type = $hunk->getDataType();135136if ($is_auto) {137// By default, we're just going to keep hunks in the same storage138// engine. In the future, we could perhaps select large hunks stored in139// text engine and move them into file storage.140$new_type = $old_type;141} else {142$new_type = $type;143}144145// Figure out if the storage format (e.g., plain text vs compressed)146// would change if we wrote this hunk anew today.147$old_format = $hunk->getDataFormat();148$new_format = $hunk->getAutomaticDataFormat();149150$same_type = ($old_type === $new_type);151$same_format = ($old_format === $new_format);152153// If we aren't going to change the storage engine and aren't going to154// change the storage format, just bail out.155if ($same_type && $same_format) {156$this->logInfo(157pht('SKIP'),158pht(159'Hunk %d is already stored in the preferred engine ("%s") '.160'with the preferred format ("%s").',161$hunk->getID(),162$new_type,163$new_format));164return;165}166167if ($is_dry_run) {168$this->logOkay(169pht('DRY RUN'),170pht(171'Hunk %d would be rewritten (storage: "%s" -> "%s"; '.172'format: "%s" -> "%s").',173$hunk->getID(),174$old_type,175$new_type,176$old_format,177$new_format));178return;179}180181$old_data = $hunk->getChanges();182183switch ($new_type) {184case DifferentialHunk::DATATYPE_TEXT:185$hunk->saveAsText();186break;187case DifferentialHunk::DATATYPE_FILE:188$hunk->saveAsFile();189break;190}191192$this->logOkay(193pht('MIGRATE'),194pht(195'Converted hunk %d to "%s" storage (with format "%s").',196$hunk->getID(),197$new_type,198$hunk->getDataFormat()));199200$hunk = $this->loadHunk($hunk->getID());201$new_data = $hunk->getChanges();202203if ($old_data !== $new_data) {204throw new Exception(205pht(206'Integrity check failed: new file data differs from old data!'));207}208}209210211}212213214