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; | ||||
} | } | ||||
} | } |