1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-30 01:10:58 +01:00

Consolidate querying of things which we can use git for-each-ref for

Summary: Ref T2230. This cleans up D7442, by using `git for-each-ref` everywhere we can, in a basically reasonable way.

Test Plan:
In bare and non-bare repositories:

  - Ran discovery with `bin/repository discover`;
  - listed branches on `/diffusion/X/`;
  - listed tags on `/diffusion/X/`;
  - listed tags, branches and refs on `/diffusion/rXnnnn`.

Reviewers: btrahan, avivey

Reviewed By: avivey

CC: aran

Maniphest Tasks: T2230

Differential Revision: https://secure.phabricator.com/D7447
This commit is contained in:
epriestley 2013-10-30 13:06:09 -07:00
parent 7360688afb
commit 3bf372c60c
8 changed files with 236 additions and 77 deletions

View file

@ -491,6 +491,8 @@ phutil_register_library_map(array(
'DiffusionLintController' => 'applications/diffusion/controller/DiffusionLintController.php',
'DiffusionLintDetailsController' => 'applications/diffusion/controller/DiffusionLintDetailsController.php',
'DiffusionLintSaveRunner' => 'applications/diffusion/DiffusionLintSaveRunner.php',
'DiffusionLowLevelGitRefQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php',
'DiffusionLowLevelQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php',
'DiffusionMercurialCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php',
'DiffusionMercurialExpandShortNameQuery' => 'applications/diffusion/query/expandshortname/DiffusionMercurialExpandShortNameQuery.php',
'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php',
@ -525,6 +527,7 @@ phutil_register_library_map(array(
'DiffusionRepositoryEditSubversionController' => 'applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php',
'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php',
'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
'DiffusionRepositoryRef' => 'applications/diffusion/data/DiffusionRepositoryRef.php',
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php',
'DiffusionSSHGitReceivePackWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php',
@ -2684,6 +2687,8 @@ phutil_register_library_map(array(
'DiffusionLastModifiedController' => 'DiffusionController',
'DiffusionLintController' => 'DiffusionController',
'DiffusionLintDetailsController' => 'DiffusionController',
'DiffusionLowLevelGitRefQuery' => 'DiffusionLowLevelQuery',
'DiffusionLowLevelQuery' => 'Phobject',
'DiffusionMercurialCommitParentsQuery' => 'DiffusionCommitParentsQuery',
'DiffusionMercurialExpandShortNameQuery' => 'DiffusionExpandShortNameQuery',
'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery',

View file

@ -27,44 +27,31 @@ final class ConduitAPI_diffusion_branchquery_Method
$limit = $request->getValue('limit');
$offset = $request->getValue('offset');
// We need to add 1 in case we pick up HEAD.
$count = $offset + $limit + 1;
if ($repository->isWorkingCopyBare()) {
list($stdout) = $repository->execxLocalCommand(
'for-each-ref %C --sort=-creatordate --format=%s refs/heads',
$count ? '--count='.(int)$count : null,
'%(refname:short) %(objectname)');
$branch_list = DiffusionGitBranch::parseLocalBranchOutput(
$stdout);
} else {
list($stdout) = $repository->execxLocalCommand(
'for-each-ref %C --sort=-creatordate --format=%s refs/remotes',
$count ? '--count='.(int)$count : null,
'%(refname:short) %(objectname)');
$branch_list = DiffusionGitBranch::parseRemoteBranchOutput(
$stdout,
$only_this_remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE);
}
$refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsOriginBranch(true)
->execute();
$branches = array();
foreach ($branch_list as $name => $head) {
if (!$repository->shouldTrackBranch($name)) {
foreach ($refs as $ref) {
$branch = id(new DiffusionBranchInformation())
->setName($ref->getShortName())
->setHeadCommitIdentifier($ref->getCommitIdentifier());
if (!$repository->shouldTrackBranch($branch->getName())) {
continue;
}
$branch = new DiffusionBranchInformation();
$branch->setName($name);
$branch->setHeadCommitIdentifier($head);
$branches[] = $branch->toDictionary();
}
// NOTE: We can't apply the offset or limit until here, because we may have
// filtered untrackable branches out of the result set.
if ($offset) {
$branches = array_slice($branches, $offset);
}
// We might have too many even after offset slicing, if there was no HEAD
// for some reason.
if ($limit) {
$branches = array_slice($branches, 0, $limit);
}

View file

@ -25,6 +25,8 @@ final class ConduitAPI_diffusion_commitbranchesquery_Method
$repository = $drequest->getRepository();
$commit = $request->getValue('commit');
// NOTE: We can't use DiffusionLowLevelGitRefQuery here because
// `git for-each-ref` does not support `--contains`.
if ($repository->isWorkingCopyBare()) {
list($contains) = $repository->execxLocalCommand(
'branch --verbose --no-abbrev --contains %s',

View file

@ -75,47 +75,21 @@ final class ConduitAPI_diffusion_tagsquery_Method
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$count = $offset + $limit;
list($stdout) = $repository->execxLocalCommand(
'for-each-ref %C --sort=-creatordate --format=%s refs/tags',
$count ? '--count='.(int)$count : null,
'%(objectname) %(objecttype) %(refname) %(*objectname) %(*objecttype) '.
'%(subject)%01%(creator)');
$stdout = trim($stdout);
if (!strlen($stdout)) {
return array();
}
$refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsTag(true)
->execute();
$tags = array();
foreach (explode("\n", $stdout) as $line) {
list($info, $creator) = explode("\1", $line);
list(
$objectname,
$objecttype,
$refname,
$refobjectname,
$refobjecttype,
$description) = explode(' ', $info, 6);
$matches = null;
if (!preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) {
// It's possible a tag doesn't have a creator (tagger)
$author = null;
$epoch = null;
} else {
$author = $matches[1];
$epoch = $matches[2];
}
$tag = new DiffusionRepositoryTag();
$tag->setAuthor($author);
$tag->setEpoch($epoch);
$tag->setCommitIdentifier(nonempty($refobjectname, $objectname));
$tag->setName(preg_replace('@^refs/tags/@', '', $refname));
$tag->setDescription($description);
$tag->setType('git/'.$objecttype);
foreach ($refs as $ref) {
$fields = $ref->getRawFields();
$tag = id(new DiffusionRepositoryTag())
->setAuthor($fields['author'])
->setEpoch($fields['epoch'])
->setCommitIdentifier($ref->getCommitIdentifier())
->setName($ref->getShortName())
->setDescription($fields['subject'])
->setType('git/'.$fields['objecttype']);
$tags[] = $tag;
}
@ -124,6 +98,10 @@ final class ConduitAPI_diffusion_tagsquery_Method
$tags = array_slice($tags, $offset);
}
if ($limit) {
$tags = array_slice($tags, 0, $limit);
}
if ($serialize) {
$tags = mpull($tags, 'toDictionary');
}

View file

@ -0,0 +1,36 @@
<?php
final class DiffusionRepositoryRef {
private $shortName;
private $commitIdentifier;
private $rawFields;
public function setRawFields(array $raw_fields) {
$this->rawFields = $raw_fields;
return $this;
}
public function getRawFields() {
return $this->rawFields;
}
public function setCommitIdentifier($commit_identifier) {
$this->commitIdentifier = $commit_identifier;
return $this;
}
public function getCommitIdentifier() {
return $this->commitIdentifier;
}
public function setShortName($short_name) {
$this->shortName = $short_name;
return $this;
}
public function getShortName() {
return $this->shortName;
}
}

View file

@ -0,0 +1,135 @@
<?php
/**
* Execute and parse a low-level Git ref query using `git for-each-ref`. This
* is useful for returning a list of tags or branches.
*
*
*/
final class DiffusionLowLevelGitRefQuery extends DiffusionLowLevelQuery {
private $isTag;
private $isOriginBranch;
public function withIsTag($is_tag) {
$this->isTag = $is_tag;
return $this;
}
public function withIsOriginBranch($is_origin_branch) {
$this->isOriginBranch = $is_origin_branch;
return $this;
}
protected function executeQuery() {
$repository = $this->getRepository();
if ($this->isTag && $this->isOriginBranch) {
throw new Exception("Specify tags or origin branches, not both!");
} else if ($this->isTag) {
$prefix = 'refs/tags/';
} else if ($this->isOriginBranch) {
if ($repository->isWorkingCopyBare()) {
$prefix = 'refs/heads/';
} else {
$remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE;
$prefix = 'refs/remotes/'.$remote.'/';
}
} else {
throw new Exception("Specify tags or origin branches!");
}
$order = '-creatordate';
list($stdout) = $repository->execxLocalCommand(
'for-each-ref --sort=%s --format=%s %s',
$order,
$this->getFormatString(),
$prefix);
$stdout = rtrim($stdout);
if (!strlen($stdout)) {
return array();
}
// NOTE: Although git supports --count, we can't apply any offset or limit
// logic until the very end because we may encounter a HEAD which we want
// to discard.
$lines = explode("\n", $stdout);
$results = array();
foreach ($lines as $line) {
$fields = $this->extractFields($line);
$creator = $fields['creator'];
$matches = null;
if (preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) {
$fields['author'] = $matches[1];
$fields['epoch'] = (int)$matches[2];
} else {
$fields['author'] = null;
$fields['epoch'] = null;
}
$commit = nonempty($fields['*objectname'], $fields['objectname']);
$short = substr($fields['refname'], strlen($prefix));
if ($short == 'HEAD') {
continue;
}
$ref = id(new DiffusionRepositoryRef())
->setShortName($short)
->setCommitIdentifier($commit)
->setRawFields($fields);
$results[] = $ref;
}
return $results;
}
/**
* List of git `--format` fields we want to grab.
*/
private function getFields() {
return array(
'objectname',
'objecttype',
'refname',
'*objectname',
'*objecttype',
'subject',
'creator',
);
}
/**
* Get a string for `--format` which enumerates all the fields we want.
*/
private function getFormatString() {
$fields = $this->getFields();
foreach ($fields as $key => $field) {
$fields[$key] = '%('.$field.')';
}
return implode("%01", $fields);
}
/**
* Parse a line back into fields.
*/
private function extractFields($line) {
$fields = $this->getFields();
$parts = explode("\1", $line, count($fields));
$dict = array();
foreach ($fields as $index => $field) {
$dict[$field] = idx($parts, $index);
}
return $dict;
}
}

View file

@ -0,0 +1,22 @@
<?php
abstract class DiffusionLowLevelQuery extends Phobject {
private $repository;
abstract protected function executeQuery();
public function setRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function getRepository() {
return $this->repository;
}
public function execute() {
return $this->executeQuery();
}
}

View file

@ -526,18 +526,12 @@ final class PhabricatorRepositoryPullLocalDaemon
$repository->getRemoteURI(),
$repository->getLocalPath());
if ($repository->isWorkingCopyBare()) {
list($stdout) = $repository->execxLocalCommand(
'branch --verbose --no-abbrev');
$branches = DiffusionGitBranch::parseLocalBranchOutput(
$stdout);
} else {
list($stdout) = $repository->execxLocalCommand(
'branch -r --verbose --no-abbrev');
$branches = DiffusionGitBranch::parseRemoteBranchOutput(
$stdout,
$only_this_remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE);
}
$refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsOriginBranch(true)
->execute();
$branches = mpull($refs, 'getCommitIdentifier', 'getShortName');
if (!$branches) {
// This repository has no branches at all, so we don't need to do