Changeset View
Changeset View
Standalone View
Standalone View
src/repository/marker/ArcanistMercurialRepositoryMarkerQuery.php
| <?php | <?php | ||||
| final class ArcanistMercurialRepositoryMarkerQuery | final class ArcanistMercurialRepositoryMarkerQuery | ||||
| extends ArcanistRepositoryMarkerQuery { | extends ArcanistRepositoryMarkerQuery { | ||||
| protected function newRefMarkers() { | protected function newLocalRefMarkers() { | ||||
| $markers = array(); | return $this->newMarkers(); | ||||
| if ($this->shouldQueryMarkerType(ArcanistMarkerRef::TYPE_BRANCH)) { | |||||
| $markers[] = $this->newBranchOrBookmarkMarkers(false); | |||||
| } | } | ||||
| if ($this->shouldQueryMarkerType(ArcanistMarkerRef::TYPE_BOOKMARK)) { | protected function newRemoteRefMarkers(ArcanistRemoteRef $remote = null) { | ||||
| $markers[] = $this->newBranchOrBookmarkMarkers(true); | return $this->newMarkers($remote); | ||||
| } | } | ||||
| return array_mergev($markers); | private function newMarkers(ArcanistRemoteRef $remote = null) { | ||||
| } | |||||
| private function newBranchOrBookmarkMarkers($is_bookmarks) { | |||||
| $api = $this->getRepositoryAPI(); | $api = $this->getRepositoryAPI(); | ||||
| $is_branches = !$is_bookmarks; | // In native Mercurial it is difficult to identify remote markers, and | ||||
| // complicated to identify local markers efficiently. We use an extension | |||||
| // NOTE: This is a bit clumsy, but it allows us to get most bookmark and | // to provide a command which works like "git for-each-ref" locally and | ||||
| // branch information in a single command, including full hashes, without | // "git ls-remote" when given a remote. | ||||
| // using "--debug" or matching any human readable strings in the output. | |||||
| // NOTE: We can't get branches and bookmarks together in a single command | |||||
| // because if we query for "heads() + bookmark()", we can't tell if a | |||||
| // bookmarked result is a branch head or not. | |||||
| $template_fields = array( | |||||
| '{node}', | |||||
| '{branch}', | |||||
| '{join(bookmarks, "\3")}', | |||||
| '{activebookmark}', | |||||
| '{desc}', | |||||
| ); | |||||
| $expect_fields = count($template_fields); | |||||
| $template = implode('\2', $template_fields).'\1'; | $argv = array(); | ||||
| foreach ($api->getMercurialExtensionArguments() as $arg) { | |||||
| if ($is_bookmarks) { | $argv[] = $arg; | ||||
| $query = hgsprintf('bookmark()'); | |||||
| } else { | |||||
| $query = hgsprintf('head()'); | |||||
| } | } | ||||
| $argv[] = 'arc-ls-markers'; | |||||
| $future = $api->newFuture( | // NOTE: In remote mode, we're using passthru and a tempfile on this | ||||
| 'log --rev %s --template %s --', | // because it's a remote command and may prompt the user to provide | ||||
| $query, | // credentials interactively. In local mode, we can just read stdout. | ||||
| $template); | |||||
| list($lines) = $future->resolve(); | if ($remote !== null) { | ||||
| $tmpfile = new TempFile(); | |||||
| Filesystem::remove($tmpfile); | |||||
| $markers = array(); | $argv[] = '--output'; | ||||
| $argv[] = phutil_string_cast($tmpfile); | |||||
| } | |||||
| $lines = explode("\1", $lines); | $argv[] = '--'; | ||||
| foreach ($lines as $line) { | |||||
| if (!strlen(trim($line))) { | if ($remote !== null) { | ||||
| continue; | $argv[] = $remote->getRemoteName(); | ||||
| } | } | ||||
| $fields = explode("\2", $line, $expect_fields); | if ($remote !== null) { | ||||
| $actual_fields = count($fields); | $passthru = $api->newPassthru('%Ls', $argv); | ||||
| if ($actual_fields !== $expect_fields) { | |||||
| $err = $passthru->execute(); | |||||
| if ($err) { | |||||
| throw new Exception( | throw new Exception( | ||||
| pht( | pht( | ||||
| 'Unexpected number of fields in line "%s", expected %s but '. | 'Call to "hg arc-ls-markers" failed with error "%s".', | ||||
| 'found %s.', | $err)); | ||||
| $line, | |||||
| new PhutilNumber($expect_fields), | |||||
| new PhutilNumber($actual_fields))); | |||||
| } | } | ||||
| $node = $fields[0]; | $raw_data = Filesystem::readFile($tmpfile); | ||||
| unset($tmpfile); | |||||
| $branch = $fields[1]; | } else { | ||||
| if (!strlen($branch)) { | $future = $api->newFuture('%Ls', $argv); | ||||
| $branch = 'default'; | list($raw_data) = $future->resolve(); | ||||
| } | } | ||||
| if ($is_bookmarks) { | $items = phutil_json_decode($raw_data); | ||||
| $bookmarks = $fields[2]; | |||||
| if (strlen($bookmarks)) { | $markers = array(); | ||||
| $bookmarks = explode("\3", $fields[2]); | foreach ($items as $item) { | ||||
| } else { | if (!empty($item['isClosed'])) { | ||||
| $bookmarks = array(); | // NOTE: For now, we ignore closed branch heads. | ||||
| continue; | |||||
| } | } | ||||
| if (strlen($fields[3])) { | $node = $item['node']; | ||||
| $active_bookmark = $fields[3]; | if (!$node) { | ||||
| } else { | // NOTE: For now, we ignore the virtual "current branch" marker. | ||||
| $active_bookmark = null; | continue; | ||||
| } | } | ||||
| } else { | |||||
| $bookmarks = array(); | switch ($item['type']) { | ||||
| $active_bookmark = null; | case 'branch': | ||||
| $marker_type = ArcanistMarkerRef::TYPE_BRANCH; | |||||
| break; | |||||
| case 'bookmark': | |||||
| $marker_type = ArcanistMarkerRef::TYPE_BOOKMARK; | |||||
| break; | |||||
| case 'commit': | |||||
| $marker_type = null; | |||||
| break; | |||||
| default: | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Call to "hg arc-ls-markers" returned marker of unknown '. | |||||
| 'type "%s".', | |||||
| $item['type'])); | |||||
| } | } | ||||
| $message = $fields[4]; | if ($marker_type === null) { | ||||
| $message_lines = phutil_split_lines($message, false); | // NOTE: For now, we ignore the virtual "head" marker. | ||||
| continue; | |||||
| } | |||||
| $commit_ref = $api->newCommitRef() | $commit_ref = $api->newCommitRef() | ||||
| ->setCommitHash($node) | ->setCommitHash($node); | ||||
| ->attachMessage($message); | |||||
| $template = id(new ArcanistMarkerRef()) | $marker_ref = id(new ArcanistMarkerRef()) | ||||
| ->setName($item['name']) | |||||
| ->setCommitHash($node) | ->setCommitHash($node) | ||||
| ->setSummary(head($message_lines)) | |||||
| ->attachCommitRef($commit_ref); | ->attachCommitRef($commit_ref); | ||||
| if ($is_bookmarks) { | if (isset($item['description'])) { | ||||
| foreach ($bookmarks as $bookmark) { | $description = $item['description']; | ||||
| $is_active = ($bookmark === $active_bookmark); | $commit_ref->attachMessage($description); | ||||
| $markers[] = id(clone $template) | $description_lines = phutil_split_lines($description, false); | ||||
| ->setMarkerType(ArcanistMarkerRef::TYPE_BOOKMARK) | $marker_ref->setSummary(head($description_lines)); | ||||
| ->setName($bookmark) | |||||
| ->setIsActive($is_active); | |||||
| } | |||||
| } | } | ||||
| if ($is_branches) { | $marker_ref | ||||
| $markers[] = id(clone $template) | ->setMarkerType($marker_type) | ||||
| ->setMarkerType(ArcanistMarkerRef::TYPE_BRANCH) | ->setIsActive(!empty($item['isActive'])); | ||||
| ->setName($branch); | |||||
| } | |||||
| } | |||||
| if ($is_branches) { | $markers[] = $marker_ref; | ||||
| $current_hash = $api->getCanonicalRevisionName('.'); | |||||
| foreach ($markers as $marker) { | |||||
| if ($marker->getMarkerType() !== ArcanistMarkerRef::TYPE_BRANCH) { | |||||
| continue; | |||||
| } | |||||
| if ($marker->getCommitHash() === $current_hash) { | |||||
| $marker->setIsActive(true); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| return $markers; | return $markers; | ||||
| } | } | ||||
| } | } | ||||