@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+236 -77
+5
src/__phutil_library_map__.php
··· 491 491 'DiffusionLintController' => 'applications/diffusion/controller/DiffusionLintController.php', 492 492 'DiffusionLintDetailsController' => 'applications/diffusion/controller/DiffusionLintDetailsController.php', 493 493 'DiffusionLintSaveRunner' => 'applications/diffusion/DiffusionLintSaveRunner.php', 494 + 'DiffusionLowLevelGitRefQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php', 495 + 'DiffusionLowLevelQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php', 494 496 'DiffusionMercurialCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php', 495 497 'DiffusionMercurialExpandShortNameQuery' => 'applications/diffusion/query/expandshortname/DiffusionMercurialExpandShortNameQuery.php', 496 498 'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', ··· 525 527 'DiffusionRepositoryEditSubversionController' => 'applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php', 526 528 'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php', 527 529 'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php', 530 + 'DiffusionRepositoryRef' => 'applications/diffusion/data/DiffusionRepositoryRef.php', 528 531 'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php', 529 532 'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php', 530 533 'DiffusionSSHGitReceivePackWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitReceivePackWorkflow.php', ··· 2684 2687 'DiffusionLastModifiedController' => 'DiffusionController', 2685 2688 'DiffusionLintController' => 'DiffusionController', 2686 2689 'DiffusionLintDetailsController' => 'DiffusionController', 2690 + 'DiffusionLowLevelGitRefQuery' => 'DiffusionLowLevelQuery', 2691 + 'DiffusionLowLevelQuery' => 'Phobject', 2687 2692 'DiffusionMercurialCommitParentsQuery' => 'DiffusionCommitParentsQuery', 2688 2693 'DiffusionMercurialExpandShortNameQuery' => 'DiffusionExpandShortNameQuery', 2689 2694 'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery',
+13 -26
src/applications/diffusion/conduit/ConduitAPI_diffusion_branchquery_Method.php
··· 27 27 $limit = $request->getValue('limit'); 28 28 $offset = $request->getValue('offset'); 29 29 30 - // We need to add 1 in case we pick up HEAD. 31 - $count = $offset + $limit + 1; 30 + $refs = id(new DiffusionLowLevelGitRefQuery()) 31 + ->setRepository($repository) 32 + ->withIsOriginBranch(true) 33 + ->execute(); 32 34 33 - if ($repository->isWorkingCopyBare()) { 34 - list($stdout) = $repository->execxLocalCommand( 35 - 'for-each-ref %C --sort=-creatordate --format=%s refs/heads', 36 - $count ? '--count='.(int)$count : null, 37 - '%(refname:short) %(objectname)'); 38 - $branch_list = DiffusionGitBranch::parseLocalBranchOutput( 39 - $stdout); 40 - } else { 41 - list($stdout) = $repository->execxLocalCommand( 42 - 'for-each-ref %C --sort=-creatordate --format=%s refs/remotes', 43 - $count ? '--count='.(int)$count : null, 44 - '%(refname:short) %(objectname)'); 45 - $branch_list = DiffusionGitBranch::parseRemoteBranchOutput( 46 - $stdout, 47 - $only_this_remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE); 48 - } 35 + $branches = array(); 36 + foreach ($refs as $ref) { 37 + $branch = id(new DiffusionBranchInformation()) 38 + ->setName($ref->getShortName()) 39 + ->setHeadCommitIdentifier($ref->getCommitIdentifier()); 49 40 50 - $branches = array(); 51 - foreach ($branch_list as $name => $head) { 52 - if (!$repository->shouldTrackBranch($name)) { 41 + if (!$repository->shouldTrackBranch($branch->getName())) { 53 42 continue; 54 43 } 55 44 56 - $branch = new DiffusionBranchInformation(); 57 - $branch->setName($name); 58 - $branch->setHeadCommitIdentifier($head); 59 45 $branches[] = $branch->toDictionary(); 60 46 } 61 47 48 + // NOTE: We can't apply the offset or limit until here, because we may have 49 + // filtered untrackable branches out of the result set. 50 + 62 51 if ($offset) { 63 52 $branches = array_slice($branches, $offset); 64 53 } 65 54 66 - // We might have too many even after offset slicing, if there was no HEAD 67 - // for some reason. 68 55 if ($limit) { 69 56 $branches = array_slice($branches, 0, $limit); 70 57 }
+2
src/applications/diffusion/conduit/ConduitAPI_diffusion_commitbranchesquery_Method.php
··· 25 25 $repository = $drequest->getRepository(); 26 26 $commit = $request->getValue('commit'); 27 27 28 + // NOTE: We can't use DiffusionLowLevelGitRefQuery here because 29 + // `git for-each-ref` does not support `--contains`. 28 30 if ($repository->isWorkingCopyBare()) { 29 31 list($contains) = $repository->execxLocalCommand( 30 32 'branch --verbose --no-abbrev --contains %s',
+17 -39
src/applications/diffusion/conduit/ConduitAPI_diffusion_tagsquery_Method.php
··· 75 75 $drequest = $this->getDiffusionRequest(); 76 76 $repository = $drequest->getRepository(); 77 77 78 - $count = $offset + $limit; 79 - 80 - list($stdout) = $repository->execxLocalCommand( 81 - 'for-each-ref %C --sort=-creatordate --format=%s refs/tags', 82 - $count ? '--count='.(int)$count : null, 83 - '%(objectname) %(objecttype) %(refname) %(*objectname) %(*objecttype) '. 84 - '%(subject)%01%(creator)'); 85 - 86 - $stdout = trim($stdout); 87 - if (!strlen($stdout)) { 88 - return array(); 89 - } 78 + $refs = id(new DiffusionLowLevelGitRefQuery()) 79 + ->setRepository($repository) 80 + ->withIsTag(true) 81 + ->execute(); 90 82 91 83 $tags = array(); 92 - foreach (explode("\n", $stdout) as $line) { 93 - list($info, $creator) = explode("\1", $line); 94 - list( 95 - $objectname, 96 - $objecttype, 97 - $refname, 98 - $refobjectname, 99 - $refobjecttype, 100 - $description) = explode(' ', $info, 6); 101 - 102 - $matches = null; 103 - if (!preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) { 104 - // It's possible a tag doesn't have a creator (tagger) 105 - $author = null; 106 - $epoch = null; 107 - } else { 108 - $author = $matches[1]; 109 - $epoch = $matches[2]; 110 - } 111 - 112 - $tag = new DiffusionRepositoryTag(); 113 - $tag->setAuthor($author); 114 - $tag->setEpoch($epoch); 115 - $tag->setCommitIdentifier(nonempty($refobjectname, $objectname)); 116 - $tag->setName(preg_replace('@^refs/tags/@', '', $refname)); 117 - $tag->setDescription($description); 118 - $tag->setType('git/'.$objecttype); 84 + foreach ($refs as $ref) { 85 + $fields = $ref->getRawFields(); 86 + $tag = id(new DiffusionRepositoryTag()) 87 + ->setAuthor($fields['author']) 88 + ->setEpoch($fields['epoch']) 89 + ->setCommitIdentifier($ref->getCommitIdentifier()) 90 + ->setName($ref->getShortName()) 91 + ->setDescription($fields['subject']) 92 + ->setType('git/'.$fields['objecttype']); 119 93 120 94 $tags[] = $tag; 121 95 } 122 96 123 97 if ($offset) { 124 98 $tags = array_slice($tags, $offset); 99 + } 100 + 101 + if ($limit) { 102 + $tags = array_slice($tags, 0, $limit); 125 103 } 126 104 127 105 if ($serialize) {
+36
src/applications/diffusion/data/DiffusionRepositoryRef.php
··· 1 + <?php 2 + 3 + final class DiffusionRepositoryRef { 4 + 5 + private $shortName; 6 + private $commitIdentifier; 7 + private $rawFields; 8 + 9 + public function setRawFields(array $raw_fields) { 10 + $this->rawFields = $raw_fields; 11 + return $this; 12 + } 13 + 14 + public function getRawFields() { 15 + return $this->rawFields; 16 + } 17 + 18 + public function setCommitIdentifier($commit_identifier) { 19 + $this->commitIdentifier = $commit_identifier; 20 + return $this; 21 + } 22 + 23 + public function getCommitIdentifier() { 24 + return $this->commitIdentifier; 25 + } 26 + 27 + public function setShortName($short_name) { 28 + $this->shortName = $short_name; 29 + return $this; 30 + } 31 + 32 + public function getShortName() { 33 + return $this->shortName; 34 + } 35 + 36 + }
+135
src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php
··· 1 + <?php 2 + 3 + /** 4 + * Execute and parse a low-level Git ref query using `git for-each-ref`. This 5 + * is useful for returning a list of tags or branches. 6 + * 7 + * 8 + */ 9 + final class DiffusionLowLevelGitRefQuery extends DiffusionLowLevelQuery { 10 + 11 + private $isTag; 12 + private $isOriginBranch; 13 + 14 + public function withIsTag($is_tag) { 15 + $this->isTag = $is_tag; 16 + return $this; 17 + } 18 + 19 + public function withIsOriginBranch($is_origin_branch) { 20 + $this->isOriginBranch = $is_origin_branch; 21 + return $this; 22 + } 23 + 24 + protected function executeQuery() { 25 + $repository = $this->getRepository(); 26 + 27 + if ($this->isTag && $this->isOriginBranch) { 28 + throw new Exception("Specify tags or origin branches, not both!"); 29 + } else if ($this->isTag) { 30 + $prefix = 'refs/tags/'; 31 + } else if ($this->isOriginBranch) { 32 + if ($repository->isWorkingCopyBare()) { 33 + $prefix = 'refs/heads/'; 34 + } else { 35 + $remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE; 36 + $prefix = 'refs/remotes/'.$remote.'/'; 37 + } 38 + } else { 39 + throw new Exception("Specify tags or origin branches!"); 40 + } 41 + 42 + $order = '-creatordate'; 43 + 44 + list($stdout) = $repository->execxLocalCommand( 45 + 'for-each-ref --sort=%s --format=%s %s', 46 + $order, 47 + $this->getFormatString(), 48 + $prefix); 49 + 50 + $stdout = rtrim($stdout); 51 + if (!strlen($stdout)) { 52 + return array(); 53 + } 54 + 55 + // NOTE: Although git supports --count, we can't apply any offset or limit 56 + // logic until the very end because we may encounter a HEAD which we want 57 + // to discard. 58 + 59 + $lines = explode("\n", $stdout); 60 + $results = array(); 61 + foreach ($lines as $line) { 62 + $fields = $this->extractFields($line); 63 + 64 + $creator = $fields['creator']; 65 + $matches = null; 66 + if (preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) { 67 + $fields['author'] = $matches[1]; 68 + $fields['epoch'] = (int)$matches[2]; 69 + } else { 70 + $fields['author'] = null; 71 + $fields['epoch'] = null; 72 + } 73 + 74 + $commit = nonempty($fields['*objectname'], $fields['objectname']); 75 + 76 + $short = substr($fields['refname'], strlen($prefix)); 77 + if ($short == 'HEAD') { 78 + continue; 79 + } 80 + 81 + $ref = id(new DiffusionRepositoryRef()) 82 + ->setShortName($short) 83 + ->setCommitIdentifier($commit) 84 + ->setRawFields($fields); 85 + 86 + $results[] = $ref; 87 + } 88 + 89 + return $results; 90 + } 91 + 92 + /** 93 + * List of git `--format` fields we want to grab. 94 + */ 95 + private function getFields() { 96 + return array( 97 + 'objectname', 98 + 'objecttype', 99 + 'refname', 100 + '*objectname', 101 + '*objecttype', 102 + 'subject', 103 + 'creator', 104 + ); 105 + } 106 + 107 + /** 108 + * Get a string for `--format` which enumerates all the fields we want. 109 + */ 110 + private function getFormatString() { 111 + $fields = $this->getFields(); 112 + 113 + foreach ($fields as $key => $field) { 114 + $fields[$key] = '%('.$field.')'; 115 + } 116 + 117 + return implode("%01", $fields); 118 + } 119 + 120 + /** 121 + * Parse a line back into fields. 122 + */ 123 + private function extractFields($line) { 124 + $fields = $this->getFields(); 125 + $parts = explode("\1", $line, count($fields)); 126 + 127 + $dict = array(); 128 + foreach ($fields as $index => $field) { 129 + $dict[$field] = idx($parts, $index); 130 + } 131 + 132 + return $dict; 133 + } 134 + 135 + }
+22
src/applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php
··· 1 + <?php 2 + 3 + abstract class DiffusionLowLevelQuery extends Phobject { 4 + 5 + private $repository; 6 + 7 + abstract protected function executeQuery(); 8 + 9 + public function setRepository(PhabricatorRepository $repository) { 10 + $this->repository = $repository; 11 + return $this; 12 + } 13 + 14 + public function getRepository() { 15 + return $this->repository; 16 + } 17 + 18 + public function execute() { 19 + return $this->executeQuery(); 20 + } 21 + 22 + }
+6 -12
src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
··· 526 526 $repository->getRemoteURI(), 527 527 $repository->getLocalPath()); 528 528 529 - if ($repository->isWorkingCopyBare()) { 530 - list($stdout) = $repository->execxLocalCommand( 531 - 'branch --verbose --no-abbrev'); 532 - $branches = DiffusionGitBranch::parseLocalBranchOutput( 533 - $stdout); 534 - } else { 535 - list($stdout) = $repository->execxLocalCommand( 536 - 'branch -r --verbose --no-abbrev'); 537 - $branches = DiffusionGitBranch::parseRemoteBranchOutput( 538 - $stdout, 539 - $only_this_remote = DiffusionBranchInformation::DEFAULT_GIT_REMOTE); 540 - } 529 + $refs = id(new DiffusionLowLevelGitRefQuery()) 530 + ->setRepository($repository) 531 + ->withIsOriginBranch(true) 532 + ->execute(); 533 + 534 + $branches = mpull($refs, 'getCommitIdentifier', 'getShortName'); 541 535 542 536 if (!$branches) { 543 537 // This repository has no branches at all, so we don't need to do