diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -113,6 +113,7 @@ 'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php', 'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php', 'ArcanistGitLandEngine' => 'land/ArcanistGitLandEngine.php', + 'ArcanistGitUpstreamPath' => 'repository/api/ArcanistGitUpstreamPath.php', 'ArcanistGlobalVariableXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistGlobalVariableXHPASTLinterRule.php', 'ArcanistGoLintLinter' => 'lint/linter/ArcanistGoLintLinter.php', 'ArcanistGoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistGoLintLinterTestCase.php', @@ -401,6 +402,7 @@ 'ArcanistGetConfigWorkflow' => 'ArcanistWorkflow', 'ArcanistGitAPI' => 'ArcanistRepositoryAPI', 'ArcanistGitLandEngine' => 'ArcanistLandEngine', + 'ArcanistGitUpstreamPath' => 'Phobject', 'ArcanistGlobalVariableXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistGoLintLinter' => 'ArcanistExternalLinter', 'ArcanistGoLintLinterTestCase' => 'ArcanistExternalLinterTestCase', diff --git a/src/repository/api/ArcanistGitAPI.php b/src/repository/api/ArcanistGitAPI.php --- a/src/repository/api/ArcanistGitAPI.php +++ b/src/repository/api/ArcanistGitAPI.php @@ -1342,7 +1342,7 @@ */ public function getPathToUpstream($start) { $cursor = $start; - $path = array(); + $path = new ArcanistGitUpstreamPath(); while (true) { list($err, $upstream) = $this->execManualLocal( 'rev-parse --symbolic-full-name %s@{upstream}', @@ -1358,13 +1358,15 @@ if (preg_match('(^refs/heads/)', $upstream)) { $upstream = preg_replace('(^refs/heads/)', '', $upstream); - $is_cycle = isset($path[$upstream]); + $is_cycle = $path->getUpstream($upstream); - $path[$cursor] = array( - 'type' => 'local', - 'name' => $upstream, - 'cycle' => $is_cycle, - ); + $path->addUpstream( + $cursor, + array( + 'type' => ArcanistGitUpstreamPath::TYPE_LOCAL, + 'name' => $upstream, + 'cycle' => $is_cycle, + )); if ($is_cycle) { // We ran into a local cycle, so we're done. @@ -1380,11 +1382,13 @@ $upstream = preg_replace('(^refs/remotes/)', '', $upstream); list($remote, $branch) = explode('/', $upstream, 2); - $path[$cursor] = array( - 'type' => 'remote', - 'name' => $branch, - 'remote' => $remote, - ); + $path->addUpstream( + $cursor, + array( + 'type' => ArcanistGitUpstreamPath::TYPE_REMOTE, + 'name' => $branch, + 'remote' => $remote, + )); // We found a remote, so we're done. break; diff --git a/src/repository/api/ArcanistGitUpstreamPath.php b/src/repository/api/ArcanistGitUpstreamPath.php new file mode 100644 --- /dev/null +++ b/src/repository/api/ArcanistGitUpstreamPath.php @@ -0,0 +1,82 @@ +path[$key] = $spec; + return $this; + } + + public function getUpstream($key) { + return idx($this->path, $key); + } + + public function getLength() { + return count($this->path); + } + + /** + * Test if this path eventually connects to a remote. + * + * @return bool True if the path connects to a remote. + */ + public function isConnectedToRemote() { + $last = last($this->path); + + if (!$last) { + return false; + } + + return ($last['type'] == self::TYPE_REMOTE); + } + + + public function getRemoteBranchName() { + if (!$this->isConnectedToRemote()) { + return null; + } + + return idx(last($this->path), 'name'); + } + + public function getRemoteRemoteName() { + if (!$this->isConnectedToRemote()) { + return null; + } + + return idx(last($this->path), 'remote'); + } + + + /** + * If this path contains a cycle, return a description of it. + * + * @return list|null Cycle, if the path contains one. + */ + public function getCycle() { + $last = last($this->path); + if (!$last) { + return null; + } + + if (empty($last['cycle'])) { + return null; + } + + $parts = array(); + foreach ($this->path as $key => $item) { + $parts[] = $key; + } + $parts[] = $item['name']; + $parts[] = pht('...'); + + return $parts; + } + +} diff --git a/src/workflow/ArcanistLandWorkflow.php b/src/workflow/ArcanistLandWorkflow.php --- a/src/workflow/ArcanistLandWorkflow.php +++ b/src/workflow/ArcanistLandWorkflow.php @@ -386,9 +386,9 @@ $api = $this->getRepositoryAPI(); $path = $api->getPathToUpstream($this->branch); - if ($path) { - $last = last($path); - if (isset($last['cycle'])) { + if ($path->getLength()) { + $cycle = $path->getCycle(); + if ($cycle) { $this->writeWarn( pht('LOCAL CYCLE'), pht( @@ -397,11 +397,11 @@ echo tsprintf( "\n %s\n\n", - $this->formatUpstreamPathCycle($path)); + implode(' -> ', $cycle)); } else { - if ($last['type'] == 'remote') { - $onto = $last['name']; + if ($path->isConnectedToRemote()) { + $onto = $path->getRemoteBranchName(); $this->writeInfo( pht('TARGET'), pht( @@ -454,18 +454,15 @@ $api = $this->getRepositoryAPI(); $path = $api->getPathToUpstream($this->branch); - if ($path) { - $last = last($path); - if ($last['type'] == 'remote') { - $remote = $last['remote']; - $this->writeInfo( - pht('REMOTE'), - pht( - 'Using remote "%s", selected by following tracking branches '. - 'upstream to the closest remote.', - $remote)); - return $remote; - } + $remote = $path->getRemoteRemoteName(); + if ($remote !== null) { + $this->writeInfo( + pht('REMOTE'), + pht( + 'Using remote "%s", selected by following tracking branches '. + 'upstream to the closest remote.', + $remote)); + return $remote; } $remote = 'origin'; @@ -477,17 +474,6 @@ return $remote; } - private function formatUpstreamPathCycle(array $cycle) { - $parts = array(); - foreach ($cycle as $key => $value) { - $parts[] = $key; - } - $parts[] = idx(last($cycle), 'name'); - $parts[] = pht('...'); - - return implode(' -> ', $parts); - } - private function readArguments() { $repository_api = $this->getRepositoryAPI();