2011-01-10 00:22:25 +01:00
|
|
|
<?php
|
|
|
|
|
2011-02-19 20:36:08 +01:00
|
|
|
/**
|
|
|
|
* Exports changes from Differential or the working copy to a file.
|
|
|
|
*/
|
2014-07-21 23:49:15 +02:00
|
|
|
final class ArcanistExportWorkflow extends ArcanistWorkflow {
|
2011-01-10 00:22:25 +01:00
|
|
|
|
|
|
|
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)
|
2012-11-09 22:36:15 +01:00
|
|
|
**export** [__commit_range__] __format__ (git, hg)
|
2011-01-10 00:22:25 +01:00
|
|
|
**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
|
2012-11-09 22:36:15 +01:00
|
|
|
Supports: svn, git, hg
|
2011-01-10 00:22:25 +01:00
|
|
|
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(
|
2015-05-13 10:05:15 +02:00
|
|
|
'help' => pht(
|
2011-01-10 00:22:25 +01:00
|
|
|
"Export change as a git patch. This format is more complete than ".
|
|
|
|
"unified, but less complete than arc bundles. These patches can be ".
|
2015-05-13 10:05:15 +02:00
|
|
|
"applied with '%s' or '%s'.",
|
|
|
|
'git apply',
|
|
|
|
'arc patch'),
|
2011-01-10 00:22:25 +01:00
|
|
|
),
|
|
|
|
'unified' => array(
|
2015-05-13 10:05:15 +02:00
|
|
|
'help' => pht(
|
2011-01-10 00:22:25 +01:00
|
|
|
"Export change as a unified patch. This format is less complete ".
|
|
|
|
"than git patches or arc bundles. These patches can be applied with ".
|
2015-05-13 10:05:15 +02:00
|
|
|
"'%s' or '%s'.",
|
|
|
|
'patch',
|
|
|
|
'arc patch'),
|
2011-01-10 00:22:25 +01:00
|
|
|
),
|
|
|
|
'arcbundle' => array(
|
|
|
|
'param' => 'file',
|
2015-05-13 10:05:15 +02:00
|
|
|
'help' => pht(
|
2011-01-10 00:22:25 +01:00
|
|
|
"Export change as an arc bundle. This format can represent all ".
|
2015-05-13 10:05:15 +02:00
|
|
|
"changes. These bundles can be applied with '%s'.",
|
|
|
|
'arc patch'),
|
2011-01-10 00:22:25 +01:00
|
|
|
),
|
2012-03-14 15:08:06 +01:00
|
|
|
'encoding' => array(
|
|
|
|
'param' => 'encoding',
|
2015-05-13 10:05:15 +02:00
|
|
|
'help' => pht(
|
|
|
|
'Attempt to convert non UTF-8 patch into specified encoding.'),
|
2012-03-14 15:08:06 +01:00
|
|
|
),
|
2011-01-10 00:22:25 +01:00
|
|
|
'revision' => array(
|
|
|
|
'param' => 'revision_id',
|
2015-05-13 10:05:15 +02:00
|
|
|
'help' => pht(
|
2014-05-23 22:53:05 +02:00
|
|
|
'Instead of exporting changes from the working copy, export them '.
|
2015-05-13 10:05:15 +02:00
|
|
|
'from a Differential revision.'),
|
2011-01-10 00:22:25 +01:00
|
|
|
),
|
|
|
|
'diff' => array(
|
|
|
|
'param' => 'diff_id',
|
2015-05-13 10:05:15 +02:00
|
|
|
'help' => pht(
|
2014-05-23 22:53:05 +02:00
|
|
|
'Instead of exporting changes from the working copy, export them '.
|
2015-05-13 10:05:15 +02:00
|
|
|
'from a Differential diff.'),
|
2011-01-10 00:22:25 +01:00
|
|
|
),
|
|
|
|
'*' => '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(
|
2015-05-13 10:05:15 +02:00
|
|
|
pht(
|
|
|
|
"Options '%s' and '%s' are not compatible. Choose exactly ".
|
|
|
|
"one change source.",
|
|
|
|
'--revision',
|
|
|
|
'--diff'));
|
2011-01-10 00:22:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$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(
|
2015-05-13 10:05:15 +02:00
|
|
|
pht(
|
|
|
|
"Specify one of '%s', '%s' or '%s' to choose an export format.",
|
|
|
|
'--git',
|
|
|
|
'--unified',
|
|
|
|
'--arcbundle <path>'));
|
2011-01-10 00:22:25 +01:00
|
|
|
} else if ($requested > 1) {
|
|
|
|
throw new ArcanistUsageException(
|
2015-05-13 10:05:15 +02:00
|
|
|
pht(
|
|
|
|
"Options '%s', '%s' and '%s' are not compatible. ".
|
|
|
|
"Choose exactly one export format.",
|
|
|
|
'--git',
|
|
|
|
'--unified',
|
|
|
|
'--arcbundle'));
|
2011-01-10 00:22:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$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-25 22:37:56 +02:00
|
|
|
$parser->setRepositoryAPI($repository_api);
|
2011-02-06 22:04:01 +01:00
|
|
|
|
2011-01-29 21:45:37 +01:00
|
|
|
if ($repository_api instanceof ArcanistGitAPI) {
|
2012-12-17 21:54:08 +01:00
|
|
|
$this->parseBaseCommitArgument($this->getArgument('paths'));
|
2014-06-11 23:37:01 +02:00
|
|
|
$diff = $repository_api->getFullGitDiff(
|
|
|
|
$repository_api->getBaseCommit(),
|
|
|
|
$repository_api->getHeadCommit());
|
2011-01-29 21:45:37 +01:00
|
|
|
$changes = $parser->parseDiff($diff);
|
2012-11-09 22:36:15 +01:00
|
|
|
$authors = $this->getConduit()->callMethodSynchronous(
|
|
|
|
'user.query',
|
|
|
|
array(
|
|
|
|
'phids' => array($this->getUserPHID()),
|
|
|
|
));
|
|
|
|
$author_dict = reset($authors);
|
Don't use `emailuser` in Mercurial
Summary:
The `emailuser` template is a relatively recent addition to Mercurial, and a few users have complained about it. It also doesn't actually do what I thought it did, e.g. in an address like this:
"Abraham Lincoln" <alincoln@whitehouse.gov>
^^^^^^^^^^^^^^^ ^^^^^^^^
(1) (2)
^^^^^^^^^^^^^^^^^^^^^^^
(3)
...I want (1), but `emailuser` means (2). Instead, extract (1) with `getDisplayName()` and (3) with `getAddress()` using PhutilEmailAddress.
The implementation in Mercurial is not particularly sophisticated or magical (it just looks for "@" and "<") so we aren't really missing anything by doing this ourselves, at least today.
Also fix some issues in `arc export`, which literally no one uses, but which is occasionally useful for testing (as here).
Test Plan:
- Ran `arc diff --only` in an `hg` repo, checked DB to see that name/email were correctly extracted.
- Ran `arc export --git` in an `hg` repo, didn't get a long series of fatals.
Reviewers: btrahan, DurhamGoode
Reviewed By: DurhamGoode
CC: aran
Maniphest Tasks: T2866, T2858
Differential Revision: https://secure.phabricator.com/D5539
2013-04-02 23:06:46 +02:00
|
|
|
|
|
|
|
list($email) = $repository_api->execxLocal('config user.email');
|
|
|
|
|
2012-11-09 22:36:15 +01:00
|
|
|
$author = sprintf('%s <%s>',
|
|
|
|
$author_dict['realName'],
|
Don't use `emailuser` in Mercurial
Summary:
The `emailuser` template is a relatively recent addition to Mercurial, and a few users have complained about it. It also doesn't actually do what I thought it did, e.g. in an address like this:
"Abraham Lincoln" <alincoln@whitehouse.gov>
^^^^^^^^^^^^^^^ ^^^^^^^^
(1) (2)
^^^^^^^^^^^^^^^^^^^^^^^
(3)
...I want (1), but `emailuser` means (2). Instead, extract (1) with `getDisplayName()` and (3) with `getAddress()` using PhutilEmailAddress.
The implementation in Mercurial is not particularly sophisticated or magical (it just looks for "@" and "<") so we aren't really missing anything by doing this ourselves, at least today.
Also fix some issues in `arc export`, which literally no one uses, but which is occasionally useful for testing (as here).
Test Plan:
- Ran `arc diff --only` in an `hg` repo, checked DB to see that name/email were correctly extracted.
- Ran `arc export --git` in an `hg` repo, didn't get a long series of fatals.
Reviewers: btrahan, DurhamGoode
Reviewed By: DurhamGoode
CC: aran
Maniphest Tasks: T2866, T2858
Differential Revision: https://secure.phabricator.com/D5539
2013-04-02 23:06:46 +02:00
|
|
|
$email);
|
2012-11-09 22:36:15 +01:00
|
|
|
} else if ($repository_api instanceof ArcanistMercurialAPI) {
|
Don't use `emailuser` in Mercurial
Summary:
The `emailuser` template is a relatively recent addition to Mercurial, and a few users have complained about it. It also doesn't actually do what I thought it did, e.g. in an address like this:
"Abraham Lincoln" <alincoln@whitehouse.gov>
^^^^^^^^^^^^^^^ ^^^^^^^^
(1) (2)
^^^^^^^^^^^^^^^^^^^^^^^
(3)
...I want (1), but `emailuser` means (2). Instead, extract (1) with `getDisplayName()` and (3) with `getAddress()` using PhutilEmailAddress.
The implementation in Mercurial is not particularly sophisticated or magical (it just looks for "@" and "<") so we aren't really missing anything by doing this ourselves, at least today.
Also fix some issues in `arc export`, which literally no one uses, but which is occasionally useful for testing (as here).
Test Plan:
- Ran `arc diff --only` in an `hg` repo, checked DB to see that name/email were correctly extracted.
- Ran `arc export --git` in an `hg` repo, didn't get a long series of fatals.
Reviewers: btrahan, DurhamGoode
Reviewed By: DurhamGoode
CC: aran
Maniphest Tasks: T2866, T2858
Differential Revision: https://secure.phabricator.com/D5539
2013-04-02 23:06:46 +02:00
|
|
|
$this->parseBaseCommitArgument($this->getArgument('paths'));
|
2012-11-09 22:36:15 +01:00
|
|
|
$diff = $repository_api->getFullMercurialDiff();
|
|
|
|
$changes = $parser->parseDiff($diff);
|
|
|
|
$authors = $this->getConduit()->callMethodSynchronous(
|
|
|
|
'user.query',
|
|
|
|
array(
|
|
|
|
'phids' => array($this->getUserPHID()),
|
|
|
|
));
|
Don't use `emailuser` in Mercurial
Summary:
The `emailuser` template is a relatively recent addition to Mercurial, and a few users have complained about it. It also doesn't actually do what I thought it did, e.g. in an address like this:
"Abraham Lincoln" <alincoln@whitehouse.gov>
^^^^^^^^^^^^^^^ ^^^^^^^^
(1) (2)
^^^^^^^^^^^^^^^^^^^^^^^
(3)
...I want (1), but `emailuser` means (2). Instead, extract (1) with `getDisplayName()` and (3) with `getAddress()` using PhutilEmailAddress.
The implementation in Mercurial is not particularly sophisticated or magical (it just looks for "@" and "<") so we aren't really missing anything by doing this ourselves, at least today.
Also fix some issues in `arc export`, which literally no one uses, but which is occasionally useful for testing (as here).
Test Plan:
- Ran `arc diff --only` in an `hg` repo, checked DB to see that name/email were correctly extracted.
- Ran `arc export --git` in an `hg` repo, didn't get a long series of fatals.
Reviewers: btrahan, DurhamGoode
Reviewed By: DurhamGoode
CC: aran
Maniphest Tasks: T2866, T2858
Differential Revision: https://secure.phabricator.com/D5539
2013-04-02 23:06:46 +02:00
|
|
|
|
|
|
|
list($author) = $repository_api->execxLocal('showconfig ui.username');
|
2011-01-29 21:45:37 +01:00
|
|
|
} else {
|
|
|
|
// TODO: paths support
|
|
|
|
$paths = $repository_api->getWorkingCopyStatus();
|
|
|
|
$changes = $parser->parseSubversionDiff(
|
|
|
|
$repository_api,
|
|
|
|
$paths);
|
2012-11-09 22:36:15 +01:00
|
|
|
$author = $this->getUserName();
|
2011-01-29 21:45:37 +01:00
|
|
|
}
|
2011-01-10 00:22:25 +01:00
|
|
|
|
|
|
|
$bundle = ArcanistBundle::newFromChanges($changes);
|
2011-12-03 01:21:14 +01:00
|
|
|
$bundle->setBaseRevision(
|
|
|
|
$repository_api->getSourceControlBaseRevision());
|
2014-07-09 01:12:13 +02:00
|
|
|
// NOTE: we can't get a revision ID for SOURCE_LOCAL
|
Don't use `emailuser` in Mercurial
Summary:
The `emailuser` template is a relatively recent addition to Mercurial, and a few users have complained about it. It also doesn't actually do what I thought it did, e.g. in an address like this:
"Abraham Lincoln" <alincoln@whitehouse.gov>
^^^^^^^^^^^^^^^ ^^^^^^^^
(1) (2)
^^^^^^^^^^^^^^^^^^^^^^^
(3)
...I want (1), but `emailuser` means (2). Instead, extract (1) with `getDisplayName()` and (3) with `getAddress()` using PhutilEmailAddress.
The implementation in Mercurial is not particularly sophisticated or magical (it just looks for "@" and "<") so we aren't really missing anything by doing this ourselves, at least today.
Also fix some issues in `arc export`, which literally no one uses, but which is occasionally useful for testing (as here).
Test Plan:
- Ran `arc diff --only` in an `hg` repo, checked DB to see that name/email were correctly extracted.
- Ran `arc export --git` in an `hg` repo, didn't get a long series of fatals.
Reviewers: btrahan, DurhamGoode
Reviewed By: DurhamGoode
CC: aran
Maniphest Tasks: T2866, T2858
Differential Revision: https://secure.phabricator.com/D5539
2013-04-02 23:06:46 +02:00
|
|
|
|
|
|
|
$parser = new PhutilEmailAddress($author);
|
|
|
|
$bundle->setAuthorName($parser->getDisplayName());
|
|
|
|
$bundle->setAuthorEmail($parser->getAddress());
|
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) {
|
|
|
|
$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');
|
2015-05-13 10:05:15 +02:00
|
|
|
echo pht("Writing bundle to '%s'...", $path)."\n";
|
2011-01-10 00:22:25 +01:00
|
|
|
$bundle->writeToDisk($path);
|
2015-05-13 10:05:15 +02:00
|
|
|
echo pht('Done.')."\n";
|
2011-01-10 00:22:25 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-07-09 01:12:13 +02:00
|
|
|
|
2011-01-10 00:22:25 +01:00
|
|
|
}
|