Something went wrong. Try again.
@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
Something went wrong. Try again.
5.4 kB · 187 lines
PHP
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188<?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 cheaply (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 %LO', id(new PhabricatorRepositoryCommit())->getTableName(), $repository->getID(), $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 c.refNameHash, c.refType, p.commitIdentifier, p.isClosed FROM %T c JOIN %T p ON p.cursorID = c.id WHERE c.repositoryPHID = %s AND c.refNameHash IN (%Ls)', id(new PhabricatorRepositoryRefCursor())->getTableName(), id(new PhabricatorRepositoryRefPosition())->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; }
}