@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 recaptime-dev/main 242 lines 6.9 kB view raw
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}