1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-22 06:42:41 +01:00

Identify published commits in working copies by using remote configuration

Summary:
Ref T13546. When running "arc branches", we want to show all unpublished commits. This is often a different set of commits than "commits not present in any remote".

Attempt to identify published commits by using the permanent ref rules in Phabricator.

Test Plan: Ran "arc look published", saw sensible published commits in Git.

Maniphest Tasks: T13546

Differential Revision: https://secure.phabricator.com/D21378
This commit is contained in:
epriestley 2020-06-30 14:49:06 -07:00
parent 50f7a853b5
commit 80f5166b70
8 changed files with 185 additions and 3 deletions

View file

@ -108,4 +108,39 @@ final class ArcanistRepositoryRef
return $branch; return $branch;
} }
public function isPermanentRef(ArcanistMarkerRef $ref) {
$rules = idxv(
$this->parameters,
array('fields', 'refRules', 'permanentRefRules'));
if ($rules === null) {
return false;
}
// If the rules exist but there are no specified rules, treat every ref
// as permanent.
if (!$rules) {
return true;
}
// TODO: It would be nice to unify evaluation of permanent ref rules
// across Arcanist and Phabricator.
$ref_name = $ref->getName();
foreach ($rules as $rule) {
$matches = null;
if (preg_match('(^regexp\\((.*)\\)\z)', $rule, $matches)) {
if (preg_match($matches[1], $ref_name)) {
return true;
}
} else {
if ($rule === $ref_name) {
return true;
}
}
}
return false;
}
} }

View file

@ -1773,4 +1773,47 @@ final class ArcanistGitAPI extends ArcanistRepositoryAPI {
$uri); $uri);
} }
protected function newPublishedCommitHashes() {
$remotes = $this->newRemoteRefQuery()
->execute();
if (!$remotes) {
return array();
}
$markers = $this->newMarkerRefQuery()
->withIsRemoteCache(true)
->execute();
if (!$markers) {
return array();
}
$runtime = $this->getRuntime();
$workflow = $runtime->getCurrentWorkflow();
$workflow->loadHardpoints(
$remotes,
ArcanistRemoteRef::HARDPOINT_REPOSITORYREFS);
$remotes = mpull($remotes, null, 'getRemoteName');
$hashes = array();
foreach ($markers as $marker) {
$remote_name = $marker->getRemoteName();
$remote = idx($remotes, $remote_name);
if (!$remote) {
continue;
}
if (!$remote->isPermanentRef($marker)) {
continue;
}
$hashes[] = $marker->getCommitHash();
}
return $hashes;
}
} }

View file

@ -807,4 +807,12 @@ abstract class ArcanistRepositoryAPI extends Phobject {
return $uri; return $uri;
} }
final public function getPublishedCommitHashes() {
return $this->newPublishedCommitHashes();
}
protected function newPublishedCommitHashes() {
return array();
}
} }

View file

@ -23,11 +23,11 @@ final class ArcanistGitRepositoryMarkerQuery
$branch_prefix = 'refs/heads/'; $branch_prefix = 'refs/heads/';
$branch_length = strlen($branch_prefix); $branch_length = strlen($branch_prefix);
// NOTE: Since we only return branches today, we restrict this operation $remote_prefix = 'refs/remotes/';
// to branches. $remote_length = strlen($remote_prefix);
list($stdout) = $api->newFuture( list($stdout) = $api->newFuture(
'for-each-ref --format %s -- refs/heads/', 'for-each-ref --format %s -- refs/',
implode('%01', $field_list))->resolve(); implode('%01', $field_list))->resolve();
$markers = array(); $markers = array();
@ -53,9 +53,20 @@ final class ArcanistGitRepositoryMarkerQuery
list($ref, $hash, $epoch, $tree, $dst_hash, $summary, $text) = $fields; list($ref, $hash, $epoch, $tree, $dst_hash, $summary, $text) = $fields;
$remote_name = null;
if (!strncmp($ref, $branch_prefix, $branch_length)) { if (!strncmp($ref, $branch_prefix, $branch_length)) {
$type = ArcanistMarkerRef::TYPE_BRANCH; $type = ArcanistMarkerRef::TYPE_BRANCH;
$name = substr($ref, $branch_length); $name = substr($ref, $branch_length);
} else if (!strncmp($ref, $remote_prefix, $remote_length)) {
// This isn't entirely correct: the ref may be a tag, etc.
$type = ArcanistMarkerRef::TYPE_BRANCH;
$label = substr($ref, $remote_length);
$parts = explode('/', $label, 2);
$remote_name = $parts[0];
$name = $parts[1];
} else { } else {
// For now, discard other refs. // For now, discard other refs.
continue; continue;
@ -70,6 +81,10 @@ final class ArcanistGitRepositoryMarkerQuery
->setSummary($summary) ->setSummary($summary)
->setMessage($text); ->setMessage($text);
if ($remote_name !== null) {
$marker->setRemoteName($remote_name);
}
if (strlen($dst_hash)) { if (strlen($dst_hash)) {
$commit_hash = $dst_hash; $commit_hash = $dst_hash;
} else { } else {

View file

@ -20,6 +20,7 @@ final class ArcanistMarkerRef
private $summary; private $summary;
private $message; private $message;
private $isActive = false; private $isActive = false;
private $remoteName;
public function getRefDisplayName() { public function getRefDisplayName() {
switch ($this->getMarkerType()) { switch ($this->getMarkerType()) {
@ -130,6 +131,15 @@ final class ArcanistMarkerRef
return $this->isActive; return $this->isActive;
} }
public function setRemoteName($remote_name) {
$this->remoteName = $remote_name;
return $this;
}
public function getRemoteName() {
return $this->remoteName;
}
public function isBookmark() { public function isBookmark() {
return ($this->getMarkerType() === self::TYPE_BOOKMARK); return ($this->getMarkerType() === self::TYPE_BOOKMARK);
} }

View file

@ -9,6 +9,7 @@ abstract class ArcanistRepositoryMarkerQuery
private $commitHashes; private $commitHashes;
private $ancestorCommitHashes; private $ancestorCommitHashes;
private $remotes; private $remotes;
private $isRemoteCache = false;
final public function withMarkerTypes(array $types) { final public function withMarkerTypes(array $types) {
$this->markerTypes = array_fuse($types); $this->markerTypes = array_fuse($types);
@ -26,6 +27,11 @@ abstract class ArcanistRepositoryMarkerQuery
return $this; return $this;
} }
final public function withIsRemoteCache($is_cache) {
$this->isRemoteCache = $is_cache;
return $this;
}
final public function withIsActive($active) { final public function withIsActive($active) {
$this->isActive = $active; $this->isActive = $active;
return $this; return $this;
@ -88,6 +94,16 @@ abstract class ArcanistRepositoryMarkerQuery
} }
} }
if ($this->isRemoteCache !== null) {
$want_cache = $this->isRemoteCache;
foreach ($markers as $key => $marker) {
$is_cache = ($marker->getRemoteName() !== null);
if ($is_cache !== $want_cache) {
unset($markers[$key]);
}
}
}
return $this->sortMarkers($markers); return $this->sortMarkers($markers);
} }

View file

@ -89,4 +89,13 @@ final class ArcanistRemoteRef
return null; return null;
} }
public function isPermanentRef(ArcanistMarkerRef $ref) {
$repository_ref = $this->getPushRepositoryRef();
if (!$repository_ref) {
return false;
}
return $repository_ref->isPermanentRef($ref);
}
} }

View file

@ -41,6 +41,10 @@ EOTEXT
return $this->lookRemotes(); return $this->lookRemotes();
} }
if ($argv === array('published')) {
return $this->lookPublished();
}
echo tsprintf( echo tsprintf(
"%s\n", "%s\n",
pht( pht(
@ -195,6 +199,48 @@ EOTEXT
echo tsprintf('%s', $view); echo tsprintf('%s', $view);
} }
echo tsprintf("\n");
echo tsprintf(
pht(
"Across the grove, a stream flows north toward ".
"**published** commits.\n"));
}
private function lookPublished() {
echo tsprintf(
"%W\n\n",
pht(
'You walk along the narrow bank of the stream as it winds lazily '.
'downhill and turns east, gradually widening into a river.'));
$api = $this->getRepositoryAPI();
$published = $api->getPublishedCommitHashes();
if ($published) {
echo tsprintf(
"%W\n\n",
pht(
'Floating on the water, you see published commits:'));
foreach ($published as $hash) {
echo tsprintf(
"%s\n",
$hash);
}
echo tsprintf(
"\n%W\n",
pht(
'They river bubbles peacefully.'));
} else {
echo tsprintf(
"%W\n",
pht(
'The river bubbles quietly, but you do not see any published '.
'commits anywhere.'));
}
} }
} }