Page MenuHomePhabricator

D21333.diff
No OneTemporary

D21333.diff

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
@@ -50,11 +50,12 @@
'ArcanistBlacklistedFunctionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBlacklistedFunctionXHPASTLinterRuleTestCase.php',
'ArcanistBlindlyTrustHTTPEngineExtension' => 'configuration/ArcanistBlindlyTrustHTTPEngineExtension.php',
'ArcanistBookmarkWorkflow' => 'workflow/ArcanistBookmarkWorkflow.php',
+ 'ArcanistBookmarksWorkflow' => 'workflow/ArcanistBookmarksWorkflow.php',
'ArcanistBoolConfigOption' => 'config/option/ArcanistBoolConfigOption.php',
'ArcanistBraceFormattingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistBraceFormattingXHPASTLinterRule.php',
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBraceFormattingXHPASTLinterRuleTestCase.php',
- 'ArcanistBranchRef' => 'ref/ArcanistBranchRef.php',
'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php',
+ 'ArcanistBranchesWorkflow' => 'workflow/ArcanistBranchesWorkflow.php',
'ArcanistBrowseCommitHardpointQuery' => 'browse/query/ArcanistBrowseCommitHardpointQuery.php',
'ArcanistBrowseCommitURIHardpointQuery' => 'browse/query/ArcanistBrowseCommitURIHardpointQuery.php',
'ArcanistBrowseObjectNameURIHardpointQuery' => 'browse/query/ArcanistBrowseObjectNameURIHardpointQuery.php',
@@ -222,6 +223,7 @@
'ArcanistGitLocalState' => 'repository/state/ArcanistGitLocalState.php',
'ArcanistGitRawCommit' => 'repository/raw/ArcanistGitRawCommit.php',
'ArcanistGitRawCommitTestCase' => 'repository/raw/__tests__/ArcanistGitRawCommitTestCase.php',
+ 'ArcanistGitRepositoryMarkerQuery' => 'repository/marker/ArcanistGitRepositoryMarkerQuery.php',
'ArcanistGitUpstreamPath' => 'repository/api/ArcanistGitUpstreamPath.php',
'ArcanistGitWorkingCopy' => 'workingcopy/ArcanistGitWorkingCopy.php',
'ArcanistGitWorkingCopyRevisionHardpointQuery' => 'query/ArcanistGitWorkingCopyRevisionHardpointQuery.php',
@@ -325,12 +327,15 @@
'ArcanistLogicalOperatorsXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistLogicalOperatorsXHPASTLinterRuleTestCase.php',
'ArcanistLowercaseFunctionsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistLowercaseFunctionsXHPASTLinterRule.php',
'ArcanistLowercaseFunctionsXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistLowercaseFunctionsXHPASTLinterRuleTestCase.php',
+ 'ArcanistMarkerRef' => 'repository/marker/ArcanistMarkerRef.php',
+ 'ArcanistMarkersWorkflow' => 'workflow/ArcanistMarkersWorkflow.php',
'ArcanistMercurialAPI' => 'repository/api/ArcanistMercurialAPI.php',
'ArcanistMercurialCommitMessageHardpointQuery' => 'query/ArcanistMercurialCommitMessageHardpointQuery.php',
'ArcanistMercurialLandEngine' => 'land/engine/ArcanistMercurialLandEngine.php',
'ArcanistMercurialLocalState' => 'repository/state/ArcanistMercurialLocalState.php',
'ArcanistMercurialParser' => 'repository/parser/ArcanistMercurialParser.php',
'ArcanistMercurialParserTestCase' => 'repository/parser/__tests__/ArcanistMercurialParserTestCase.php',
+ 'ArcanistMercurialRepositoryMarkerQuery' => 'repository/marker/ArcanistMercurialRepositoryMarkerQuery.php',
'ArcanistMercurialWorkingCopy' => 'workingcopy/ArcanistMercurialWorkingCopy.php',
'ArcanistMercurialWorkingCopyRevisionHardpointQuery' => 'query/ArcanistMercurialWorkingCopyRevisionHardpointQuery.php',
'ArcanistMergeConflictLinter' => 'lint/linter/ArcanistMergeConflictLinter.php',
@@ -415,6 +420,7 @@
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
'ArcanistRepositoryLocalState' => 'repository/state/ArcanistRepositoryLocalState.php',
+ 'ArcanistRepositoryMarkerQuery' => 'repository/marker/ArcanistRepositoryMarkerQuery.php',
'ArcanistRepositoryRef' => 'ref/ArcanistRepositoryRef.php',
'ArcanistReusedAsIteratorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistReusedAsIteratorXHPASTLinterRule.php',
'ArcanistReusedAsIteratorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistReusedAsIteratorXHPASTLinterRuleTestCase.php',
@@ -538,7 +544,6 @@
'ArcanistWorkflowInformation' => 'toolset/ArcanistWorkflowInformation.php',
'ArcanistWorkflowMercurialHardpointQuery' => 'query/ArcanistWorkflowMercurialHardpointQuery.php',
'ArcanistWorkingCopy' => 'workingcopy/ArcanistWorkingCopy.php',
- 'ArcanistWorkingCopyCommitHardpointQuery' => 'query/ArcanistWorkingCopyCommitHardpointQuery.php',
'ArcanistWorkingCopyConfigurationSource' => 'config/source/ArcanistWorkingCopyConfigurationSource.php',
'ArcanistWorkingCopyIdentity' => 'workingcopyidentity/ArcanistWorkingCopyIdentity.php',
'ArcanistWorkingCopyPath' => 'workingcopy/ArcanistWorkingCopyPath.php',
@@ -1059,11 +1064,12 @@
'ArcanistBlacklistedFunctionXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistBlindlyTrustHTTPEngineExtension' => 'PhutilHTTPEngineExtension',
'ArcanistBookmarkWorkflow' => 'ArcanistFeatureBaseWorkflow',
+ 'ArcanistBookmarksWorkflow' => 'ArcanistMarkersWorkflow',
'ArcanistBoolConfigOption' => 'ArcanistSingleSourceConfigOption',
'ArcanistBraceFormattingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
- 'ArcanistBranchRef' => 'ArcanistRef',
'ArcanistBranchWorkflow' => 'ArcanistFeatureBaseWorkflow',
+ 'ArcanistBranchesWorkflow' => 'ArcanistMarkersWorkflow',
'ArcanistBrowseCommitHardpointQuery' => 'ArcanistRuntimeHardpointQuery',
'ArcanistBrowseCommitURIHardpointQuery' => 'ArcanistBrowseURIHardpointQuery',
'ArcanistBrowseObjectNameURIHardpointQuery' => 'ArcanistBrowseURIHardpointQuery',
@@ -1245,6 +1251,7 @@
'ArcanistGitLocalState' => 'ArcanistRepositoryLocalState',
'ArcanistGitRawCommit' => 'Phobject',
'ArcanistGitRawCommitTestCase' => 'PhutilTestCase',
+ 'ArcanistGitRepositoryMarkerQuery' => 'ArcanistRepositoryMarkerQuery',
'ArcanistGitUpstreamPath' => 'Phobject',
'ArcanistGitWorkingCopy' => 'ArcanistWorkingCopy',
'ArcanistGitWorkingCopyRevisionHardpointQuery' => 'ArcanistWorkflowGitHardpointQuery',
@@ -1348,12 +1355,15 @@
'ArcanistLogicalOperatorsXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistLowercaseFunctionsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistLowercaseFunctionsXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
+ 'ArcanistMarkerRef' => 'ArcanistRef',
+ 'ArcanistMarkersWorkflow' => 'ArcanistArcWorkflow',
'ArcanistMercurialAPI' => 'ArcanistRepositoryAPI',
'ArcanistMercurialCommitMessageHardpointQuery' => 'ArcanistWorkflowMercurialHardpointQuery',
'ArcanistMercurialLandEngine' => 'ArcanistLandEngine',
'ArcanistMercurialLocalState' => 'ArcanistRepositoryLocalState',
'ArcanistMercurialParser' => 'Phobject',
'ArcanistMercurialParserTestCase' => 'PhutilTestCase',
+ 'ArcanistMercurialRepositoryMarkerQuery' => 'ArcanistRepositoryMarkerQuery',
'ArcanistMercurialWorkingCopy' => 'ArcanistWorkingCopy',
'ArcanistMercurialWorkingCopyRevisionHardpointQuery' => 'ArcanistWorkflowMercurialHardpointQuery',
'ArcanistMergeConflictLinter' => 'ArcanistLinter',
@@ -1441,6 +1451,7 @@
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
'ArcanistRepositoryLocalState' => 'Phobject',
+ 'ArcanistRepositoryMarkerQuery' => 'Phobject',
'ArcanistRepositoryRef' => 'ArcanistRef',
'ArcanistReusedAsIteratorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistReusedAsIteratorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
@@ -1571,7 +1582,6 @@
'ArcanistWorkflowInformation' => 'Phobject',
'ArcanistWorkflowMercurialHardpointQuery' => 'ArcanistRuntimeHardpointQuery',
'ArcanistWorkingCopy' => 'Phobject',
- 'ArcanistWorkingCopyCommitHardpointQuery' => 'ArcanistRuntimeHardpointQuery',
'ArcanistWorkingCopyConfigurationSource' => 'ArcanistFilesystemConfigurationSource',
'ArcanistWorkingCopyIdentity' => 'Phobject',
'ArcanistWorkingCopyPath' => 'Phobject',
diff --git a/src/console/view/PhutilConsoleTable.php b/src/console/view/PhutilConsoleTable.php
--- a/src/console/view/PhutilConsoleTable.php
+++ b/src/console/view/PhutilConsoleTable.php
@@ -57,9 +57,9 @@
/* -( Data )--------------------------------------------------------------- */
- public function addColumn($key, array $column) {
+ public function addColumn($key, array $column = array()) {
PhutilTypeSpec::checkMap($column, array(
- 'title' => 'string',
+ 'title' => 'optional string',
'align' => 'optional string',
));
$this->columns[$key] = $column;
@@ -85,6 +85,16 @@
return $this;
}
+ public function drawRows(array $rows) {
+ $this->data = array();
+ $this->widths = array();
+
+ foreach ($rows as $row) {
+ $this->addRow($row);
+ }
+
+ return $this->draw();
+ }
/* -( Drawing )------------------------------------------------------------ */
diff --git a/src/query/ArcanistWorkingCopyCommitHardpointQuery.php b/src/query/ArcanistWorkingCopyCommitHardpointQuery.php
deleted file mode 100644
--- a/src/query/ArcanistWorkingCopyCommitHardpointQuery.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-final class ArcanistWorkingCopyCommitHardpointQuery
- extends ArcanistRuntimeHardpointQuery {
-
- public function getHardpoints() {
- return array(
- ArcanistWorkingCopyStateRef::HARDPOINT_COMMITREF,
- );
- }
-
- protected function canLoadRef(ArcanistRef $ref) {
- return ($ref instanceof ArcanistWorkingCopyStateRef);
- }
-
- public function loadHardpoint(array $refs, $hardpoint) {
- yield $this->yieldRequests(
- $refs,
- array(
- ArcanistWorkingCopyStateRef::HARDPOINT_BRANCHREF,
- ));
-
- $branch_refs = mpull($refs, 'getBranchRef');
-
- yield $this->yieldRequests(
- $branch_refs,
- array(
- ArcanistBranchRef::HARDPOINT_COMMITREF,
- ));
-
- $results = array();
- foreach ($refs as $key => $ref) {
- $results[$key] = $ref->getBranchRef()->getCommitRef();
- }
-
- yield $this->yieldMap($results);
- }
-
-}
diff --git a/src/ref/ArcanistBranchRef.php b/src/ref/ArcanistBranchRef.php
deleted file mode 100644
--- a/src/ref/ArcanistBranchRef.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-final class ArcanistBranchRef
- extends ArcanistRef {
-
- const HARDPOINT_COMMITREF = 'commitRef';
-
- private $branchName;
- private $refName;
- private $isCurrentBranch;
-
- public function getRefDisplayName() {
- return pht('Branch %s', $this->getBranchName());
- }
-
- protected function newHardpoints() {
- return array(
- $this->newHardpoint(self::HARDPOINT_COMMITREF),
- );
- }
-
- public function setBranchName($branch_name) {
- $this->branchName = $branch_name;
- return $this;
- }
-
- public function getBranchName() {
- return $this->branchName;
- }
-
- public function setRefName($ref_name) {
- $this->refName = $ref_name;
- return $this;
- }
-
- public function getRefName() {
- return $this->refName;
- }
-
- public function setIsCurrentBranch($is_current_branch) {
- $this->isCurrentBranch = $is_current_branch;
- return $this;
- }
-
- public function getIsCurrentBranch() {
- return $this->isCurrentBranch;
- }
-
- public function attachCommitRef(ArcanistCommitRef $ref) {
- return $this->attachHardpoint(self::HARDPOINT_COMMITREF, $ref);
- }
-
- public function getCommitRef() {
- return $this->getHardpoint(self::HARDPOINT_COMMITREF);
- }
-
-}
diff --git a/src/ref/revision/ArcanistRevisionRef.php b/src/ref/revision/ArcanistRevisionRef.php
--- a/src/ref/revision/ArcanistRevisionRef.php
+++ b/src/ref/revision/ArcanistRevisionRef.php
@@ -72,6 +72,10 @@
return idxv($this->parameters, array('fields', 'status', 'name'));
}
+ public function getStatusANSIColor() {
+ return idxv($this->parameters, array('fields', 'status', 'color.ansi'));
+ }
+
public function isStatusChangesPlanned() {
$status = $this->getStatus();
return ($status === 'changes-planned');
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
@@ -1113,10 +1113,9 @@
->setCommitEpoch($branch['epoch'])
->attachMessage($branch['text']);
- $refs[] = $this->newBranchRef()
- ->setBranchName($branch['name'])
- ->setRefName($branch['ref'])
- ->setIsCurrentBranch($branch['current'])
+ $refs[] = $this->newMarkerRef()
+ ->setName($branch['name'])
+ ->setIsActive($branch['current'])
->attachCommitRef($commit_ref);
}
@@ -1770,4 +1769,15 @@
return trim($stdout);
}
+ protected function newSupportedMarkerTypes() {
+ return array(
+ ArcanistMarkerRef::TYPE_BRANCH,
+ );
+ }
+
+ protected function newMarkerRefQueryTemplate() {
+ return new ArcanistGitRepositoryMarkerQuery();
+ }
+
+
}
diff --git a/src/repository/api/ArcanistMercurialAPI.php b/src/repository/api/ArcanistMercurialAPI.php
--- a/src/repository/api/ArcanistMercurialAPI.php
+++ b/src/repository/api/ArcanistMercurialAPI.php
@@ -542,7 +542,7 @@
$commit_ref = $this->newCommitRef()
->setCommitHash($branch['hash']);
- $refs[] = $this->newBranchRef()
+ $refs[] = $this->newMarkerRef()
->setBranchName($branch['name'])
->setIsCurrentBranch($branch['current'])
->attachCommitRef($commit_ref);
@@ -1190,5 +1190,15 @@
return !$err;
}
+ protected function newSupportedMarkerTypes() {
+ return array(
+ ArcanistMarkerRef::TYPE_BRANCH,
+ ArcanistMarkerRef::TYPE_BOOKMARK,
+ );
+ }
+
+ protected function newMarkerRefQueryTemplate() {
+ return new ArcanistMercurialRepositoryMarkerQuery();
+ }
}
diff --git a/src/repository/api/ArcanistRepositoryAPI.php b/src/repository/api/ArcanistRepositoryAPI.php
--- a/src/repository/api/ArcanistRepositoryAPI.php
+++ b/src/repository/api/ArcanistRepositoryAPI.php
@@ -745,8 +745,8 @@
return new ArcanistCommitRef();
}
- final public function newBranchRef() {
- return new ArcanistBranchRef();
+ final public function newMarkerRef() {
+ return new ArcanistMarkerRef();
}
final public function getLandEngine() {
@@ -763,4 +763,21 @@
return null;
}
+ final public function getSupportedMarkerTypes() {
+ return $this->newSupportedMarkerTypes();
+ }
+
+ protected function newSupportedMarkerTypes() {
+ return array();
+ }
+
+ final public function newMarkerRefQuery() {
+ return id($this->newMarkerRefQueryTemplate())
+ ->setRepositoryAPI($this);
+ }
+
+ protected function newMarkerRefQueryTemplate() {
+ throw new PhutilMethodNotImplementedException();
+ }
+
}
diff --git a/src/repository/marker/ArcanistGitRepositoryMarkerQuery.php b/src/repository/marker/ArcanistGitRepositoryMarkerQuery.php
new file mode 100644
--- /dev/null
+++ b/src/repository/marker/ArcanistGitRepositoryMarkerQuery.php
@@ -0,0 +1,125 @@
+<?php
+
+final class ArcanistGitRepositoryMarkerQuery
+ extends ArcanistRepositoryMarkerQuery {
+
+
+ protected function newRefMarkers() {
+ $api = $this->getRepositoryAPI();
+
+ $future = $this->newCurrentBranchNameFuture()->start();
+
+ $field_list = array(
+ '%(refname)',
+ '%(objectname)',
+ '%(committerdate:raw)',
+ '%(tree)',
+ '%(*objectname)',
+ '%(subject)',
+ '%(subject)%0a%0a%(body)',
+ '%02',
+ );
+ $expect_count = count($field_list);
+
+ $branch_prefix = 'refs/heads/';
+ $branch_length = strlen($branch_prefix);
+
+ // NOTE: Since we only return branches today, we restrict this operation
+ // to branches.
+
+ list($stdout) = $api->newFuture(
+ 'for-each-ref --format %s -- refs/heads/',
+ implode('%01', $field_list))->resolve();
+
+ $markers = array();
+
+ $lines = explode("\2", $stdout);
+ foreach ($lines as $line) {
+ $line = trim($line);
+ if (!strlen($line)) {
+ continue;
+ }
+
+ $fields = explode("\1", $line, $expect_count);
+ $actual_count = count($fields);
+ if ($actual_count !== $expect_count) {
+ throw new Exception(
+ pht(
+ 'Unexpected field count when parsing line "%s", got %s but '.
+ 'expected %s.',
+ $line,
+ new PhutilNumber($actual_count),
+ new PhutilNumber($expect_count)));
+ }
+
+ list($ref, $hash, $epoch, $tree, $dst_hash, $summary, $text) = $fields;
+
+ if (!strncmp($ref, $branch_prefix, $branch_length)) {
+ $type = ArcanistMarkerRef::TYPE_BRANCH;
+ $name = substr($ref, $branch_length);
+ } else {
+ // For now, discard other refs.
+ continue;
+ }
+
+ $marker = id(new ArcanistMarkerRef())
+ ->setName($name)
+ ->setMarkerType($type)
+ ->setEpoch((int)$epoch)
+ ->setMarkerHash($hash)
+ ->setTreeHash($tree)
+ ->setSummary($summary)
+ ->setMessage($text);
+
+ if (strlen($dst_hash)) {
+ $commit_hash = $dst_hash;
+ } else {
+ $commit_hash = $hash;
+ }
+
+ $marker->setCommitHash($commit_hash);
+
+ $commit_ref = $api->newCommitRef()
+ ->setCommitHash($commit_hash)
+ ->attachMessage($text);
+
+ $marker->attachCommitRef($commit_ref);
+
+ $markers[] = $marker;
+ }
+
+ $current = $this->resolveCurrentBranchNameFuture($future);
+
+ if ($current !== null) {
+ foreach ($markers as $marker) {
+ if ($marker->getName() === $current) {
+ $marker->setIsActive(true);
+ }
+ }
+ }
+
+ return $markers;
+ }
+
+ private function newCurrentBranchNameFuture() {
+ $api = $this->getRepositoryAPI();
+ return $api->newFuture('symbolic-ref --quiet HEAD --')
+ ->setResolveOnError(true);
+ }
+
+ private function resolveCurrentBranchNameFuture($future) {
+ list($err, $stdout) = $future->resolve();
+
+ if ($err) {
+ return null;
+ }
+
+ $matches = null;
+ if (!preg_match('(^refs/heads/(.*)\z)', trim($stdout), $matches)) {
+ return null;
+ }
+
+ return $matches[1];
+ }
+
+}
diff --git a/src/repository/marker/ArcanistMarkerRef.php b/src/repository/marker/ArcanistMarkerRef.php
new file mode 100644
--- /dev/null
+++ b/src/repository/marker/ArcanistMarkerRef.php
@@ -0,0 +1,120 @@
+<?php
+
+final class ArcanistMarkerRef
+ extends ArcanistRef {
+
+ const HARDPOINT_COMMITREF = 'commitRef';
+
+ const TYPE_BRANCH = 'branch';
+ const TYPE_BOOKMARK = 'bookmark';
+
+ private $name;
+ private $markerType;
+ private $epoch;
+ private $markerHash;
+ private $commitHash;
+ private $treeHash;
+ private $summary;
+ private $message;
+ private $isActive = false;
+
+ public function getRefDisplayName() {
+ return pht('Marker %s', $this->getName());
+ }
+
+ protected function newHardpoints() {
+ return array(
+ $this->newHardpoint(self::HARDPOINT_COMMITREF),
+ );
+ }
+
+ public function setName($name) {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getName() {
+ return $this->name;
+ }
+
+ public function setMarkerType($marker_type) {
+ $this->markerType = $marker_type;
+ return $this;
+ }
+
+ public function getMarkerType() {
+ return $this->markerType;
+ }
+
+ public function setEpoch($epoch) {
+ $this->epoch = $epoch;
+ return $this;
+ }
+
+ public function getEpoch() {
+ return $this->epoch;
+ }
+
+ public function setMarkerHash($marker_hash) {
+ $this->markerHash = $marker_hash;
+ return $this;
+ }
+
+ public function getMarkerHash() {
+ return $this->markerHash;
+ }
+
+ public function setCommitHash($commit_hash) {
+ $this->commitHash = $commit_hash;
+ return $this;
+ }
+
+ public function getCommitHash() {
+ return $this->commitHash;
+ }
+
+ public function setTreeHash($tree_hash) {
+ $this->treeHash = $tree_hash;
+ return $this;
+ }
+
+ public function getTreeHash() {
+ return $this->treeHash;
+ }
+
+ public function setSummary($summary) {
+ $this->summary = $summary;
+ return $this;
+ }
+
+ public function getSummary() {
+ return $this->summary;
+ }
+
+ public function setMessage($message) {
+ $this->message = $message;
+ return $this;
+ }
+
+ public function getMessage() {
+ return $this->message;
+ }
+
+ public function setIsActive($is_active) {
+ $this->isActive = $is_active;
+ return $this;
+ }
+
+ public function getIsActive() {
+ return $this->isActive;
+ }
+
+ public function attachCommitRef(ArcanistCommitRef $ref) {
+ return $this->attachHardpoint(self::HARDPOINT_COMMITREF, $ref);
+ }
+
+ public function getCommitRef() {
+ return $this->getHardpoint(self::HARDPOINT_COMMITREF);
+ }
+
+}
diff --git a/src/repository/marker/ArcanistMercurialRepositoryMarkerQuery.php b/src/repository/marker/ArcanistMercurialRepositoryMarkerQuery.php
new file mode 100644
--- /dev/null
+++ b/src/repository/marker/ArcanistMercurialRepositoryMarkerQuery.php
@@ -0,0 +1,147 @@
+<?php
+
+final class ArcanistMercurialRepositoryMarkerQuery
+ extends ArcanistRepositoryMarkerQuery {
+
+ protected function newRefMarkers() {
+ $markers = array();
+
+ if ($this->shouldQueryMarkerType(ArcanistMarkerRef::TYPE_BRANCH)) {
+ $markers[] = $this->newBranchOrBookmarkMarkers(false);
+ }
+
+ if ($this->shouldQueryMarkerType(ArcanistMarkerRef::TYPE_BOOKMARK)) {
+ $markers[] = $this->newBranchOrBookmarkMarkers(true);
+ }
+
+ return array_mergev($markers);
+ }
+
+ private function newBranchOrBookmarkMarkers($is_bookmarks) {
+ $api = $this->getRepositoryAPI();
+
+ $is_branches = !$is_bookmarks;
+
+ // NOTE: This is a bit clumsy, but it allows us to get most bookmark and
+ // branch information in a single command, including full hashes, without
+ // 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';
+
+ if ($is_bookmarks) {
+ $query = hgsprintf('bookmark()');
+ } else {
+ $query = hgsprintf('head()');
+ }
+
+ $future = $api->newFuture(
+ 'log --rev %s --template %s --',
+ $query,
+ $template);
+
+ list($lines) = $future->resolve();
+
+ $markers = array();
+
+ $lines = explode("\1", $lines);
+ foreach ($lines as $line) {
+ if (!strlen(trim($line))) {
+ continue;
+ }
+
+ $fields = explode("\2", $line, $expect_fields);
+ $actual_fields = count($fields);
+ if ($actual_fields !== $expect_fields) {
+ throw new Exception(
+ pht(
+ 'Unexpected number of fields in line "%s", expected %s but '.
+ 'found %s.',
+ $line,
+ new PhutilNumber($expect_fields),
+ new PhutilNumber($actual_fields)));
+ }
+
+ $node = $fields[0];
+
+ $branch = $fields[1];
+ if (!strlen($branch)) {
+ $branch = 'default';
+ }
+
+ if ($is_bookmarks) {
+ $bookmarks = $fields[2];
+ if (strlen($bookmarks)) {
+ $bookmarks = explode("\3", $fields[2]);
+ } else {
+ $bookmarks = array();
+ }
+
+ if (strlen($fields[3])) {
+ $active_bookmark = $fields[3];
+ } else {
+ $active_bookmark = null;
+ }
+ } else {
+ $bookmarks = array();
+ $active_bookmark = null;
+ }
+
+ $message = $fields[4];
+
+ $commit_ref = $api->newCommitRef()
+ ->setCommitHash($node)
+ ->attachMessage($message);
+
+ $template = id(new ArcanistMarkerRef())
+ ->setCommitHash($node)
+ ->attachCommitRef($commit_ref);
+
+ if ($is_bookmarks) {
+ foreach ($bookmarks as $bookmark) {
+ $is_active = ($bookmark === $active_bookmark);
+
+ $markers[] = id(clone $template)
+ ->setMarkerType(ArcanistMarkerRef::TYPE_BOOKMARK)
+ ->setName($bookmark)
+ ->setIsActive($is_active);
+ }
+ }
+
+ if ($is_branches) {
+ $markers[] = id(clone $template)
+ ->setMarkerType(ArcanistMarkerRef::TYPE_BRANCH)
+ ->setName($branch);
+ }
+ }
+
+ if ($is_branches) {
+ $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;
+ }
+
+}
diff --git a/src/repository/marker/ArcanistRepositoryMarkerQuery.php b/src/repository/marker/ArcanistRepositoryMarkerQuery.php
new file mode 100644
--- /dev/null
+++ b/src/repository/marker/ArcanistRepositoryMarkerQuery.php
@@ -0,0 +1,63 @@
+<?php
+
+abstract class ArcanistRepositoryMarkerQuery
+ extends Phobject {
+
+ private $repositoryAPI;
+ private $types;
+ private $commitHashes;
+ private $ancestorCommitHashes;
+
+ final public function setRepositoryAPI(ArcanistRepositoryAPI $api) {
+ $this->repositoryAPI = $api;
+ return $this;
+ }
+
+ final public function getRepositoryAPI() {
+ return $this->repositoryAPI;
+ }
+
+ final public function withTypes(array $types) {
+ $this->types = array_fuse($types);
+ return $this;
+ }
+
+ final public function execute() {
+ $markers = $this->newRefMarkers();
+
+ $types = $this->types;
+ if ($types !== null) {
+ foreach ($markers as $key => $marker) {
+ if (!isset($types[$marker->getMarkerType()])) {
+ unset($markers[$key]);
+ }
+ }
+ }
+
+ return $this->sortMarkers($markers);
+ }
+
+ private function sortMarkers(array $markers) {
+ // Sort the list in natural order. If we apply a stable sort later,
+ // markers will sort in "feature1", "feature2", etc., order if they
+ // don't otherwise have a unique position.
+
+ // This can improve behavior if two branches were updated at the same
+ // time, as is common when cascading rebases after changes land.
+
+ $map = mpull($markers, 'getName');
+ natcasesort($map);
+ $markers = array_select_keys($markers, array_keys($map));
+
+ return $markers;
+ }
+
+ final protected function shouldQueryMarkerType($marker_type) {
+ if ($this->types === null) {
+ return true;
+ }
+
+ return isset($this->types[$marker_type]);
+ }
+
+}
diff --git a/src/workflow/ArcanistBookmarksWorkflow.php b/src/workflow/ArcanistBookmarksWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/workflow/ArcanistBookmarksWorkflow.php
@@ -0,0 +1,43 @@
+<?php
+
+final class ArcanistBookmarksWorkflow
+ extends ArcanistMarkersWorkflow {
+
+ public function getWorkflowName() {
+ return 'bookmarks';
+ }
+
+ public function getWorkflowArguments() {
+ return array();
+ }
+
+ public function getWorkflowInformation() {
+ $help = pht(<<<EOHELP
+Lists bookmarks in the working copy, annotated with additional information
+about review status.
+EOHELP
+ );
+
+ return $this->newWorkflowInformation()
+ ->setSynopsis(
+ pht('Show an enhanced view of bookmarks in the working copy.'))
+ ->addExample(pht('**bookmarks**'))
+ ->setHelp($help);
+ }
+
+ protected function getWorkflowMarkerType() {
+ $api = $this->getRepositoryAPI();
+ $marker_type = ArcanistMarkerRef::TYPE_BOOKMARK;
+
+ if (!$this->hasMarkerTypeSupport($marker_type)) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'The version control system ("%s") in the current working copy '.
+ 'does not support bookmarks.',
+ $api->getSourceControlSystemName()));
+ }
+
+ return $marker_type;
+ }
+
+}
diff --git a/src/workflow/ArcanistBranchesWorkflow.php b/src/workflow/ArcanistBranchesWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/workflow/ArcanistBranchesWorkflow.php
@@ -0,0 +1,43 @@
+<?php
+
+final class ArcanistBranchesWorkflow
+ extends ArcanistMarkersWorkflow {
+
+ public function getWorkflowName() {
+ return 'branches';
+ }
+
+ public function getWorkflowArguments() {
+ return array();
+ }
+
+ public function getWorkflowInformation() {
+ $help = pht(<<<EOHELP
+Lists branches in the working copy, annotated with additional information
+about review status.
+EOHELP
+ );
+
+ return $this->newWorkflowInformation()
+ ->setSynopsis(
+ pht('Show an enhanced view of branches in the working copy.'))
+ ->addExample(pht('**branches**'))
+ ->setHelp($help);
+ }
+
+ protected function getWorkflowMarkerType() {
+ $api = $this->getRepositoryAPI();
+ $marker_type = ArcanistMarkerRef::TYPE_BRANCH;
+
+ if (!$this->hasMarkerTypeSupport($marker_type)) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'The version control system ("%s") in the current working copy '.
+ 'does not support branches.',
+ $api->getSourceControlSystemName()));
+ }
+
+ return $marker_type;
+ }
+
+}
diff --git a/src/workflow/ArcanistFeatureBaseWorkflow.php b/src/workflow/ArcanistFeatureBaseWorkflow.php
--- a/src/workflow/ArcanistFeatureBaseWorkflow.php
+++ b/src/workflow/ArcanistFeatureBaseWorkflow.php
@@ -191,7 +191,7 @@
}
}
- if (!$this->getArgument('view-all') && !$branch->getIsCurrentBranch()) {
+ if (!$this->getArgument('view-all') && !$branch->getIsActive()) {
if ($status == 'Closed' || $status == 'Abandoned') {
continue;
}
@@ -216,8 +216,8 @@
}
$out[] = array(
- 'name' => $branch->getBranchName(),
- 'current' => $branch->getIsCurrentBranch(),
+ 'name' => $branch->getName(),
+ 'current' => $branch->getIsActive(),
'status' => $status,
'desc' => $desc,
'revision' => $revision ? $revision->getID() : null,
diff --git a/src/workflow/ArcanistMarkersWorkflow.php b/src/workflow/ArcanistMarkersWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/workflow/ArcanistMarkersWorkflow.php
@@ -0,0 +1,118 @@
+<?php
+
+abstract class ArcanistMarkersWorkflow
+ extends ArcanistArcWorkflow {
+
+ abstract protected function getWorkflowMarkerType();
+
+ public function runWorkflow() {
+ $api = $this->getRepositoryAPI();
+
+ $marker_type = $this->getWorkflowMarkerType();
+
+ $markers = $api->newMarkerRefQuery()
+ ->withTypes(array($marker_type))
+ ->execute();
+
+ $states = array();
+ foreach ($markers as $marker) {
+ $state_ref = id(new ArcanistWorkingCopyStateRef())
+ ->setCommitRef($marker->getCommitRef());
+
+ $states[] = array(
+ 'marker' => $marker,
+ 'state' => $state_ref,
+ );
+ }
+
+ $this->loadHardpoints(
+ ipull($states, 'state'),
+ ArcanistWorkingCopyStateRef::HARDPOINT_REVISIONREFS);
+
+ $vectors = array();
+ foreach ($states as $key => $state) {
+ $marker_ref = $state['marker'];
+ $state_ref = $state['state'];
+
+ $vector = id(new PhutilSortVector())
+ ->addInt($marker_ref->getIsActive() ? 1 : 0)
+ ->addInt($marker_ref->getEpoch());
+
+ $vectors[$key] = $vector;
+ }
+
+ $vectors = msortv($vectors, 'getSelf');
+ $states = array_select_keys($states, array_keys($vectors));
+
+ $table = id(new PhutilConsoleTable())
+ ->setShowHeader(false)
+ ->addColumn('active')
+ ->addColumn('name')
+ ->addColumn('status')
+ ->addColumn('description');
+
+ $rows = array();
+ foreach ($states as $state) {
+ $marker_ref = $state['marker'];
+ $state_ref = $state['state'];
+ $revision_ref = null;
+ $commit_ref = $marker_ref->getCommitRef();
+
+ $marker_name = tsprintf('**%s**', $marker_ref->getName());
+
+ if ($state_ref->hasAmbiguousRevisionRefs()) {
+ $status = pht('Ambiguous');
+ } else {
+ $revision_ref = $state_ref->getRevisionRef();
+ if (!$revision_ref) {
+ $status = tsprintf(
+ '<fg:blue>%s</fg>',
+ pht('No Revision'));
+ } else {
+ $status = $revision_ref->getStatusDisplayName();
+
+ $ansi_color = $revision_ref->getStatusANSIColor();
+ if ($ansi_color) {
+ $status = tsprintf(
+ sprintf('<fg:%s>%%s</fg>', $ansi_color),
+ $status);
+ }
+ }
+ }
+
+ if ($revision_ref) {
+ $description = $revision_ref->getFullName();
+ } else {
+ $description = $commit_ref->getSummary();
+ }
+
+ if ($marker_ref->getIsActive()) {
+ $active_mark = '*';
+ } else {
+ $active_mark = ' ';
+ }
+ $is_active = tsprintf('** %s **', $active_mark);
+
+ $rows[] = array(
+ 'active' => $is_active,
+ 'name' => $marker_name,
+ 'status' => $status,
+ 'description' => $description,
+ );
+ }
+
+ $table->drawRows($rows);
+
+ return 0;
+ }
+
+ final protected function hasMarkerTypeSupport($marker_type) {
+ $api = $this->getRepositoryAPI();
+
+ $types = $api->getSupportedMarkerTypes();
+ $types = array_fuse($types);
+
+ return isset($types[$marker_type]);
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Mar 7, 2:07 PM (2 w, 11 h ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/p6/fs/gzc4dai3cwpr6ilb
Default Alt Text
D21333.diff (33 KB)

Event Timeline