public function execute(PhutilArgumentParser $args)
 {
     $console = PhutilConsole::getConsole();
     $engine_id = $args->getArg('engine');
     if (!$engine_id) {
         throw new PhutilArgumentUsageException('Specify an engine to migrate to with `--engine`. ' . 'Use `files engines` to get a list of engines.');
     }
     $engine = PhabricatorFile::buildEngine($engine_id);
     $iterator = $this->buildIterator($args);
     if (!$iterator) {
         throw new PhutilArgumentUsageException('Either specify a list of files to migrate, or use `--all` ' . 'to migrate all files.');
     }
     $is_dry_run = $args->getArg('dry-run');
     $failed = array();
     foreach ($iterator as $file) {
         $fid = 'F' . $file->getID();
         if ($file->getStorageEngine() == $engine_id) {
             $console->writeOut("%s: Already stored on '%s'\n", $fid, $engine_id);
             continue;
         }
         if ($is_dry_run) {
             $console->writeOut("%s: Would migrate from '%s' to '%s' (dry run)\n", $fid, $file->getStorageEngine(), $engine_id);
             continue;
         }
         $console->writeOut("%s: Migrating from '%s' to '%s'...", $fid, $file->getStorageEngine(), $engine_id);
         try {
             $file->migrateToEngine($engine);
             $console->writeOut("done.\n");
         } catch (Exception $ex) {
             $console->writeOut("failed!\n");
             $console->writeErr("%s\n", (string) $ex);
             $failed[] = $file;
         }
     }
     if ($failed) {
         $console->writeOut("**Failures!**\n");
         $ids = array();
         foreach ($failed as $file) {
             $ids[] = 'F' . $file->getID();
         }
         $console->writeOut("%s\n", implode(', ', $ids));
         return 1;
     } else {
         $console->writeOut("**Success!**\n");
         return 0;
     }
 }
 public function execute(PhutilArgumentParser $args)
 {
     $target_key = $args->getArg('engine');
     if (!$target_key) {
         throw new PhutilArgumentUsageException(pht('Specify an engine to migrate to with `%s`. ' . 'Use `%s` to get a list of engines.', '--engine', 'files engines'));
     }
     $target_engine = PhabricatorFile::buildEngine($target_key);
     $iterator = $this->buildIterator($args);
     if (!$iterator) {
         throw new PhutilArgumentUsageException(pht('Either specify a list of files to migrate, or use `%s` ' . 'to migrate all files.', '--all'));
     }
     $is_dry_run = $args->getArg('dry-run');
     $min_size = (int) $args->getArg('min-size');
     $max_size = (int) $args->getArg('max-size');
     $is_copy = $args->getArg('copy');
     $failed = array();
     $engines = PhabricatorFileStorageEngine::loadAllEngines();
     $total_bytes = 0;
     $total_files = 0;
     foreach ($iterator as $file) {
         $monogram = $file->getMonogram();
         $engine_key = $file->getStorageEngine();
         $engine = idx($engines, $engine_key);
         if (!$engine) {
             echo tsprintf("%s\n", pht('%s: Uses unknown storage engine "%s".', $monogram, $engine_key));
             $failed[] = $file;
             continue;
         }
         if ($engine->isChunkEngine()) {
             echo tsprintf("%s\n", pht('%s: Stored as chunks, no data to migrate directly.', $monogram));
             continue;
         }
         if ($engine_key === $target_key) {
             echo tsprintf("%s\n", pht('%s: Already stored in engine "%s".', $monogram, $target_key));
             continue;
         }
         $byte_size = $file->getByteSize();
         if ($min_size && $byte_size < $min_size) {
             echo tsprintf("%s\n", pht('%s: File size (%s) is smaller than minimum size (%s).', $monogram, phutil_format_bytes($byte_size), phutil_format_bytes($min_size)));
             continue;
         }
         if ($max_size && $byte_size > $max_size) {
             echo tsprintf("%s\n", pht('%s: File size (%s) is larger than maximum size (%s).', $monogram, phutil_format_bytes($byte_size), phutil_format_bytes($max_size)));
             continue;
         }
         if ($is_dry_run) {
             echo tsprintf("%s\n", pht('%s: (%s) Would migrate from "%s" to "%s" (dry run)...', $monogram, phutil_format_bytes($byte_size), $engine_key, $target_key));
         } else {
             echo tsprintf("%s\n", pht('%s: (%s) Migrating from "%s" to "%s"...', $monogram, phutil_format_bytes($byte_size), $engine_key, $target_key));
         }
         try {
             if ($is_dry_run) {
                 // Do nothing, this is a dry run.
             } else {
                 $file->migrateToEngine($target_engine, $is_copy);
             }
             $total_files += 1;
             $total_bytes += $byte_size;
             echo tsprintf("%s\n", pht('Done.'));
         } catch (Exception $ex) {
             echo tsprintf("%s\n", pht('Failed! %s', (string) $ex));
             $failed[] = $file;
             throw $ex;
         }
     }
     echo tsprintf("%s\n", pht('Total Migrated Files: %s', new PhutilNumber($total_files)));
     echo tsprintf("%s\n", pht('Total Migrated Bytes: %s', phutil_format_bytes($total_bytes)));
     if ($is_dry_run) {
         echo tsprintf("%s\n", pht('This was a dry run, so no real migrations were performed.'));
     }
     if ($failed) {
         $monograms = mpull($failed, 'getMonogram');
         echo tsprintf("%s\n", pht('Failures: %s.', implode(', ', $monograms)));
         return 1;
     }
     return 0;
 }