@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<?php
2
3final class DiffusionDiffQueryConduitAPIMethod
4 extends DiffusionQueryConduitAPIMethod {
5
6 private $effectiveCommit;
7
8 public function getAPIMethodName() {
9 return 'diffusion.diffquery';
10 }
11
12 public function getMethodDescription() {
13 return pht(
14 'Get diff information from a repository for a specific path at an '.
15 '(optional) commit.');
16 }
17
18 protected function defineReturnType() {
19 return 'array';
20 }
21
22 protected function defineCustomParamTypes() {
23 return array(
24 'path' => 'required string',
25 'commit' => 'optional string',
26 'encoding' => 'optional string',
27 );
28 }
29
30 protected function getResult(ConduitAPIRequest $request) {
31 $result = parent::getResult($request);
32
33 return array(
34 'changes' => mpull($result, 'toDictionary'),
35 'effectiveCommit' => $this->getEffectiveCommit($request),
36 );
37 }
38
39 protected function getGitResult(ConduitAPIRequest $request) {
40 return $this->getGitOrMercurialResult($request);
41 }
42
43 protected function getMercurialResult(ConduitAPIRequest $request) {
44 return $this->getGitOrMercurialResult($request);
45 }
46
47 /**
48 * NOTE: We have to work particularly hard for SVN as compared to other VCS.
49 * That's okay but means this shares little code with the other VCS.
50 */
51 protected function getSVNResult(ConduitAPIRequest $request) {
52 $drequest = $this->getDiffusionRequest();
53 $repository = $drequest->getRepository();
54
55 $effective_commit = $this->getEffectiveCommit($request);
56 if (!$effective_commit) {
57 return $this->getEmptyResult();
58 }
59
60 $drequest = clone $drequest;
61 $drequest->updateSymbolicCommit($effective_commit);
62
63 $path_change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
64 $drequest);
65 $path_changes = $path_change_query->loadChanges();
66
67 $path = null;
68 foreach ($path_changes as $change) {
69 if ($change->getPath() == $drequest->getPath()) {
70 $path = $change;
71 }
72 }
73
74 if (!$path) {
75 return $this->getEmptyResult();
76 }
77
78 $change_type = $path->getChangeType();
79 switch ($change_type) {
80 case DifferentialChangeType::TYPE_MULTICOPY:
81 case DifferentialChangeType::TYPE_DELETE:
82 if ($path->getTargetPath()) {
83 $old = array(
84 $path->getTargetPath(),
85 $path->getTargetCommitIdentifier(),
86 );
87 } else {
88 $old = array($path->getPath(), $path->getCommitIdentifier() - 1);
89 }
90 $old_name = $path->getPath();
91 $new_name = '';
92 $new = null;
93 break;
94 case DifferentialChangeType::TYPE_ADD:
95 $old = null;
96 $new = array($path->getPath(), $path->getCommitIdentifier());
97 $old_name = '';
98 $new_name = $path->getPath();
99 break;
100 case DifferentialChangeType::TYPE_MOVE_HERE:
101 case DifferentialChangeType::TYPE_COPY_HERE:
102 $old = array(
103 $path->getTargetPath(),
104 $path->getTargetCommitIdentifier(),
105 );
106 $new = array($path->getPath(), $path->getCommitIdentifier());
107 $old_name = $path->getTargetPath();
108 $new_name = $path->getPath();
109 break;
110 case DifferentialChangeType::TYPE_MOVE_AWAY:
111 $old = array(
112 $path->getPath(),
113 $path->getCommitIdentifier() - 1,
114 );
115 $old_name = $path->getPath();
116 $new_name = null;
117 $new = null;
118 break;
119 default:
120 $old = array($path->getPath(), $path->getCommitIdentifier() - 1);
121 $new = array($path->getPath(), $path->getCommitIdentifier());
122 $old_name = $path->getPath();
123 $new_name = $path->getPath();
124 break;
125 }
126
127 $futures = array(
128 'old' => $this->buildSVNContentFuture($old),
129 'new' => $this->buildSVNContentFuture($new),
130 );
131 $futures = array_filter($futures);
132
133 foreach (new FutureIterator($futures) as $key => $future) {
134 $stdout = '';
135 try {
136 list($stdout) = $future->resolvex();
137 } catch (CommandException $e) {
138 if ($path->getFileType() != DifferentialChangeType::FILE_DIRECTORY) {
139 throw $e;
140 }
141 }
142 $futures[$key] = $stdout;
143 }
144
145 $old_data = idx($futures, 'old', '');
146 $new_data = idx($futures, 'new', '');
147
148 $engine = new PhabricatorDifferenceEngine();
149 $engine->setOldName($old_name);
150 $engine->setNewName($new_name);
151 $raw_diff = $engine->generateRawDiffFromFileContent($old_data, $new_data);
152
153 $arcanist_changes = DiffusionPathChange::convertToArcanistChanges(
154 $path_changes);
155
156 $parser = $this->getDefaultParser($request);
157 $parser->setChanges($arcanist_changes);
158 $parser->forcePath($path->getPath());
159 $changes = $parser->parseDiff($raw_diff);
160
161 $change = $changes[$path->getPath()];
162
163 return array($change);
164 }
165
166 private function getEffectiveCommit(ConduitAPIRequest $request) {
167 if ($this->effectiveCommit === null) {
168 $drequest = $this->getDiffusionRequest();
169
170 $path = $drequest->getPath();
171 $result = DiffusionQuery::callConduitWithDiffusionRequest(
172 $request->getUser(),
173 $drequest,
174 'diffusion.lastmodifiedquery',
175 array(
176 'paths' => array($path => $drequest->getStableCommit()),
177 ));
178
179 $this->effectiveCommit = idx($result, $path);
180 }
181
182 return $this->effectiveCommit;
183 }
184
185 private function buildSVNContentFuture($spec) {
186 if (!$spec) {
187 return null;
188 }
189
190 $drequest = $this->getDiffusionRequest();
191 $repository = $drequest->getRepository();
192
193 list($ref, $rev) = $spec;
194 return $repository->getRemoteCommandFuture(
195 'cat %s',
196 $repository->getSubversionPathURI($ref, $rev));
197 }
198
199 private function getGitOrMercurialResult(ConduitAPIRequest $request) {
200 $drequest = $this->getDiffusionRequest();
201 $repository = $drequest->getRepository();
202
203 $effective_commit = $this->getEffectiveCommit($request);
204 if (!$effective_commit) {
205 return $this->getEmptyResult();
206 }
207
208 $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest)
209 ->setAnchorCommit($effective_commit);
210
211 $raw_diff = $raw_query->executeInline();
212 if (!$raw_diff) {
213 return $this->getEmptyResult();
214 }
215
216 $parser = $this->getDefaultParser($request);
217 $changes = $parser->parseDiff($raw_diff);
218
219 return $changes;
220 }
221
222 private function getDefaultParser(ConduitAPIRequest $request) {
223 $drequest = $this->getDiffusionRequest();
224 $repository = $drequest->getRepository();
225
226 $parser = new ArcanistDiffParser();
227 $try_encoding = coalesce(
228 $request->getValue('encoding'),
229 $repository->getDetail('encoding'));
230 if ($try_encoding) {
231 $parser->setTryEncoding($try_encoding);
232 }
233 $parser->setDetectBinaryFiles(true);
234
235 return $parser;
236 }
237
238 private function getEmptyResult() {
239 return array();
240 }
241
242}