1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 16:22:42 +01:00
phorge-arcanist/src/repository/marker/ArcanistGitRepositoryMarkerQuery.php
epriestley 80f5166b70 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
2020-06-30 14:56:34 -07:00

195 lines
4.8 KiB
PHP

<?php
final class ArcanistGitRepositoryMarkerQuery
extends ArcanistRepositoryMarkerQuery {
protected function newLocalRefMarkers() {
$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);
$remote_prefix = 'refs/remotes/';
$remote_length = strlen($remote_prefix);
list($stdout) = $api->newFuture(
'for-each-ref --format %s -- refs/',
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;
$remote_name = null;
if (!strncmp($ref, $branch_prefix, $branch_length)) {
$type = ArcanistMarkerRef::TYPE_BRANCH;
$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 {
// 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 ($remote_name !== null) {
$marker->setRemoteName($remote_name);
}
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];
}
protected function newRemoteRefMarkers(ArcanistRemoteRef $remote) {
$api = $this->getRepositoryAPI();
// NOTE: Since we only care about branches today, we only list branches.
$future = $api->newFuture(
'ls-remote --refs %s %s',
$remote->getRemoteName(),
'refs/heads/*');
list($stdout) = $future->resolve();
$branch_prefix = 'refs/heads/';
$branch_length = strlen($branch_prefix);
$pattern = '(^(?P<hash>\S+)\t(?P<ref>\S+)\z)';
$markers = array();
$lines = phutil_split_lines($stdout, false);
foreach ($lines as $line) {
$matches = null;
$ok = preg_match($pattern, $line, $matches);
if (!$ok) {
throw new Exception(
pht(
'Failed to match "ls-remote" pattern against line "%s".',
$line));
}
$hash = $matches['hash'];
$ref = $matches['ref'];
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)
->setMarkerHash($hash)
->setCommitHash($hash);
$commit_ref = $api->newCommitRef()
->setCommitHash($hash);
$marker->attachCommitRef($commit_ref);
$markers[] = $marker;
}
return $markers;
}
}