2011-01-10 00:22:25 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
2011-12-03 01:21:14 +01:00
|
|
|
* Copyright 2012 Facebook, Inc.
|
2011-01-10 00:22:25 +01:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2011-02-19 20:36:08 +01:00
|
|
|
/**
|
|
|
|
* Exports changes from Differential or the working copy to a file.
|
|
|
|
*
|
|
|
|
* @group workflow
|
|
|
|
*/
|
2011-01-10 00:22:25 +01:00
|
|
|
final class ArcanistExportWorkflow extends ArcanistBaseWorkflow {
|
|
|
|
|
|
|
|
const SOURCE_LOCAL = 'local';
|
|
|
|
const SOURCE_DIFF = 'diff';
|
|
|
|
const SOURCE_REVISION = 'revision';
|
|
|
|
|
|
|
|
const FORMAT_GIT = 'git';
|
|
|
|
const FORMAT_UNIFIED = 'unified';
|
|
|
|
const FORMAT_BUNDLE = 'arcbundle';
|
|
|
|
|
|
|
|
private $source;
|
|
|
|
private $sourceID;
|
|
|
|
private $format;
|
|
|
|
|
Make Arcanist workflow names explicit
Summary:
Currently, adding a new workflow requires you to override ArcanistConfiguration, which is messy. Instead, just load everything that extends ArcanistBaseWorkflow.
Remove all the rules tying workflow names to class names through arcane incantations.
This has a very small performance cost in that we need to load every Workflow class every time now, but we don't hit __init__ and such anymore and it was pretty negligible on my machine (98ms vs 104ms or something).
Test Plan: Ran "arc help", "arc which", "arc diff", etc.
Reviewers: edward, vrana, btrahan
Reviewed By: edward
CC: aran, zeeg
Differential Revision: https://secure.phabricator.com/D3691
2012-10-17 17:35:03 +02:00
|
|
|
public function getWorkflowName() {
|
|
|
|
return 'export';
|
|
|
|
}
|
|
|
|
|
2012-03-05 19:02:37 +01:00
|
|
|
public function getCommandSynopses() {
|
2011-01-10 00:22:25 +01:00
|
|
|
return phutil_console_format(<<<EOTEXT
|
|
|
|
**export** [__paths__] __format__ (svn)
|
|
|
|
**export** [__commit_range__] __format__ (git)
|
|
|
|
**export** __--revision__ __revision_id__ __format__
|
|
|
|
**export** __--diff__ __diff_id__ __format__
|
2012-03-05 19:02:37 +01:00
|
|
|
EOTEXT
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCommandHelp() {
|
|
|
|
return phutil_console_format(<<<EOTEXT
|
2011-01-10 00:22:25 +01:00
|
|
|
Supports: git, svn
|
|
|
|
Export the local changeset (or a Differential changeset) to a file,
|
|
|
|
in some __format__: git diff (__--git__), unified diff
|
|
|
|
(__--unified__), or arc bundle (__--arcbundle__ __path__) format.
|
|
|
|
EOTEXT
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getArguments() {
|
|
|
|
return array(
|
|
|
|
'git' => array(
|
|
|
|
'help' =>
|
|
|
|
"Export change as a git patch. This format is more complete than ".
|
|
|
|
"unified, but less complete than arc bundles. These patches can be ".
|
|
|
|
"applied with 'git apply' or 'arc patch'.",
|
|
|
|
),
|
|
|
|
'unified' => array(
|
|
|
|
'help' =>
|
|
|
|
"Export change as a unified patch. This format is less complete ".
|
|
|
|
"than git patches or arc bundles. These patches can be applied with ".
|
|
|
|
"'patch' or 'arc patch'.",
|
|
|
|
),
|
|
|
|
'arcbundle' => array(
|
|
|
|
'param' => 'file',
|
|
|
|
'help' =>
|
|
|
|
"Export change as an arc bundle. This format can represent all ".
|
|
|
|
"changes. These bundles can be applied with 'arc patch'.",
|
|
|
|
),
|
2012-03-14 15:08:06 +01:00
|
|
|
'encoding' => array(
|
|
|
|
'param' => 'encoding',
|
|
|
|
'help' =>
|
|
|
|
"Attempt to convert non UTF-8 patch into specified encoding.",
|
|
|
|
),
|
2011-01-10 00:22:25 +01:00
|
|
|
'revision' => array(
|
|
|
|
'param' => 'revision_id',
|
|
|
|
'help' =>
|
|
|
|
"Instead of exporting changes from the working copy, export them ".
|
|
|
|
"from a Differential revision."
|
|
|
|
),
|
|
|
|
'diff' => array(
|
|
|
|
'param' => 'diff_id',
|
|
|
|
'help' =>
|
|
|
|
"Instead of exporting changes from the working copy, export them ".
|
|
|
|
"from a Differential diff."
|
|
|
|
),
|
|
|
|
'*' => 'paths',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected function didParseArguments() {
|
|
|
|
$source = self::SOURCE_LOCAL;
|
|
|
|
$requested = 0;
|
|
|
|
if ($this->getArgument('revision')) {
|
|
|
|
$source = self::SOURCE_REVISION;
|
|
|
|
$requested++;
|
2012-06-28 03:33:28 +02:00
|
|
|
|
|
|
|
$source_id = $this->getArgument($source);
|
|
|
|
$this->sourceID = $this->normalizeRevisionID($source_id);
|
2011-01-10 00:22:25 +01:00
|
|
|
}
|
|
|
|
if ($this->getArgument('diff')) {
|
|
|
|
$source = self::SOURCE_DIFF;
|
|
|
|
$requested++;
|
2012-06-28 03:33:28 +02:00
|
|
|
|
|
|
|
$this->sourceID = $this->getArgument($source);
|
2011-01-10 00:22:25 +01:00
|
|
|
}
|
2012-06-28 03:33:28 +02:00
|
|
|
$this->source = $source;
|
2011-01-10 00:22:25 +01:00
|
|
|
|
|
|
|
if ($requested > 1) {
|
|
|
|
throw new ArcanistUsageException(
|
|
|
|
"Options '--revision' and '--diff' are not compatible. Choose exactly ".
|
|
|
|
"one change source.");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$format = null;
|
|
|
|
$requested = 0;
|
|
|
|
if ($this->getArgument('git')) {
|
|
|
|
$format = self::FORMAT_GIT;
|
|
|
|
$requested++;
|
|
|
|
}
|
|
|
|
if ($this->getArgument('unified')) {
|
|
|
|
$format = self::FORMAT_UNIFIED;
|
|
|
|
$requested++;
|
|
|
|
}
|
|
|
|
if ($this->getArgument('arcbundle')) {
|
|
|
|
$format = self::FORMAT_BUNDLE;
|
|
|
|
$requested++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($requested === 0) {
|
|
|
|
throw new ArcanistUsageException(
|
|
|
|
"Specify one of '--git', '--unified' or '--arcbundle <path>' to ".
|
|
|
|
"choose an export format.");
|
|
|
|
} else if ($requested > 1) {
|
|
|
|
throw new ArcanistUsageException(
|
|
|
|
"Options '--git', '--unified' and '--arcbundle' are not compatible. ".
|
|
|
|
"Choose exactly one export format.");
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->format = $format;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function requiresConduit() {
|
2012-05-11 02:24:59 +02:00
|
|
|
return true;
|
2011-01-10 00:22:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function requiresAuthentication() {
|
|
|
|
return $this->requiresConduit();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function requiresRepositoryAPI() {
|
|
|
|
return $this->getSource() == self::SOURCE_LOCAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function requiresWorkingCopy() {
|
|
|
|
return $this->getSource() == self::SOURCE_LOCAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getSource() {
|
|
|
|
return $this->source;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getSourceID() {
|
|
|
|
return $this->sourceID;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getFormat() {
|
|
|
|
return $this->format;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function run() {
|
|
|
|
|
|
|
|
$source = $this->getSource();
|
|
|
|
|
|
|
|
switch ($source) {
|
|
|
|
case self::SOURCE_LOCAL:
|
|
|
|
$repository_api = $this->getRepositoryAPI();
|
|
|
|
$parser = new ArcanistDiffParser();
|
2012-10-20 17:42:16 +02:00
|
|
|
$parser->setRepositorAPI($repository_api);
|
2011-02-06 22:04:01 +01:00
|
|
|
|
2011-01-29 21:45:37 +01:00
|
|
|
if ($repository_api instanceof ArcanistGitAPI) {
|
2011-10-18 04:22:48 +02:00
|
|
|
$repository_api->parseRelativeLocalCommit(
|
2011-01-29 21:45:37 +01:00
|
|
|
$this->getArgument('paths'));
|
|
|
|
$diff = $repository_api->getFullGitDiff();
|
|
|
|
$changes = $parser->parseDiff($diff);
|
|
|
|
} else {
|
|
|
|
// TODO: paths support
|
|
|
|
$paths = $repository_api->getWorkingCopyStatus();
|
|
|
|
$changes = $parser->parseSubversionDiff(
|
|
|
|
$repository_api,
|
|
|
|
$paths);
|
|
|
|
}
|
2011-01-10 00:22:25 +01:00
|
|
|
|
|
|
|
$bundle = ArcanistBundle::newFromChanges($changes);
|
2011-11-30 07:27:40 +01:00
|
|
|
$bundle->setProjectID($this->getWorkingCopy()->getProjectID());
|
2011-12-03 01:21:14 +01:00
|
|
|
$bundle->setBaseRevision(
|
|
|
|
$repository_api->getSourceControlBaseRevision());
|
2012-01-18 00:47:00 +01:00
|
|
|
// note we can't get a revision ID for SOURCE_LOCAL
|
2011-01-10 00:22:25 +01:00
|
|
|
break;
|
|
|
|
case self::SOURCE_REVISION:
|
|
|
|
$bundle = $this->loadRevisionBundleFromConduit(
|
|
|
|
$this->getConduit(),
|
|
|
|
$this->getSourceID());
|
|
|
|
break;
|
|
|
|
case self::SOURCE_DIFF:
|
|
|
|
$bundle = $this->loadDiffBundleFromConduit(
|
|
|
|
$this->getConduit(),
|
|
|
|
$this->getSourceID());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-03-14 15:08:06 +01:00
|
|
|
$try_encoding = nonempty($this->getArgument('encoding'), null);
|
|
|
|
if (!$try_encoding) {
|
|
|
|
try {
|
2012-05-11 02:24:59 +02:00
|
|
|
$project_info = $this->getConduit()->callMethodSynchronous(
|
|
|
|
'arcanist.projectinfo',
|
|
|
|
array(
|
|
|
|
'name' => $bundle->getProjectID(),
|
|
|
|
));
|
|
|
|
$try_encoding = $project_info['encoding'];
|
2012-03-14 15:08:06 +01:00
|
|
|
} catch (ConduitClientException $e) {
|
|
|
|
$try_encoding = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($try_encoding) {
|
|
|
|
$bundle->setEncoding($try_encoding);
|
|
|
|
}
|
|
|
|
|
2011-01-10 00:22:25 +01:00
|
|
|
$format = $this->getFormat();
|
|
|
|
|
|
|
|
switch ($format) {
|
|
|
|
case self::FORMAT_GIT:
|
|
|
|
echo $bundle->toGitPatch();
|
|
|
|
break;
|
|
|
|
case self::FORMAT_UNIFIED:
|
|
|
|
echo $bundle->toUnifiedDiff();
|
|
|
|
break;
|
|
|
|
case self::FORMAT_BUNDLE:
|
|
|
|
$path = $this->getArgument('arcbundle');
|
2011-05-21 16:52:49 +02:00
|
|
|
echo "Writing bundle to '{$path}'...\n";
|
2011-01-10 00:22:25 +01:00
|
|
|
$bundle->writeToDisk($path);
|
|
|
|
echo "done.\n";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|