Changeset View
Changeset View
Standalone View
Standalone View
src/workflow/ArcanistFeatureWorkflow.php
Show All 32 Lines | return phutil_console_format(<<<EOTEXT | ||||
__name__ doesn't exist and is in format D123 then the branch of | __name__ doesn't exist and is in format D123 then the branch of | ||||
revision D123 is checked out. Use __start__ to specify where the new | revision D123 is checked out. Use __start__ to specify where the new | ||||
branch will start. Use 'arc.feature.start.default' to set the default | branch will start. Use 'arc.feature.start.default' to set the default | ||||
feature start location. | feature start location. | ||||
EOTEXT | EOTEXT | ||||
); | ); | ||||
} | } | ||||
public function requiresConduit() { | |||||
return true; | |||||
} | |||||
public function requiresRepositoryAPI() { | public function requiresRepositoryAPI() { | ||||
return true; | return true; | ||||
} | } | ||||
public function requiresAuthentication() { | |||||
return !$this->getArgument('branch'); | |||||
} | |||||
public function getArguments() { | public function getArguments() { | ||||
return array( | return array( | ||||
'view-all' => array( | 'view-all' => array( | ||||
'help' => pht('Include closed and abandoned revisions.'), | 'help' => pht('Include closed and abandoned revisions.'), | ||||
), | ), | ||||
'by-status' => array( | 'by-status' => array( | ||||
'help' => pht('Sort branches by status instead of time.'), | 'help' => pht('Sort branches by status instead of time.'), | ||||
), | ), | ||||
Show All 20 Lines | public function run() { | ||||
$names = $this->getArgument('branch'); | $names = $this->getArgument('branch'); | ||||
if ($names) { | if ($names) { | ||||
if (count($names) > 2) { | if (count($names) > 2) { | ||||
throw new ArcanistUsageException(pht('Specify only one branch.')); | throw new ArcanistUsageException(pht('Specify only one branch.')); | ||||
} | } | ||||
return $this->checkoutBranch($names); | return $this->checkoutBranch($names); | ||||
} | } | ||||
$branches = $repository_api->getAllBranches(); | // TODO: Everything in this whole workflow that says "branch" means | ||||
// "bookmark" in Mercurial. | |||||
$branches = $repository_api->getAllBranchRefs(); | |||||
if (!$branches) { | if (!$branches) { | ||||
throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
pht('No branches in this working copy.')); | pht('No branches in this working copy.')); | ||||
} | } | ||||
$branches = $this->loadCommitInfo($branches); | $states = array(); | ||||
$revisions = $this->loadRevisions($branches); | foreach ($branches as $branch) { | ||||
$this->printBranches($branches, $revisions); | $states[] = $this->newWorkingCopyStateRef() | ||||
->attachBranchRef($branch); | |||||
} | |||||
$this->newRefQuery($states) | |||||
->needHardpoints( | |||||
array( | |||||
'revisionRefs', | |||||
)) | |||||
->execute(); | |||||
$this->printBranches($states); | |||||
return 0; | return 0; | ||||
} | } | ||||
private function checkoutBranch(array $names) { | private function checkoutBranch(array $names) { | ||||
$api = $this->getRepositoryAPI(); | $api = $this->getRepositoryAPI(); | ||||
if ($api instanceof ArcanistMercurialAPI) { | if ($api instanceof ArcanistMercurialAPI) { | ||||
Show All 14 Lines | private function checkoutBranch(array $names) { | ||||
$branches = $api->getAllBranches(); | $branches = $api->getAllBranches(); | ||||
if (in_array($name, ipull($branches, 'name'))) { | if (in_array($name, ipull($branches, 'name'))) { | ||||
list($err, $stdout, $stderr) = $api->execManualLocal($command, $name); | list($err, $stdout, $stderr) = $api->execManualLocal($command, $name); | ||||
} | } | ||||
if ($err) { | if ($err) { | ||||
$match = null; | $match = null; | ||||
if (preg_match('/^D(\d+)$/', $name, $match)) { | if (preg_match('/^D(\d+)$/', $name, $match)) { | ||||
try { | $diff = $this->getConduitEngine()->resolveCall( | ||||
$diff = $this->getConduit()->callMethodSynchronous( | |||||
'differential.querydiffs', | 'differential.querydiffs', | ||||
array( | array( | ||||
'revisionIDs' => array($match[1]), | 'revisionIDs' => array($match[1]), | ||||
)); | )); | ||||
$diff = head($diff); | $diff = head($diff); | ||||
if ($diff['branch'] != '') { | if ($diff['branch'] != '') { | ||||
$name = $diff['branch']; | $name = $diff['branch']; | ||||
list($err, $stdout, $stderr) = $api->execManualLocal( | list($err, $stdout, $stderr) = $api->execManualLocal( | ||||
$command, | $command, | ||||
$name); | $name); | ||||
} | } | ||||
} catch (ConduitClientException $ex) {} | |||||
} | } | ||||
} | } | ||||
if ($err) { | if ($err) { | ||||
if ($api instanceof ArcanistMercurialAPI) { | if ($api instanceof ArcanistMercurialAPI) { | ||||
$rev = ''; | $rev = ''; | ||||
if ($start) { | if ($start) { | ||||
$rev = csprintf('-r %s', $start); | $rev = csprintf('-r %s', $start); | ||||
Show All 15 Lines | if ($err) { | ||||
list($err, $stdout, $stderr) = $exec; | list($err, $stdout, $stderr) = $exec; | ||||
} | } | ||||
echo $stdout; | echo $stdout; | ||||
fprintf(STDERR, $stderr); | fprintf(STDERR, $stderr); | ||||
return $err; | return $err; | ||||
} | } | ||||
private function loadCommitInfo(array $branches) { | private function printBranches(array $states) { | ||||
$repository_api = $this->getRepositoryAPI(); | |||||
$branches = ipull($branches, null, 'name'); | |||||
if ($repository_api instanceof ArcanistMercurialAPI) { | |||||
epriestley: This is like 1K lines of code but I was able to remove one `instanceof` so that's progress… | |||||
$futures = array(); | |||||
foreach ($branches as $branch) { | |||||
$futures[$branch['name']] = $repository_api->execFutureLocal( | |||||
'log -l 1 --template %s -r %s', | |||||
"{node}\1{date|hgdate}\1{p1node}\1{desc|firstline}\1{desc}", | |||||
hgsprintf('%s', $branch['name'])); | |||||
} | |||||
$futures = id(new FutureIterator($futures)) | |||||
->limit(16); | |||||
foreach ($futures as $name => $future) { | |||||
list($info) = $future->resolvex(); | |||||
$fields = explode("\1", trim($info), 5); | |||||
list($hash, $epoch, $tree, $desc, $text) = $fields; | |||||
$branches[$name] += array( | |||||
'hash' => $hash, | |||||
'desc' => $desc, | |||||
'tree' => $tree, | |||||
'epoch' => (int)$epoch, | |||||
'text' => $text, | |||||
); | |||||
} | |||||
} | |||||
foreach ($branches as $name => $branch) { | |||||
$text = $branch['text']; | |||||
try { | |||||
$message = ArcanistDifferentialCommitMessage::newFromRawCorpus($text); | |||||
$id = $message->getRevisionID(); | |||||
$branch['revisionID'] = $id; | |||||
} catch (ArcanistUsageException $ex) { | |||||
// In case of invalid commit message which fails the parsing, | |||||
// do nothing. | |||||
$branch['revisionID'] = null; | |||||
} | |||||
$branches[$name] = $branch; | |||||
} | |||||
return $branches; | |||||
} | |||||
private function loadRevisions(array $branches) { | |||||
$ids = array(); | |||||
$hashes = array(); | |||||
foreach ($branches as $branch) { | |||||
if ($branch['revisionID']) { | |||||
$ids[] = $branch['revisionID']; | |||||
} | |||||
$hashes[] = array('gtcm', $branch['hash']); | |||||
$hashes[] = array('gttr', $branch['tree']); | |||||
} | |||||
$calls = array(); | |||||
if ($ids) { | |||||
$calls[] = $this->getConduit()->callMethod( | |||||
'differential.query', | |||||
array( | |||||
'ids' => $ids, | |||||
)); | |||||
} | |||||
if ($hashes) { | |||||
$calls[] = $this->getConduit()->callMethod( | |||||
'differential.query', | |||||
array( | |||||
'commitHashes' => $hashes, | |||||
)); | |||||
} | |||||
$results = array(); | |||||
foreach (new FutureIterator($calls) as $call) { | |||||
$results[] = $call->resolve(); | |||||
} | |||||
return array_mergev($results); | |||||
} | |||||
private function printBranches(array $branches, array $revisions) { | |||||
$revisions = ipull($revisions, null, 'id'); | |||||
static $color_map = array( | static $color_map = array( | ||||
'Closed' => 'cyan', | 'Closed' => 'cyan', | ||||
'Needs Review' => 'magenta', | 'Needs Review' => 'magenta', | ||||
'Needs Revision' => 'red', | 'Needs Revision' => 'red', | ||||
'Accepted' => 'green', | 'Accepted' => 'green', | ||||
'No Revision' => 'blue', | 'No Revision' => 'blue', | ||||
'Abandoned' => 'default', | 'Abandoned' => 'default', | ||||
); | ); | ||||
static $ssort_map = array( | static $ssort_map = array( | ||||
'Closed' => 1, | 'Closed' => 1, | ||||
'No Revision' => 2, | 'No Revision' => 2, | ||||
'Needs Review' => 3, | 'Needs Review' => 3, | ||||
'Needs Revision' => 4, | 'Needs Revision' => 4, | ||||
'Accepted' => 5, | 'Accepted' => 5, | ||||
); | ); | ||||
$out = array(); | $out = array(); | ||||
foreach ($branches as $branch) { | foreach ($states as $state) { | ||||
$revision = idx($revisions, idx($branch, 'revisionID')); | $branch = $state->getBranchRef(); | ||||
// If we haven't identified a revision by ID, try to identify it by hash. | |||||
if (!$revision) { | |||||
foreach ($revisions as $rev) { | |||||
$hashes = idx($rev, 'hashes', array()); | |||||
foreach ($hashes as $hash) { | |||||
if (($hash[0] == 'gtcm' && $hash[1] == $branch['hash']) || | |||||
($hash[0] == 'gttr' && $hash[1] == $branch['tree'])) { | |||||
$revision = $rev; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
$revision = null; | |||||
if ($state->hasAmbiguousRevisionRefs()) { | |||||
$status = pht('Ambiguous Revision'); | |||||
} else { | |||||
$revision = $state->getRevisionRef(); | |||||
if ($revision) { | if ($revision) { | ||||
$desc = 'D'.$revision['id'].': '.$revision['title']; | $status = $revision->getStatusDisplayName(); | ||||
$status = $revision['statusName']; | |||||
} else { | } else { | ||||
$desc = $branch['desc']; | |||||
$status = pht('No Revision'); | $status = pht('No Revision'); | ||||
} | } | ||||
} | |||||
if (!$this->getArgument('view-all') && !$branch['current']) { | if (!$this->getArgument('view-all') && !$branch->getIsCurrentBranch()) { | ||||
if ($status == 'Closed' || $status == 'Abandoned') { | if ($status == 'Closed' || $status == 'Abandoned') { | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
$epoch = $branch['epoch']; | $commit = $branch->getCommitRef(); | ||||
$epoch = $commit->getCommitEpoch(); | |||||
$color = idx($color_map, $status, 'default'); | $color = idx($color_map, $status, 'default'); | ||||
$ssort = sprintf('%d%012d', idx($ssort_map, $status, 0), $epoch); | $ssort = sprintf('%d%012d', idx($ssort_map, $status, 0), $epoch); | ||||
if ($revision) { | |||||
$desc = $revision->getFullName(); | |||||
} else { | |||||
$desc = $commit->getSummary(); | |||||
} | |||||
$out[] = array( | $out[] = array( | ||||
'name' => $branch['name'], | 'name' => $branch->getBranchName(), | ||||
'current' => $branch['current'], | 'current' => $branch->getIsCurrentBranch(), | ||||
'status' => $status, | 'status' => $status, | ||||
'desc' => $desc, | 'desc' => $desc, | ||||
'revision' => $revision ? $revision['id'] : null, | 'revision' => $revision ? $revision->getID() : null, | ||||
'color' => $color, | 'color' => $color, | ||||
'esort' => $epoch, | 'esort' => $epoch, | ||||
'epoch' => $epoch, | 'epoch' => $epoch, | ||||
'ssort' => $ssort, | 'ssort' => $ssort, | ||||
); | ); | ||||
} | } | ||||
if (!$out) { | if (!$out) { | ||||
Show All 40 Lines |
This is like 1K lines of code but I was able to remove one instanceof so that's progress right?