@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.

Improve ref resolution for Git branches and tags

Summary:
Fixes T7982.

- When resolving branches, make sure they get type `'branch'`.
- Correctly resolve refs when a repository has a branch and tag with the same name.

Test Plan: Disabled ref cache and resolved refs in a Git repository with a 'master' tag and a 'master' branch. Saw refs resolve accurately.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7982

Differential Revision: https://secure.phabricator.com/D12609

+114 -61
+59 -42
src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php
··· 3 3 /** 4 4 * Execute and parse a low-level Git ref query using `git for-each-ref`. This 5 5 * is useful for returning a list of tags or branches. 6 - * 7 - * 8 6 */ 9 7 final class DiffusionLowLevelGitRefQuery extends DiffusionLowLevelQuery { 10 8 ··· 24 22 protected function executeQuery() { 25 23 $repository = $this->getRepository(); 26 24 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) { 25 + $prefixes = array(); 26 + 27 + $any = ($this->isTag || $this->isOriginBranch); 28 + if (!$any) { 29 + throw new Exception(pht('Specify types of refs to query.')); 30 + } 31 + 32 + if ($this->isOriginBranch) { 32 33 if ($repository->isWorkingCopyBare()) { 33 34 $prefix = 'refs/heads/'; 34 35 } else { 35 36 $remote = DiffusionGitBranch::DEFAULT_GIT_REMOTE; 36 37 $prefix = 'refs/remotes/'.$remote.'/'; 37 38 } 38 - } else { 39 - throw new Exception('Specify tags or origin branches!'); 39 + $prefixes[] = $prefix; 40 + } 41 + 42 + if ($this->isTag) { 43 + $prefixes[] = 'refs/tags/'; 40 44 } 41 45 42 46 $order = '-creatordate'; 43 47 44 - list($stdout) = $repository->execxLocalCommand( 45 - 'for-each-ref --sort=%s --format=%s %s', 46 - $order, 47 - $this->getFormatString(), 48 - $prefix); 48 + $futures = array(); 49 + foreach ($prefixes as $prefix) { 50 + $futures[$prefix] = $repository->getLocalCommandFuture( 51 + 'for-each-ref --sort=%s --format=%s %s', 52 + $order, 53 + $this->getFormatString(), 54 + $prefix); 55 + } 49 56 50 - $stdout = rtrim($stdout); 51 - if (!strlen($stdout)) { 52 - return array(); 57 + // Resolve all the futures first. We want to iterate over them in prefix 58 + // order, not resolution order. 59 + foreach (new FutureIterator($futures) as $prefix => $future) { 60 + $future->resolvex(); 53 61 } 54 62 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 63 $results = array(); 61 - foreach ($lines as $line) { 62 - $fields = $this->extractFields($line); 64 + foreach ($futures as $prefix => $future) { 65 + list($stdout) = $future->resolvex(); 63 66 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; 67 + $stdout = rtrim($stdout); 68 + if (!strlen($stdout)) { 69 + continue; 72 70 } 73 71 74 - $commit = nonempty($fields['*objectname'], $fields['objectname']); 72 + // NOTE: Although git supports --count, we can't apply any offset or 73 + // limit logic until the very end because we may encounter a HEAD which 74 + // we want to discard. 75 75 76 - $short = substr($fields['refname'], strlen($prefix)); 77 - if ($short == 'HEAD') { 78 - continue; 79 - } 76 + $lines = explode("\n", $stdout); 77 + foreach ($lines as $line) { 78 + $fields = $this->extractFields($line); 80 79 81 - $ref = id(new DiffusionRepositoryRef()) 82 - ->setShortName($short) 83 - ->setCommitIdentifier($commit) 84 - ->setRawFields($fields); 80 + $creator = $fields['creator']; 81 + $matches = null; 82 + if (preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) { 83 + $fields['author'] = $matches[1]; 84 + $fields['epoch'] = (int)$matches[2]; 85 + } else { 86 + $fields['author'] = null; 87 + $fields['epoch'] = null; 88 + } 89 + 90 + $commit = nonempty($fields['*objectname'], $fields['objectname']); 91 + 92 + $short = substr($fields['refname'], strlen($prefix)); 93 + if ($short == 'HEAD') { 94 + continue; 95 + } 85 96 86 - $results[] = $ref; 97 + $ref = id(new DiffusionRepositoryRef()) 98 + ->setShortName($short) 99 + ->setCommitIdentifier($commit) 100 + ->setRawFields($fields); 101 + 102 + $results[] = $ref; 103 + } 87 104 } 88 105 89 106 return $results;
+54 -5
src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
··· 55 55 private function resolveGitRefs() { 56 56 $repository = $this->getRepository(); 57 57 58 - // TODO: When refs are ambiguous (for example, tags and branches with 59 - // the same name) this will only resolve one of them. 58 + $unresolved = array_fuse($this->refs); 59 + $results = array(); 60 + 61 + // First, resolve branches and tags. 62 + $ref_map = id(new DiffusionLowLevelGitRefQuery()) 63 + ->setRepository($repository) 64 + ->withIsTag(true) 65 + ->withIsOriginBranch(true) 66 + ->execute(); 67 + $ref_map = mgroup($ref_map, 'getShortName'); 68 + 69 + $tag_prefix = 'refs/tags/'; 70 + foreach ($unresolved as $ref) { 71 + if (empty($ref_map[$ref])) { 72 + continue; 73 + } 74 + 75 + foreach ($ref_map[$ref] as $result) { 76 + $fields = $result->getRawFields(); 77 + $objectname = idx($fields, 'refname'); 78 + if (!strncmp($objectname, $tag_prefix, strlen($tag_prefix))) { 79 + $type = 'tag'; 80 + } else { 81 + $type = 'branch'; 82 + } 83 + 84 + $info = array( 85 + 'type' => $type, 86 + 'identifier' => $result->getCommitIdentifier(), 87 + ); 88 + 89 + if ($type == 'tag') { 90 + $alternate = idx($fields, 'objectname'); 91 + if ($alternate) { 92 + $info['alternate'] = $alternate; 93 + } 94 + } 95 + 96 + $results[$ref][] = $info; 97 + } 98 + 99 + unset($unresolved[$ref]); 100 + } 101 + 102 + // If we resolved everything, we're done. 103 + if (!$unresolved) { 104 + return $results; 105 + } 106 + 107 + // Try to resolve anything else. This stuff either doesn't exist or is 108 + // some ref like "HEAD^^^". 60 109 $future = $repository->getLocalCommandFuture('cat-file --batch-check'); 61 - $future->write(implode("\n", $this->refs)); 110 + $future->write(implode("\n", $unresolved)); 62 111 list($stdout) = $future->resolvex(); 63 112 64 113 $lines = explode("\n", rtrim($stdout, "\n")); 65 - if (count($lines) !== count($this->refs)) { 114 + if (count($lines) !== count($unresolved)) { 66 115 throw new Exception('Unexpected line count from `git cat-file`!'); 67 116 } 68 117 69 118 $hits = array(); 70 119 $tags = array(); 71 120 72 - $lines = array_combine($this->refs, $lines); 121 + $lines = array_combine($unresolved, $lines); 73 122 foreach ($lines as $ref => $line) { 74 123 $parts = explode(' ', $line); 75 124 if (count($parts) < 2) {
-9
src/applications/diffusion/request/DiffusionGitRequest.php
··· 20 20 throw new Exception('Unable to determine branch!'); 21 21 } 22 22 23 - protected function getResolvableBranchName($branch) { 24 - if ($this->repository->isWorkingCopyBare()) { 25 - return $branch; 26 - } else { 27 - $remote = DiffusionGitBranch::DEFAULT_GIT_REMOTE; 28 - return $remote.'/'.$branch; 29 - } 30 - } 31 - 32 23 }
+1 -5
src/applications/diffusion/request/DiffusionRequest.php
··· 723 723 $ref = $this->symbolicCommit; 724 724 } else { 725 725 if ($this->supportsBranches()) { 726 - $ref = $this->getResolvableBranchName($this->getBranch()); 726 + $ref = $this->getBranch(); 727 727 $types = array( 728 728 PhabricatorRepositoryRefCursor::TYPE_BRANCH, 729 729 ); ··· 793 793 } 794 794 795 795 return $match; 796 - } 797 - 798 - protected function getResolvableBranchName($branch) { 799 - return $branch; 800 796 } 801 797 802 798 private function resolveRefs(array $refs, array $types) {