[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DiffusionGetCommitsConduitAPIMethod 4 extends DiffusionConduitAPIMethod { 5 6 public function getAPIMethodName() { 7 return 'diffusion.getcommits'; 8 } 9 10 public function getMethodDescription() { 11 return pht('Retrieve Diffusion commit information.'); 12 } 13 14 public function getMethodStatus() { 15 return self::METHOD_STATUS_DEPRECATED; 16 } 17 18 public function getMethodStatusDescription() { 19 return pht('Obsoleted by diffusion.querycommits.'); 20 } 21 22 public function defineParamTypes() { 23 return array( 24 'commits' => 'required list<string>', 25 ); 26 } 27 28 public function defineReturnType() { 29 return 'nonempty list<dict<string, wild>>'; 30 } 31 32 public function defineErrorTypes() { 33 return array(); 34 } 35 36 protected function execute(ConduitAPIRequest $request) { 37 $results = array(); 38 39 $commits = $request->getValue('commits'); 40 $commits = array_fill_keys($commits, array()); 41 foreach ($commits as $name => $info) { 42 $matches = null; 43 if (!preg_match('/^r([A-Z]+)([0-9a-f]+)\z/', $name, $matches)) { 44 $results[$name] = array( 45 'error' => 'ERR-UNPARSEABLE', 46 ); 47 unset($commits[$name]); 48 continue; 49 } 50 $commits[$name] = array( 51 'callsign' => $matches[1], 52 'commitIdentifier' => $matches[2], 53 ); 54 } 55 56 if (!$commits) { 57 return $results; 58 } 59 60 $callsigns = ipull($commits, 'callsign'); 61 $callsigns = array_unique($callsigns); 62 $repos = id(new PhabricatorRepositoryQuery()) 63 ->setViewer($request->getUser()) 64 ->withCallsigns($callsigns) 65 ->execute(); 66 $repos = mpull($repos, null, 'getCallsign'); 67 68 foreach ($commits as $name => $info) { 69 $repo = idx($repos, $info['callsign']); 70 if (!$repo) { 71 $results[$name] = $info + array( 72 'error' => 'ERR-UNKNOWN-REPOSITORY', 73 ); 74 unset($commits[$name]); 75 continue; 76 } 77 $commits[$name] += array( 78 'repositoryPHID' => $repo->getPHID(), 79 'repositoryID' => $repo->getID(), 80 ); 81 } 82 83 if (!$commits) { 84 return $results; 85 } 86 87 // Execute a complicated query to figure out the primary commit information 88 // for each referenced commit. 89 $cdata = $this->queryCommitInformation($commits, $repos); 90 91 // We've built the queries so that each row also has the identifier we used 92 // to select it, which might be a git prefix rather than a full identifier. 93 $ref_map = ipull($cdata, 'commitIdentifier', 'commitRef'); 94 95 $cobjs = id(new PhabricatorRepositoryCommit())->loadAllFromArray($cdata); 96 $cobjs = mgroup($cobjs, 'getRepositoryID', 'getCommitIdentifier'); 97 foreach ($commits as $name => $commit) { 98 99 // Expand short git names into full identifiers. For SVN this map is just 100 // the identity. 101 $full_identifier = idx($ref_map, $commit['commitIdentifier']); 102 103 $repo_id = $commit['repositoryID']; 104 unset($commits[$name]['repositoryID']); 105 106 if (empty($full_identifier) || 107 empty($cobjs[$commit['repositoryID']][$full_identifier])) { 108 $results[$name] = $commit + array( 109 'error' => 'ERR-UNKNOWN-COMMIT', 110 ); 111 unset($commits[$name]); 112 continue; 113 } 114 115 $cobj_arr = $cobjs[$commit['repositoryID']][$full_identifier]; 116 $cobj = head($cobj_arr); 117 118 $commits[$name] += array( 119 'epoch' => $cobj->getEpoch(), 120 'commitPHID' => $cobj->getPHID(), 121 'commitID' => $cobj->getID(), 122 ); 123 124 // Upgrade git short references into full commit identifiers. 125 $identifier = $cobj->getCommitIdentifier(); 126 $commits[$name]['commitIdentifier'] = $identifier; 127 128 $callsign = $commits[$name]['callsign']; 129 $uri = "/r{$callsign}{$identifier}"; 130 $commits[$name]['uri'] = PhabricatorEnv::getProductionURI($uri); 131 } 132 133 if (!$commits) { 134 return $results; 135 } 136 137 $commits = $this->addRepositoryCommitDataInformation($commits); 138 $commits = $this->addDifferentialInformation($commits); 139 $commits = $this->addManiphestInformation($commits); 140 141 foreach ($commits as $name => $commit) { 142 $results[$name] = $commit; 143 } 144 145 return $results; 146 } 147 148 /** 149 * Retrieve primary commit information for all referenced commits. 150 */ 151 private function queryCommitInformation(array $commits, array $repos) { 152 assert_instances_of($repos, 'PhabricatorRepository'); 153 $conn_r = id(new PhabricatorRepositoryCommit())->establishConnection('r'); 154 $repos = mpull($repos, null, 'getID'); 155 156 $groups = array(); 157 foreach ($commits as $name => $commit) { 158 $groups[$commit['repositoryID']][] = $commit['commitIdentifier']; 159 } 160 161 // NOTE: MySQL goes crazy and does a massive table scan if we build a more 162 // sensible version of this query. Make sure the query plan is OK if you 163 // attempt to reduce the craziness here. METANOTE: The addition of prefix 164 // selection for Git further complicates matters. 165 $query = array(); 166 $commit_table = id(new PhabricatorRepositoryCommit())->getTableName(); 167 168 foreach ($groups as $repository_id => $identifiers) { 169 $vcs = $repos[$repository_id]->getVersionControlSystem(); 170 $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT); 171 if ($is_git) { 172 foreach ($identifiers as $identifier) { 173 if (strlen($identifier) < 7) { 174 // Don't bother with silly stuff like 'rX2', which will select 175 // 1/16th of all commits. Note that with length 7 we'll still get 176 // collisions in repositories at the tens-of-thousands-of-commits 177 // scale. 178 continue; 179 } 180 $query[] = qsprintf( 181 $conn_r, 182 'SELECT %T.*, %s commitRef 183 FROM %T WHERE repositoryID = %d 184 AND commitIdentifier LIKE %>', 185 $commit_table, 186 $identifier, 187 $commit_table, 188 $repository_id, 189 $identifier); 190 } 191 } else { 192 $query[] = qsprintf( 193 $conn_r, 194 'SELECT %T.*, commitIdentifier commitRef 195 FROM %T WHERE repositoryID = %d 196 AND commitIdentifier IN (%Ls)', 197 $commit_table, 198 $commit_table, 199 $repository_id, 200 $identifiers); 201 } 202 } 203 204 return queryfx_all( 205 $conn_r, 206 '%Q', 207 implode(' UNION ALL ', $query)); 208 } 209 210 /** 211 * Enhance the commit list with RepositoryCommitData information. 212 */ 213 private function addRepositoryCommitDataInformation(array $commits) { 214 $commit_ids = ipull($commits, 'commitID'); 215 216 $data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 217 'commitID in (%Ld)', 218 $commit_ids); 219 $data = mpull($data, null, 'getCommitID'); 220 221 foreach ($commits as $name => $commit) { 222 if (isset($data[$commit['commitID']])) { 223 $dobj = $data[$commit['commitID']]; 224 $commits[$name] += array( 225 'commitMessage' => $dobj->getCommitMessage(), 226 'commitDetails' => $dobj->getCommitDetails(), 227 ); 228 } 229 230 // Remove this information so we don't expose it via the API since 231 // external services shouldn't be storing internal Commit IDs. 232 unset($commits[$name]['commitID']); 233 } 234 235 return $commits; 236 } 237 238 /** 239 * Enhance the commit list with Differential information. 240 */ 241 private function addDifferentialInformation(array $commits) { 242 $commit_phids = ipull($commits, 'commitPHID'); 243 244 // TODO: (T603) This should be policy checked, either by moving to 245 // DifferentialRevisionQuery or by doing a followup query to make sure 246 // the matched objects are visible. 247 248 $rev_conn_r = id(new DifferentialRevision())->establishConnection('r'); 249 $revs = queryfx_all( 250 $rev_conn_r, 251 'SELECT r.id id, r.phid phid, c.commitPHID commitPHID FROM %T r JOIN %T c 252 ON r.id = c.revisionID 253 WHERE c.commitPHID in (%Ls)', 254 id(new DifferentialRevision())->getTableName(), 255 DifferentialRevision::TABLE_COMMIT, 256 $commit_phids); 257 258 $revs = ipull($revs, null, 'commitPHID'); 259 foreach ($commits as $name => $commit) { 260 if (isset($revs[$commit['commitPHID']])) { 261 $rev = $revs[$commit['commitPHID']]; 262 $commits[$name] += array( 263 'differentialRevisionID' => 'D'.$rev['id'], 264 'differentialRevisionPHID' => $rev['phid'], 265 ); 266 } 267 } 268 269 return $commits; 270 } 271 272 /** 273 * Enhances the commits list with Maniphest information. 274 */ 275 private function addManiphestInformation(array $commits) { 276 $task_type = DiffusionCommitHasTaskEdgeType::EDGECONST; 277 278 $commit_phids = ipull($commits, 'commitPHID'); 279 280 $edge_query = id(new PhabricatorEdgeQuery()) 281 ->withSourcePHIDs($commit_phids) 282 ->withEdgeTypes(array($task_type)); 283 284 $edges = $edge_query->execute(); 285 286 foreach ($commits as $name => $commit) { 287 $task_phids = $edge_query->getDestinationPHIDs( 288 array($commit['commitPHID']), 289 array($task_type)); 290 291 $commits[$name] += array( 292 'taskPHIDs' => $task_phids, 293 ); 294 } 295 296 return $commits; 297 } 298 299 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |