2013-02-27 17:04:54 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class DiffusionCommitQuery
|
|
|
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
|
|
|
|
2013-05-17 12:51:33 +02:00
|
|
|
private $ids;
|
2013-02-27 17:04:54 +01:00
|
|
|
private $identifiers;
|
2013-02-27 19:54:39 +01:00
|
|
|
private $phids;
|
2013-05-07 03:05:33 +02:00
|
|
|
private $defaultRepository;
|
2013-07-21 19:57:07 +02:00
|
|
|
private $identifierMap;
|
2013-02-27 17:04:54 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Load commits by partial or full identifiers, e.g. "rXab82393", "rX1234",
|
|
|
|
* or "a9caf12". When an identifier matches multiple commits, they will all
|
|
|
|
* be returned; callers should be prepared to deal with more results than
|
|
|
|
* they queried for.
|
|
|
|
*/
|
|
|
|
public function withIdentifiers(array $identifiers) {
|
|
|
|
$this->identifiers = $identifiers;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-05-07 03:05:33 +02:00
|
|
|
/**
|
|
|
|
* If a default repository is provided, ambiguous commit identifiers will
|
|
|
|
* be assumed to belong to the default repository.
|
|
|
|
*
|
|
|
|
* For example, "r123" appearing in a commit message in repository X is
|
|
|
|
* likely to be unambiguously "rX123". Normally the reference would be
|
|
|
|
* considered ambiguous, but if you provide a default repository it will
|
|
|
|
* be correctly resolved.
|
|
|
|
*/
|
|
|
|
public function withDefaultRepository(PhabricatorRepository $repository) {
|
|
|
|
$this->defaultRepository = $repository;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-05-17 12:51:33 +02:00
|
|
|
public function withIDs(array $ids) {
|
|
|
|
$this->ids = $ids;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-02-27 19:54:39 +01:00
|
|
|
public function withPHIDs(array $phids) {
|
|
|
|
$this->phids = $phids;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-07-21 19:57:07 +02:00
|
|
|
public function getIdentifierMap() {
|
|
|
|
if ($this->identifierMap === null) {
|
|
|
|
throw new Exception(
|
|
|
|
"You must execute() the query before accessing the identifier map.");
|
|
|
|
}
|
|
|
|
return $this->identifierMap;
|
|
|
|
}
|
|
|
|
|
2013-03-01 20:28:02 +01:00
|
|
|
protected function loadPage() {
|
2013-07-21 19:57:07 +02:00
|
|
|
if ($this->identifierMap === null) {
|
|
|
|
$this->identifierMap = array();
|
|
|
|
}
|
|
|
|
|
2013-02-27 17:04:54 +01:00
|
|
|
$table = new PhabricatorRepositoryCommit();
|
|
|
|
$conn_r = $table->establishConnection('r');
|
|
|
|
|
|
|
|
$data = queryfx_all(
|
|
|
|
$conn_r,
|
|
|
|
'SELECT * FROM %T %Q %Q %Q',
|
|
|
|
$table->getTableName(),
|
|
|
|
$this->buildWhereClause($conn_r),
|
|
|
|
$this->buildOrderClause($conn_r),
|
|
|
|
$this->buildLimitClause($conn_r));
|
|
|
|
|
|
|
|
return $table->loadAllFromArray($data);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function willFilterPage(array $commits) {
|
|
|
|
$repository_ids = mpull($commits, 'getRepositoryID', 'getRepositoryID');
|
|
|
|
$repos = id(new PhabricatorRepositoryQuery())
|
|
|
|
->setViewer($this->getViewer())
|
|
|
|
->withIDs($repository_ids)
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
foreach ($commits as $key => $commit) {
|
|
|
|
$repo = idx($repos, $commit->getRepositoryID());
|
|
|
|
if ($repo) {
|
|
|
|
$commit->attachRepository($repo);
|
|
|
|
} else {
|
|
|
|
unset($commits[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-21 19:57:07 +02:00
|
|
|
if ($this->identifiers !== null) {
|
|
|
|
$ids = array_fuse($this->identifiers);
|
|
|
|
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
|
|
|
|
|
|
|
|
$result = array();
|
|
|
|
foreach ($commits as $commit) {
|
|
|
|
$prefix = 'r'.$commit->getRepository()->getCallsign();
|
|
|
|
$suffix = $commit->getCommitIdentifier();
|
|
|
|
|
|
|
|
if ($commit->getRepository()->isSVN()) {
|
|
|
|
if (isset($ids[$prefix.$suffix])) {
|
|
|
|
$result[$prefix.$suffix][] = $commit;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This awkward contruction is so we can link the commits up in O(N)
|
|
|
|
// time instead of O(N^2).
|
|
|
|
for ($ii = $min_qualified; $ii <= strlen($suffix); $ii++) {
|
|
|
|
$part = substr($suffix, 0, $ii);
|
|
|
|
if (isset($ids[$prefix.$part])) {
|
|
|
|
$result[$prefix.$part][] = $commit;
|
|
|
|
}
|
|
|
|
if (isset($ids[$part])) {
|
|
|
|
$result[$part][] = $commit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($result as $identifier => $matching_commits) {
|
|
|
|
if (count($matching_commits) == 1) {
|
|
|
|
$result[$identifier] = head($matching_commits);
|
|
|
|
} else {
|
|
|
|
// This reference is ambiguous (it matches more than one commit) so
|
|
|
|
// don't link it
|
|
|
|
unset($result[$identifier]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->identifierMap += $result;
|
|
|
|
}
|
|
|
|
|
2013-02-27 17:04:54 +01:00
|
|
|
return $commits;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
|
|
|
$where = array();
|
|
|
|
|
|
|
|
if ($this->identifiers) {
|
|
|
|
$min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH;
|
|
|
|
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
|
|
|
|
|
|
|
|
$refs = array();
|
|
|
|
$bare = array();
|
|
|
|
foreach ($this->identifiers as $identifier) {
|
|
|
|
$matches = null;
|
|
|
|
preg_match('/^(?:r([A-Z]+))?(.*)$/', $identifier, $matches);
|
|
|
|
$repo = nonempty($matches[1], null);
|
|
|
|
$identifier = nonempty($matches[2], null);
|
|
|
|
|
2013-05-07 03:05:33 +02:00
|
|
|
if ($repo === null) {
|
|
|
|
if ($this->defaultRepository) {
|
|
|
|
$repo = $this->defaultRepository->getCallsign();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-27 17:04:54 +01:00
|
|
|
if ($repo === null) {
|
|
|
|
if (strlen($identifier) < $min_unqualified) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$bare[] = $identifier;
|
|
|
|
} else {
|
|
|
|
$refs[] = array(
|
|
|
|
'callsign' => $repo,
|
|
|
|
'identifier' => $identifier,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$sql = array();
|
|
|
|
|
|
|
|
foreach ($bare as $identifier) {
|
|
|
|
$sql[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'(commitIdentifier LIKE %> AND LENGTH(commitIdentifier) = 40)',
|
|
|
|
$identifier);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($refs) {
|
|
|
|
$callsigns = ipull($refs, 'callsign');
|
|
|
|
$repos = id(new PhabricatorRepositoryQuery())
|
|
|
|
->setViewer($this->getViewer())
|
|
|
|
->withCallsigns($callsigns)
|
|
|
|
->execute();
|
|
|
|
$repos = mpull($repos, null, 'getCallsign');
|
|
|
|
|
|
|
|
foreach ($refs as $key => $ref) {
|
|
|
|
$repo = idx($repos, $ref['callsign']);
|
|
|
|
if (!$repo) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($repo->isSVN()) {
|
2013-03-19 23:30:16 +01:00
|
|
|
if (!ctype_digit($ref['identifier'])) {
|
|
|
|
continue;
|
|
|
|
}
|
2013-02-27 17:04:54 +01:00
|
|
|
$sql[] = qsprintf(
|
|
|
|
$conn_r,
|
2013-06-14 03:01:40 +02:00
|
|
|
'(repositoryID = %d AND commitIdentifier = %s)',
|
2013-02-27 17:04:54 +01:00
|
|
|
$repo->getID(),
|
2013-06-14 03:01:40 +02:00
|
|
|
// NOTE: Because the 'commitIdentifier' column is a string, MySQL
|
|
|
|
// ignores the index if we hand it an integer. Hand it a string.
|
|
|
|
// See T3377.
|
|
|
|
(int)$ref['identifier']);
|
2013-02-27 17:04:54 +01:00
|
|
|
} else {
|
|
|
|
if (strlen($ref['identifier']) < $min_qualified) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$sql[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'(repositoryID = %d AND commitIdentifier LIKE %>)',
|
|
|
|
$repo->getID(),
|
|
|
|
$ref['identifier']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-01 20:28:02 +01:00
|
|
|
if (!$sql) {
|
2013-02-27 17:04:54 +01:00
|
|
|
// If we discarded all possible identifiers (e.g., they all referenced
|
|
|
|
// bogus repositories or were all too short), make sure the query finds
|
|
|
|
// nothing.
|
2013-03-01 20:28:02 +01:00
|
|
|
throw new PhabricatorEmptyQueryException('No commit identifiers.');
|
2013-02-27 17:04:54 +01:00
|
|
|
}
|
2013-03-01 20:28:02 +01:00
|
|
|
|
|
|
|
$where[] = '('.implode(' OR ', $sql).')';
|
2013-02-27 17:04:54 +01:00
|
|
|
}
|
|
|
|
|
2013-05-17 12:51:33 +02:00
|
|
|
if ($this->ids) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'id IN (%Ld)',
|
|
|
|
$this->ids);
|
|
|
|
}
|
|
|
|
|
2013-02-27 19:54:39 +01:00
|
|
|
if ($this->phids) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'phid IN (%Ls)',
|
|
|
|
$this->phids);
|
|
|
|
}
|
|
|
|
|
2013-02-27 17:04:54 +01:00
|
|
|
return $this->formatWhereClause($where);
|
|
|
|
}
|
|
|
|
|
2013-07-21 19:57:07 +02:00
|
|
|
public function didFilterResults(array $filtered) {
|
|
|
|
if ($this->identifierMap) {
|
|
|
|
foreach ($this->identifierMap as $name => $commit) {
|
|
|
|
if (isset($filtered[$commit->getPHID()])) {
|
|
|
|
unset($this->identifierMap[$name]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-27 17:04:54 +01:00
|
|
|
}
|