Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15473137
D11476.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
D11476.id.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -464,6 +464,7 @@
'DiffusionBrowseResultSet' => 'applications/diffusion/data/DiffusionBrowseResultSet.php',
'DiffusionBrowseSearchController' => 'applications/diffusion/controller/DiffusionBrowseSearchController.php',
'DiffusionBrowseTableView' => 'applications/diffusion/view/DiffusionBrowseTableView.php',
+ 'DiffusionCachedResolveRefsQuery' => 'applications/diffusion/query/DiffusionCachedResolveRefsQuery.php',
'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php',
'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php',
'DiffusionCommitChangeTableView' => 'applications/diffusion/view/DiffusionCommitChangeTableView.php',
@@ -3560,6 +3561,7 @@
'DiffusionBrowseQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionBrowseSearchController' => 'DiffusionBrowseController',
'DiffusionBrowseTableView' => 'DiffusionView',
+ 'DiffusionCachedResolveRefsQuery' => 'DiffusionLowLevelQuery',
'DiffusionChangeController' => 'DiffusionController',
'DiffusionCommitBranchesController' => 'DiffusionController',
'DiffusionCommitChangeTableView' => 'DiffusionView',
diff --git a/src/applications/diffusion/query/DiffusionCachedResolveRefsQuery.php b/src/applications/diffusion/query/DiffusionCachedResolveRefsQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/query/DiffusionCachedResolveRefsQuery.php
@@ -0,0 +1,174 @@
+<?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;
+
+ public function withRefs(array $refs) {
+ $this->refs = $refs;
+ 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('Unsupported repository type!');
+ }
+
+ 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 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'],
+ );
+
+ // 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;
+ }
+
+}
diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
--- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
+++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
@@ -4,6 +4,11 @@
* Resolves references (like short commit names, branch names, tag names, etc.)
* into canonical, stable commit identifiers. This query works for all
* repository types.
+ *
+ * This query will always resolve refs which can be resolved, but may need to
+ * perform VCS operations. A faster (but less complete) counterpart query is
+ * available in @{class:DiffusionCachedResolveRefsQuery}; that query can
+ * resolve most refs without VCS operations.
*/
final class DiffusionLowLevelResolveRefsQuery
extends DiffusionLowLevelQuery {
@@ -169,51 +174,12 @@
}
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;
+ // We don't have any VCS logic for Subversion, so just use the cached
+ // query.
+ return id(new DiffusionCachedResolveRefsQuery())
+ ->setRepository($this->getRepository())
+ ->withRefs($this->refs)
+ ->execute();
}
}
diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php
--- a/src/applications/diffusion/request/DiffusionRequest.php
+++ b/src/applications/diffusion/request/DiffusionRequest.php
@@ -750,20 +750,42 @@
}
private function resolveRefs(array $refs) {
- if ($this->shouldInitFromConduit()) {
- return DiffusionQuery::callConduitWithDiffusionRequest(
- $this->getUser(),
- $this,
- 'diffusion.resolverefs',
- array(
- 'refs' => $refs,
- ));
+ // First, try to resolve refs from fast cache sources.
+ $cached_results = id(new DiffusionCachedResolveRefsQuery())
+ ->setRepository($this->getRepository())
+ ->withRefs($refs)
+ ->execute();
+
+ // Throw away all the refs we resolved. Hopefully, we'll throw away
+ // everything here.
+ foreach ($refs as $key => $ref) {
+ if (isset($cached_results[$ref])) {
+ unset($refs[$key]);
+ }
+ }
+
+ // If we couldn't pull everything out of the cache, execute the underlying
+ // VCS operation.
+ if ($refs) {
+ if ($this->shouldInitFromConduit()) {
+ $vcs_results = DiffusionQuery::callConduitWithDiffusionRequest(
+ $this->getUser(),
+ $this,
+ 'diffusion.resolverefs',
+ array(
+ 'refs' => $refs,
+ ));
+ } else {
+ $vcs_results = id(new DiffusionLowLevelResolveRefsQuery())
+ ->setRepository($this->getRepository())
+ ->withRefs($refs)
+ ->execute();
+ }
} else {
- return id(new DiffusionLowLevelResolveRefsQuery())
- ->setRepository($this->getRepository())
- ->withRefs($refs)
- ->execute();
+ $vcs_results = array();
}
+
+ return $vcs_results + $cached_results;
}
public function setIsClusterRequest($is_cluster_request) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Apr 6, 7:46 PM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7733654
Default Alt Text
D11476.id.diff (11 KB)
Attached To
Mode
D11476: Provide a fast path for resolving repository refs
Attached
Detach File
Event Timeline
Log In to Comment