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, '%s', $stderr); | fprintf(STDERR, '%s', $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) { | |||||
| $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 | |||||