@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 296 lines 8.3 kB view raw
1<?php 2 3final class DiffusionHistoryQueryConduitAPIMethod 4 extends DiffusionQueryConduitAPIMethod { 5 6 private $parents = array(); 7 8 public function getAPIMethodName() { 9 return 'diffusion.historyquery'; 10 } 11 12 public function getMethodDescription() { 13 return pht( 14 'Returns history information for a repository at a specific '. 15 'commit and path.'); 16 } 17 18 protected function defineReturnType() { 19 return 'array'; 20 } 21 22 protected function defineCustomParamTypes() { 23 return array( 24 'commit' => 'required string', 25 'against' => 'optional string', 26 'path' => 'required string', 27 'offset' => 'optional int', 28 'limit' => 'required int', 29 'needDirectChanges' => 'optional bool', 30 'needChildChanges' => 'optional bool', 31 ); 32 } 33 34 protected function getResult(ConduitAPIRequest $request) { 35 $path_changes = parent::getResult($request); 36 37 return array( 38 'pathChanges' => mpull($path_changes, 'toDictionary'), 39 'parents' => $this->parents, 40 ); 41 } 42 43 protected function getGitResult(ConduitAPIRequest $request) { 44 $drequest = $this->getDiffusionRequest(); 45 $repository = $drequest->getRepository(); 46 $commit_hash = $request->getValue('commit'); 47 $against_hash = $request->getValue('against'); 48 49 $path = $request->getValue('path'); 50 if (!phutil_nonempty_string($path)) { 51 $path = null; 52 } 53 54 $offset = $request->getIntValue('offset'); 55 $limit = $request->getIntValue('limit', 100); 56 57 if (phutil_nonempty_string($against_hash)) { 58 $commit_range = "{$against_hash}..{$commit_hash}"; 59 } else { 60 $commit_range = $commit_hash; 61 } 62 63 $argv = array(); 64 65 $argv[] = '--skip'; 66 $argv[] = $offset; 67 68 $argv[] = '--max-count'; 69 $argv[] = $limit; 70 71 $argv[] = '--format=%H:%P'; 72 73 $argv[] = gitsprintf('%s', $commit_range); 74 75 $argv[] = '--'; 76 77 if ($path !== null) { 78 $argv[] = $path; 79 } 80 81 list($stdout) = $repository->execxLocalCommand( 82 'log %Ls', 83 $argv); 84 85 $lines = explode("\n", trim($stdout)); 86 $lines = array_filter($lines); 87 88 $hash_list = array(); 89 $parent_map = array(); 90 foreach ($lines as $line) { 91 list($hash, $parents) = explode(':', $line); 92 $hash_list[] = $hash; 93 $parent_map[$hash] = preg_split('/\s+/', $parents); 94 } 95 96 $this->parents = $parent_map; 97 98 if (!$hash_list) { 99 return array(); 100 } 101 102 return DiffusionQuery::loadHistoryForCommitIdentifiers( 103 $hash_list, 104 $drequest); 105 } 106 107 protected function getMercurialResult(ConduitAPIRequest $request) { 108 $drequest = $this->getDiffusionRequest(); 109 $repository = $drequest->getRepository(); 110 $commit_hash = $request->getValue('commit'); 111 $path = $request->getValue('path'); 112 $offset = $request->getValue('offset', 0); 113 $limit = $request->getValue('limit'); 114 115 $path = DiffusionPathIDQuery::normalizePath($path); 116 $path = ltrim($path, '/'); 117 118 // NOTE: Older versions of Mercurial give different results for these 119 // commands (see T1268): 120 // 121 // $ hg log -- '' 122 // $ hg log 123 // 124 // All versions of Mercurial give different results for these commands 125 // (merge commits are excluded with the "." version): 126 // 127 // $ hg log -- . 128 // $ hg log 129 // 130 // If we don't have a path component in the query, omit it from the command 131 // entirely to avoid these inconsistencies. 132 133 // NOTE: When viewing the history of a file, we don't use "-b", because 134 // Mercurial stops history at the branchpoint but we're interested in all 135 // ancestors. When viewing history of a branch, we do use "-b", and thus 136 // stop history (this is more consistent with the Mercurial worldview of 137 // branches). 138 139 $path_args = array(); 140 if (strlen($path)) { 141 $path_args[] = $path; 142 $revset_arg = hgsprintf( 143 'reverse(ancestors(%s))', 144 $commit_hash); 145 } else { 146 $revset_arg = hgsprintf( 147 'reverse(ancestors(%s)) and branch(%s)', 148 $commit_hash, 149 $drequest->getBranch()); 150 } 151 152 $hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg'); 153 if ($hg_analyzer->isMercurialTemplatePnodeAvailable()) { 154 $hg_log_template = '{node} {p1.node} {p2.node}\\n'; 155 } else { 156 $hg_log_template = '{node} {p1node} {p2node}\\n'; 157 } 158 159 list($stdout) = $repository->execxLocalCommand( 160 'log --template %s --limit %d --rev %s -- %Ls', 161 $hg_log_template, 162 ($offset + $limit), // No '--skip' in Mercurial. 163 $revset_arg, 164 $path_args); 165 166 $lines = explode("\n", trim($stdout)); 167 $lines = array_slice($lines, $offset); 168 169 $hash_list = array(); 170 $parent_map = array(); 171 172 $last = null; 173 foreach (array_reverse($lines) as $line) { 174 $parts = explode(' ', trim($line)); 175 $hash = $parts[0]; 176 $parents = array_slice($parts, 1, 2); 177 foreach ($parents as $parent) { 178 if (!preg_match('/^0+\z/', $parent)) { 179 $parent_map[$hash][] = $parent; 180 } 181 } 182 // This may happen for the zeroth commit in repository, both hashes 183 // are "000000000...". 184 if (empty($parent_map[$hash])) { 185 $parent_map[$hash] = array('...'); 186 } 187 188 // The rendering code expects the first commit to be "mainline", like 189 // Git. Flip the order so it does the right thing. 190 $parent_map[$hash] = array_reverse($parent_map[$hash]); 191 192 $hash_list[] = $hash; 193 $last = $hash; 194 } 195 196 $hash_list = array_reverse($hash_list); 197 $this->parents = array_reverse($parent_map, true); 198 199 return DiffusionQuery::loadHistoryForCommitIdentifiers( 200 $hash_list, 201 $drequest); 202 } 203 204 protected function getSVNResult(ConduitAPIRequest $request) { 205 $drequest = $this->getDiffusionRequest(); 206 $repository = $drequest->getRepository(); 207 $commit = $request->getValue('commit'); 208 $path = $request->getValue('path'); 209 $offset = $request->getValue('offset'); 210 $limit = $request->getValue('limit'); 211 $need_direct_changes = $request->getValue('needDirectChanges'); 212 $need_child_changes = $request->getValue('needChildChanges'); 213 214 $conn_r = $repository->establishConnection('r'); 215 216 $paths = queryfx_all( 217 $conn_r, 218 'SELECT id, path FROM %T WHERE pathHash IN (%Ls)', 219 PhabricatorRepository::TABLE_PATH, 220 array(md5('/'.trim($path, '/')))); 221 $paths = ipull($paths, 'id', 'path'); 222 $path_id = idx($paths, '/'.trim($path, '/')); 223 224 if (!$path_id) { 225 return array(); 226 } 227 228 $filter_query = qsprintf($conn_r, ''); 229 if ($need_direct_changes) { 230 if ($need_child_changes) { 231 $filter_query = qsprintf( 232 $conn_r, 233 'AND (isDirect = 1 OR changeType = %s)', 234 DifferentialChangeType::TYPE_CHILD); 235 } else { 236 $filter_query = qsprintf( 237 $conn_r, 238 'AND (isDirect = 1)'); 239 } 240 } 241 242 $history_data = queryfx_all( 243 $conn_r, 244 'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d 245 AND commitSequence <= %d 246 %Q 247 ORDER BY commitSequence DESC 248 LIMIT %d, %d', 249 PhabricatorRepository::TABLE_PATHCHANGE, 250 $repository->getID(), 251 $path_id, 252 $commit ? $commit : 0x7FFFFFFF, 253 $filter_query, 254 $offset, 255 $limit); 256 257 $commits = array(); 258 $commit_data = array(); 259 260 $commit_ids = ipull($history_data, 'commitID'); 261 if ($commit_ids) { 262 $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( 263 'id IN (%Ld)', 264 $commit_ids); 265 if ($commits) { 266 $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 267 'commitID in (%Ld)', 268 $commit_ids); 269 $commit_data = mpull($commit_data, null, 'getCommitID'); 270 } 271 } 272 273 $history = array(); 274 foreach ($history_data as $row) { 275 $item = new DiffusionPathChange(); 276 277 $commit = idx($commits, $row['commitID']); 278 if ($commit) { 279 $item->setCommit($commit); 280 $item->setCommitIdentifier($commit->getCommitIdentifier()); 281 $data = idx($commit_data, $commit->getID()); 282 if ($data) { 283 $item->setCommitData($data); 284 } 285 } 286 287 $item->setChangeType($row['changeType']); 288 $item->setFileType($row['fileType']); 289 290 $history[] = $item; 291 } 292 293 return $history; 294 } 295 296}