2013-11-04 14:13:07 -08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolves references (like short commit names, branch names, tag names, etc.)
|
|
|
|
* into canonical, stable commit identifiers. This query works for all
|
|
|
|
* repository types.
|
2015-01-23 13:31:17 -08:00
|
|
|
*
|
|
|
|
* 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.
|
2013-11-04 14:13:07 -08:00
|
|
|
*/
|
|
|
|
final class DiffusionLowLevelResolveRefsQuery
|
|
|
|
extends DiffusionLowLevelQuery {
|
|
|
|
|
|
|
|
private $refs;
|
2015-04-27 03:51:53 -07:00
|
|
|
private $types;
|
2013-11-04 14:13:07 -08:00
|
|
|
|
|
|
|
public function withRefs(array $refs) {
|
|
|
|
$this->refs = $refs;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-04-27 03:51:53 -07:00
|
|
|
public function withTypes(array $types) {
|
|
|
|
$this->types = $types;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-01-16 06:56:32 +11:00
|
|
|
protected function executeQuery() {
|
2013-11-04 14:13:07 -08:00
|
|
|
if (!$this->refs) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($this->getRepository()->getVersionControlSystem()) {
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
|
|
|
$result = $this->resolveGitRefs();
|
|
|
|
break;
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
|
|
|
$result = $this->resolveMercurialRefs();
|
|
|
|
break;
|
|
|
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
|
|
|
$result = $this->resolveSubversionRefs();
|
|
|
|
break;
|
|
|
|
default:
|
2014-06-09 11:36:49 -07:00
|
|
|
throw new Exception('Unsupported repository type!');
|
2013-11-04 14:13:07 -08:00
|
|
|
}
|
|
|
|
|
2015-04-27 03:51:53 -07:00
|
|
|
if ($this->types !== null) {
|
|
|
|
$result = $this->filterRefsByType($result, $this->types);
|
|
|
|
}
|
|
|
|
|
2013-11-04 14:13:07 -08:00
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function resolveGitRefs() {
|
|
|
|
$repository = $this->getRepository();
|
|
|
|
|
2015-04-27 03:49:57 -07:00
|
|
|
// TODO: When refs are ambiguous (for example, tags and branches with
|
|
|
|
// the same name) this will only resolve one of them.
|
2013-11-04 14:13:07 -08:00
|
|
|
$future = $repository->getLocalCommandFuture('cat-file --batch-check');
|
|
|
|
$future->write(implode("\n", $this->refs));
|
|
|
|
list($stdout) = $future->resolvex();
|
|
|
|
|
|
|
|
$lines = explode("\n", rtrim($stdout, "\n"));
|
|
|
|
if (count($lines) !== count($this->refs)) {
|
2014-06-09 11:36:49 -07:00
|
|
|
throw new Exception('Unexpected line count from `git cat-file`!');
|
2013-11-04 14:13:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
$hits = array();
|
|
|
|
$tags = array();
|
|
|
|
|
|
|
|
$lines = array_combine($this->refs, $lines);
|
|
|
|
foreach ($lines as $ref => $line) {
|
|
|
|
$parts = explode(' ', $line);
|
|
|
|
if (count($parts) < 2) {
|
|
|
|
throw new Exception("Failed to parse `git cat-file` output: {$line}");
|
|
|
|
}
|
|
|
|
list($identifier, $type) = $parts;
|
|
|
|
|
|
|
|
if ($type == 'missing') {
|
|
|
|
// This is either an ambiguous reference which resolves to several
|
|
|
|
// objects, or an invalid reference. For now, always treat it as
|
|
|
|
// invalid. It would be nice to resolve all possibilities for
|
|
|
|
// ambiguous references at some point, although the strategy for doing
|
|
|
|
// so isn't clear to me.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($type) {
|
|
|
|
case 'commit':
|
|
|
|
break;
|
|
|
|
case 'tag':
|
|
|
|
$tags[] = $identifier;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception(
|
|
|
|
"Unexpected object type from `git cat-file`: {$line}");
|
|
|
|
}
|
|
|
|
|
|
|
|
$hits[] = array(
|
|
|
|
'ref' => $ref,
|
|
|
|
'type' => $type,
|
|
|
|
'identifier' => $identifier,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$tag_map = array();
|
|
|
|
if ($tags) {
|
|
|
|
// If some of the refs were tags, just load every tag in order to figure
|
|
|
|
// out which commits they map to. This might be somewhat inefficient in
|
|
|
|
// repositories with a huge number of tags.
|
|
|
|
$tag_refs = id(new DiffusionLowLevelGitRefQuery())
|
|
|
|
->setRepository($repository)
|
|
|
|
->withIsTag(true)
|
|
|
|
->executeQuery();
|
|
|
|
foreach ($tag_refs as $tag_ref) {
|
|
|
|
$tag_map[$tag_ref->getShortName()] = $tag_ref->getCommitIdentifier();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$results = array();
|
|
|
|
foreach ($hits as $hit) {
|
|
|
|
$type = $hit['type'];
|
|
|
|
$ref = $hit['ref'];
|
|
|
|
|
|
|
|
$alternate = null;
|
|
|
|
if ($type == 'tag') {
|
|
|
|
$alternate = $identifier;
|
|
|
|
$identifier = idx($tag_map, $ref);
|
|
|
|
if (!$identifier) {
|
|
|
|
throw new Exception("Failed to look up tag '{$ref}'!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$result = array(
|
|
|
|
'type' => $type,
|
|
|
|
'identifier' => $identifier,
|
|
|
|
);
|
|
|
|
|
|
|
|
if ($alternate !== null) {
|
|
|
|
$result['alternate'] = $alternate;
|
|
|
|
}
|
|
|
|
|
|
|
|
$results[$ref][] = $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function resolveMercurialRefs() {
|
|
|
|
$repository = $this->getRepository();
|
|
|
|
|
Improve low-level branch resolution in Mercurial
Summary:
Ref T7100. Ref T7108. Ref T6160. Several issues:
- High load for mercurial repositories with huge numbers of branches (T7108).
- In Mercurial, we resolve refs individually (one `hg` call per ref).
- Each repository update also updates all refs, which requires resolving all of them.
- For repositories with a huge number of branches,
- We don't distinguish between closed branches (a Mercurial-only concept) and open branches (T6160).
- In Git, when a branch is merged, it ceases to exist.
- In Mercurial, when a branch is merged, it still exists, it's just "closed". Normally, no one cares about these branches.
- In the low-level query, correctly identify which refs we resolve as branches.
- In the low-level query, correctly mark closed branches as closed.
- This marginally improves ref handling in general (see T7100).
Test Plan:
{F384366}
{F384367}
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T6160, T7108, T7100
Differential Revision: https://secure.phabricator.com/D12548
2015-04-27 03:50:20 -07:00
|
|
|
// First, pull all of the branch heads in the repository. Doing this in
|
|
|
|
// bulk is much faster than querying each individual head if we're
|
|
|
|
// checking even a small number of refs.
|
2015-04-27 03:50:55 -07:00
|
|
|
$branches = id(new DiffusionLowLevelMercurialBranchesQuery())
|
|
|
|
->setRepository($repository)
|
|
|
|
->executeQuery();
|
|
|
|
|
|
|
|
$branches = mgroup($branches, 'getShortName');
|
Improve low-level branch resolution in Mercurial
Summary:
Ref T7100. Ref T7108. Ref T6160. Several issues:
- High load for mercurial repositories with huge numbers of branches (T7108).
- In Mercurial, we resolve refs individually (one `hg` call per ref).
- Each repository update also updates all refs, which requires resolving all of them.
- For repositories with a huge number of branches,
- We don't distinguish between closed branches (a Mercurial-only concept) and open branches (T6160).
- In Git, when a branch is merged, it ceases to exist.
- In Mercurial, when a branch is merged, it still exists, it's just "closed". Normally, no one cares about these branches.
- In the low-level query, correctly identify which refs we resolve as branches.
- In the low-level query, correctly mark closed branches as closed.
- This marginally improves ref handling in general (see T7100).
Test Plan:
{F384366}
{F384367}
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T6160, T7108, T7100
Differential Revision: https://secure.phabricator.com/D12548
2015-04-27 03:50:20 -07:00
|
|
|
|
|
|
|
$results = array();
|
|
|
|
$unresolved = $this->refs;
|
|
|
|
foreach ($unresolved as $key => $ref) {
|
2015-04-27 03:50:55 -07:00
|
|
|
if (empty($branches[$ref])) {
|
Improve low-level branch resolution in Mercurial
Summary:
Ref T7100. Ref T7108. Ref T6160. Several issues:
- High load for mercurial repositories with huge numbers of branches (T7108).
- In Mercurial, we resolve refs individually (one `hg` call per ref).
- Each repository update also updates all refs, which requires resolving all of them.
- For repositories with a huge number of branches,
- We don't distinguish between closed branches (a Mercurial-only concept) and open branches (T6160).
- In Git, when a branch is merged, it ceases to exist.
- In Mercurial, when a branch is merged, it still exists, it's just "closed". Normally, no one cares about these branches.
- In the low-level query, correctly identify which refs we resolve as branches.
- In the low-level query, correctly mark closed branches as closed.
- This marginally improves ref handling in general (see T7100).
Test Plan:
{F384366}
{F384367}
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T6160, T7108, T7100
Differential Revision: https://secure.phabricator.com/D12548
2015-04-27 03:50:20 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-04-27 03:50:55 -07:00
|
|
|
foreach ($branches[$ref] as $branch) {
|
|
|
|
$fields = $branch->getRawFields();
|
|
|
|
|
|
|
|
$results[$ref][] = array(
|
Improve low-level branch resolution in Mercurial
Summary:
Ref T7100. Ref T7108. Ref T6160. Several issues:
- High load for mercurial repositories with huge numbers of branches (T7108).
- In Mercurial, we resolve refs individually (one `hg` call per ref).
- Each repository update also updates all refs, which requires resolving all of them.
- For repositories with a huge number of branches,
- We don't distinguish between closed branches (a Mercurial-only concept) and open branches (T6160).
- In Git, when a branch is merged, it ceases to exist.
- In Mercurial, when a branch is merged, it still exists, it's just "closed". Normally, no one cares about these branches.
- In the low-level query, correctly identify which refs we resolve as branches.
- In the low-level query, correctly mark closed branches as closed.
- This marginally improves ref handling in general (see T7100).
Test Plan:
{F384366}
{F384367}
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T6160, T7108, T7100
Differential Revision: https://secure.phabricator.com/D12548
2015-04-27 03:50:20 -07:00
|
|
|
'type' => 'branch',
|
2015-04-27 03:50:55 -07:00
|
|
|
'identifier' => $branch->getCommitIdentifier(),
|
|
|
|
'closed' => idx($fields, 'closed', false),
|
Improve low-level branch resolution in Mercurial
Summary:
Ref T7100. Ref T7108. Ref T6160. Several issues:
- High load for mercurial repositories with huge numbers of branches (T7108).
- In Mercurial, we resolve refs individually (one `hg` call per ref).
- Each repository update also updates all refs, which requires resolving all of them.
- For repositories with a huge number of branches,
- We don't distinguish between closed branches (a Mercurial-only concept) and open branches (T6160).
- In Git, when a branch is merged, it ceases to exist.
- In Mercurial, when a branch is merged, it still exists, it's just "closed". Normally, no one cares about these branches.
- In the low-level query, correctly identify which refs we resolve as branches.
- In the low-level query, correctly mark closed branches as closed.
- This marginally improves ref handling in general (see T7100).
Test Plan:
{F384366}
{F384367}
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T6160, T7108, T7100
Differential Revision: https://secure.phabricator.com/D12548
2015-04-27 03:50:20 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
unset($unresolved[$key]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$unresolved) {
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we still have unresolved refs (which might be things like "tip"),
|
|
|
|
// try to resolve them individually.
|
|
|
|
|
|
|
|
$futures = array();
|
|
|
|
foreach ($unresolved as $ref) {
|
2013-11-04 14:13:07 -08:00
|
|
|
$futures[$ref] = $repository->getLocalCommandFuture(
|
|
|
|
'log --template=%s --rev %s',
|
|
|
|
'{node}',
|
|
|
|
hgsprintf('%s', $ref));
|
|
|
|
}
|
|
|
|
|
2014-12-30 23:13:38 +11:00
|
|
|
foreach (new FutureIterator($futures) as $ref => $future) {
|
2013-11-04 14:13:07 -08:00
|
|
|
try {
|
|
|
|
list($stdout) = $future->resolvex();
|
|
|
|
} catch (CommandException $ex) {
|
|
|
|
if (preg_match('/ambiguous identifier/', $ex->getStdErr())) {
|
|
|
|
// This indicates that the ref ambiguously matched several things.
|
|
|
|
// Eventually, it would be nice to return all of them, but it is
|
|
|
|
// unclear how to best do that. For now, treat it as a miss instead.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
throw $ex;
|
|
|
|
}
|
|
|
|
|
|
|
|
// It doesn't look like we can figure out the type (commit/branch/rev)
|
|
|
|
// from this output very easily. For now, just call everything a commit.
|
|
|
|
$type = 'commit';
|
|
|
|
|
|
|
|
$results[$ref][] = array(
|
|
|
|
'type' => $type,
|
|
|
|
'identifier' => trim($stdout),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function resolveSubversionRefs() {
|
2015-01-23 13:31:17 -08:00
|
|
|
// 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();
|
2013-11-04 14:13:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|