mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-01 03:02:43 +01:00
36e2d02d6e
Summary: `pht`ize a whole bunch of strings in rP. Test Plan: Intense eyeballing. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: hach-que, Korvin, epriestley Differential Revision: https://secure.phabricator.com/D12797
185 lines
5.2 KiB
PHP
185 lines
5.2 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Resolves references into canonical, stable commit identifiers by examining
|
|
* database caches.
|
|
*
|
|
* This is a counterpart to @{class:DiffusionLowLevelResolveRefsQuery}. This
|
|
* query offers fast resolution, but can not resolve everything that the
|
|
* low-level query can.
|
|
*
|
|
* This class can resolve the most common refs (commits, branches, tags) and
|
|
* can do so cheapy (by examining the database, without needing to make calls
|
|
* to the VCS or the service host).
|
|
*/
|
|
final class DiffusionCachedResolveRefsQuery
|
|
extends DiffusionLowLevelQuery {
|
|
|
|
private $refs;
|
|
private $types;
|
|
|
|
public function withRefs(array $refs) {
|
|
$this->refs = $refs;
|
|
return $this;
|
|
}
|
|
|
|
public function withTypes(array $types) {
|
|
$this->types = $types;
|
|
return $this;
|
|
}
|
|
|
|
protected function executeQuery() {
|
|
if (!$this->refs) {
|
|
return array();
|
|
}
|
|
|
|
switch ($this->getRepository()->getVersionControlSystem()) {
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
|
$result = $this->resolveGitAndMercurialRefs();
|
|
break;
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
|
$result = $this->resolveSubversionRefs();
|
|
break;
|
|
default:
|
|
throw new Exception(pht('Unsupported repository type!'));
|
|
}
|
|
|
|
if ($this->types !== null) {
|
|
$result = $this->filterRefsByType($result, $this->types);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Resolve refs in Git and Mercurial repositories.
|
|
*
|
|
* We can resolve commit hashes from the commits table, and branch and tag
|
|
* names from the refcursor table.
|
|
*/
|
|
private function resolveGitAndMercurialRefs() {
|
|
$repository = $this->getRepository();
|
|
|
|
$conn_r = $repository->establishConnection('r');
|
|
|
|
$results = array();
|
|
|
|
$prefixes = array();
|
|
foreach ($this->refs as $ref) {
|
|
// We require refs to look like hashes and be at least 4 characters
|
|
// long. This is similar to the behavior of git.
|
|
if (preg_match('/^[a-f0-9]{4,}$/', $ref)) {
|
|
$prefixes[] = qsprintf(
|
|
$conn_r,
|
|
'(commitIdentifier LIKE %>)',
|
|
$ref);
|
|
}
|
|
}
|
|
|
|
if ($prefixes) {
|
|
$commits = queryfx_all(
|
|
$conn_r,
|
|
'SELECT commitIdentifier FROM %T
|
|
WHERE repositoryID = %s AND %Q',
|
|
id(new PhabricatorRepositoryCommit())->getTableName(),
|
|
$repository->getID(),
|
|
implode(' OR ', $prefixes));
|
|
|
|
foreach ($commits as $commit) {
|
|
$hash = $commit['commitIdentifier'];
|
|
foreach ($this->refs as $ref) {
|
|
if (!strncmp($hash, $ref, strlen($ref))) {
|
|
$results[$ref][] = array(
|
|
'type' => 'commit',
|
|
'identifier' => $hash,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$name_hashes = array();
|
|
foreach ($this->refs as $ref) {
|
|
$name_hashes[PhabricatorHash::digestForIndex($ref)] = $ref;
|
|
}
|
|
|
|
$cursors = queryfx_all(
|
|
$conn_r,
|
|
'SELECT refNameHash, refType, commitIdentifier, isClosed FROM %T
|
|
WHERE repositoryPHID = %s AND refNameHash IN (%Ls)',
|
|
id(new PhabricatorRepositoryRefCursor())->getTableName(),
|
|
$repository->getPHID(),
|
|
array_keys($name_hashes));
|
|
|
|
foreach ($cursors as $cursor) {
|
|
if (isset($name_hashes[$cursor['refNameHash']])) {
|
|
$results[$name_hashes[$cursor['refNameHash']]][] = array(
|
|
'type' => $cursor['refType'],
|
|
'identifier' => $cursor['commitIdentifier'],
|
|
'closed' => (bool)$cursor['isClosed'],
|
|
);
|
|
|
|
// TODO: In Git, we don't store (and thus don't return) the hash
|
|
// of the tag itself. It would be vaguely nice to do this.
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
|
|
/**
|
|
* Resolve refs in Subversion repositories.
|
|
*
|
|
* We can resolve all numeric identifiers and the keyword `HEAD`.
|
|
*/
|
|
private function resolveSubversionRefs() {
|
|
$repository = $this->getRepository();
|
|
|
|
$max_commit = id(new PhabricatorRepositoryCommit())
|
|
->loadOneWhere(
|
|
'repositoryID = %d ORDER BY epoch DESC, id DESC LIMIT 1',
|
|
$repository->getID());
|
|
if (!$max_commit) {
|
|
// This repository is empty or hasn't parsed yet, so none of the refs are
|
|
// going to resolve.
|
|
return array();
|
|
}
|
|
|
|
$max_commit_id = (int)$max_commit->getCommitIdentifier();
|
|
|
|
$results = array();
|
|
foreach ($this->refs as $ref) {
|
|
if ($ref == 'HEAD') {
|
|
// Resolve "HEAD" to mean "the most recent commit".
|
|
$results[$ref][] = array(
|
|
'type' => 'commit',
|
|
'identifier' => $max_commit_id,
|
|
);
|
|
continue;
|
|
}
|
|
|
|
if (!preg_match('/^\d+$/', $ref)) {
|
|
// This ref is non-numeric, so it doesn't resolve to anything.
|
|
continue;
|
|
}
|
|
|
|
// Resolve other commits if we can deduce their existence.
|
|
|
|
// TODO: When we import only part of a repository, we won't necessarily
|
|
// have all of the smaller commits. Should we fail to resolve them here
|
|
// for repositories with a subpath? It might let us simplify other things
|
|
// elsewhere.
|
|
if ((int)$ref <= $max_commit_id) {
|
|
$results[$ref][] = array(
|
|
'type' => 'commit',
|
|
'identifier' => (int)$ref,
|
|
);
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
}
|