Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / diffusion / query / lowlevel / DiffusionLowLevelGitRefQuery.php
blobd11b658131cb2c27d9fe31e8f2994285f1b1254c
1 <?php
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 final class DiffusionLowLevelGitRefQuery extends DiffusionLowLevelQuery {
9 private $refTypes;
11 public function withRefTypes(array $ref_types) {
12 $this->refTypes = $ref_types;
13 return $this;
16 protected function executeQuery() {
17 $type_branch = PhabricatorRepositoryRefCursor::TYPE_BRANCH;
18 $type_tag = PhabricatorRepositoryRefCursor::TYPE_TAG;
19 $type_ref = PhabricatorRepositoryRefCursor::TYPE_REF;
21 $ref_types = $this->refTypes;
22 if (!$ref_types) {
23 $ref_types = array($type_branch, $type_tag, $type_ref);
26 $ref_types = array_fuse($ref_types);
28 $with_branches = isset($ref_types[$type_branch]);
29 $with_tags = isset($ref_types[$type_tag]);
30 $with_refs = isset($refs_types[$type_ref]);
32 $repository = $this->getRepository();
34 $prefixes = array();
36 $branch_prefix = 'refs/heads/';
37 $tag_prefix = 'refs/tags/';
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;
47 if ($with_tags) {
48 $prefix = $tag_prefix;
52 $branch_len = strlen($branch_prefix);
53 $tag_len = strlen($tag_prefix);
55 list($stdout) = $repository->execxLocalCommand(
56 'for-each-ref --sort=%s --format=%s -- %s',
57 '-creatordate',
58 $this->getFormatString(),
59 $prefix);
61 $stdout = rtrim($stdout);
62 if (!strlen($stdout)) {
63 return array();
66 $remote_prefix = 'refs/remotes/';
67 $remote_len = strlen($remote_prefix);
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.
73 $lines = explode("\n", $stdout);
74 $results = array();
75 foreach ($lines as $line) {
76 $fields = $this->extractFields($line);
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;
96 // If this isn't a type of ref we care about, skip it.
97 if (empty($ref_types[$type])) {
98 continue;
101 // If this is the local HEAD, skip it.
102 if ($short == 'HEAD') {
103 continue;
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;
116 $commit = nonempty($fields['*objectname'], $fields['objectname']);
118 $ref = id(new DiffusionRepositoryRef())
119 ->setRefType($type)
120 ->setShortName($short)
121 ->setCommitIdentifier($commit)
122 ->setRawFields($fields);
124 $results[] = $ref;
127 return $results;
131 * List of git `--format` fields we want to grab.
133 private function getFields() {
134 return array(
135 'objectname',
136 'objecttype',
137 'refname',
138 '*objectname',
139 '*objecttype',
140 'subject',
141 'creator',
146 * Get a string for `--format` which enumerates all the fields we want.
148 private function getFormatString() {
149 $fields = $this->getFields();
151 foreach ($fields as $key => $field) {
152 $fields[$key] = '%('.$field.')';
155 return implode('%01', $fields);
159 * Parse a line back into fields.
161 private function extractFields($line) {
162 $fields = $this->getFields();
163 $parts = explode("\1", $line, count($fields));
165 $dict = array();
166 foreach ($fields as $index => $field) {
167 $dict[$field] = idx($parts, $index);
170 return $dict;