@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
at upstream/main 173 lines 4.6 kB view raw
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 */ 7final class DiffusionLowLevelGitRefQuery extends DiffusionLowLevelQuery { 8 9 private $refTypes; 10 11 public function withRefTypes(array $ref_types) { 12 $this->refTypes = $ref_types; 13 return $this; 14 } 15 16 protected function executeQuery() { 17 $type_branch = PhabricatorRepositoryRefCursor::TYPE_BRANCH; 18 $type_tag = PhabricatorRepositoryRefCursor::TYPE_TAG; 19 $type_ref = PhabricatorRepositoryRefCursor::TYPE_REF; 20 21 $ref_types = $this->refTypes; 22 if (!$ref_types) { 23 $ref_types = array($type_branch, $type_tag, $type_ref); 24 } 25 26 $ref_types = array_fuse($ref_types); 27 28 $with_branches = isset($ref_types[$type_branch]); 29 $with_tags = isset($ref_types[$type_tag]); 30 $with_refs = isset($ref_types[$type_ref]); 31 32 $repository = $this->getRepository(); 33 34 $prefixes = array(); 35 36 $branch_prefix = 'refs/heads/'; 37 $tag_prefix = 'refs/tags/'; 38 39 if ($with_refs || count($ref_types) > 1) { 40 // If we're loading refs or more than one type of ref, just query 41 // everything. 42 $prefix = 'refs/'; 43 } else { 44 if ($with_branches) { 45 $prefix = $branch_prefix; 46 } 47 if ($with_tags) { 48 $prefix = $tag_prefix; 49 } 50 } 51 52 $branch_len = strlen($branch_prefix); 53 $tag_len = strlen($tag_prefix); 54 55 list($stdout) = $repository->execxLocalCommand( 56 'for-each-ref --sort=%s --format=%s -- %s', 57 '-creatordate', 58 $this->getFormatString(), 59 $prefix); 60 61 $stdout = rtrim($stdout); 62 if (!strlen($stdout)) { 63 return array(); 64 } 65 66 $remote_prefix = 'refs/remotes/'; 67 $remote_len = strlen($remote_prefix); 68 69 // NOTE: Although git supports --count, we can't apply any offset or 70 // limit logic until the very end because we may encounter a HEAD which 71 // we want to discard. 72 73 $lines = explode("\n", $stdout); 74 $results = array(); 75 foreach ($lines as $line) { 76 $fields = $this->extractFields($line); 77 78 $refname = $fields['refname']; 79 if (!strncmp($refname, $branch_prefix, $branch_len)) { 80 $short = substr($refname, $branch_len); 81 $type = $type_branch; 82 } else if (!strncmp($refname, $tag_prefix, $tag_len)) { 83 $short = substr($refname, $tag_len); 84 $type = $type_tag; 85 } else if (!strncmp($refname, $remote_prefix, $remote_len)) { 86 // If we've found a remote ref that we didn't recognize as naming a 87 // branch, just ignore it. This can happen if we're observing a remote, 88 // and that remote has its own remotes. We don't care about their 89 // state and they may be out of date, so ignore them. 90 continue; 91 } else { 92 $short = $refname; 93 $type = $type_ref; 94 } 95 96 // If this isn't a type of ref we care about, skip it. 97 if (empty($ref_types[$type])) { 98 continue; 99 } 100 101 // If this is the local HEAD, skip it. 102 if ($short == 'HEAD') { 103 continue; 104 } 105 106 $creator = $fields['creator']; 107 $matches = null; 108 if (preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) { 109 $fields['author'] = $matches[1]; 110 $fields['epoch'] = (int)$matches[2]; 111 } else { 112 $fields['author'] = null; 113 $fields['epoch'] = null; 114 } 115 116 $commit = nonempty($fields['*objectname'], $fields['objectname']); 117 118 $ref = id(new DiffusionRepositoryRef()) 119 ->setRefType($type) 120 ->setShortName($short) 121 ->setCommitIdentifier($commit) 122 ->setRawFields($fields); 123 124 $results[] = $ref; 125 } 126 127 return $results; 128 } 129 130 /** 131 * List of git `--format` fields we want to grab. 132 */ 133 private function getFields() { 134 return array( 135 'objectname', 136 'objecttype', 137 'refname', 138 '*objectname', 139 '*objecttype', 140 'subject', 141 'creator', 142 ); 143 } 144 145 /** 146 * Get a string for `--format` which enumerates all the fields we want. 147 */ 148 private function getFormatString() { 149 $fields = $this->getFields(); 150 151 foreach ($fields as $key => $field) { 152 $fields[$key] = '%('.$field.')'; 153 } 154 155 return implode('%01', $fields); 156 } 157 158 /** 159 * Parse a line back into fields. 160 */ 161 private function extractFields($line) { 162 $fields = $this->getFields(); 163 $parts = explode("\1", $line, count($fields)); 164 165 $dict = array(); 166 foreach ($fields as $index => $field) { 167 $dict[$field] = idx($parts, $index); 168 } 169 170 return $dict; 171 } 172 173}