Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F18433010
D21333.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
33 KB
Referenced Files
None
Subscribers
None
D21333.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sep 1 2025, 7:16 AM (6 w, 3 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/fk/c3/rkmozedqs34jbqlr
Default Alt Text
D21333.diff (33 KB)
Attached To
Mode
D21333: Provide a more powerful query mechanism for "markers" (branches/bookmarks)
Attached
Detach File
Event Timeline
Log In to Comment