[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DifferentialDiff 4 extends DifferentialDAO 5 implements 6 PhabricatorPolicyInterface, 7 HarbormasterBuildableInterface, 8 PhabricatorApplicationTransactionInterface, 9 PhabricatorDestructibleInterface { 10 11 protected $revisionID; 12 protected $authorPHID; 13 protected $repositoryPHID; 14 15 protected $sourceMachine; 16 protected $sourcePath; 17 18 protected $sourceControlSystem; 19 protected $sourceControlBaseRevision; 20 protected $sourceControlPath; 21 22 protected $lintStatus; 23 protected $unitStatus; 24 25 protected $lineCount; 26 27 protected $branch; 28 protected $bookmark; 29 30 protected $arcanistProjectPHID; 31 protected $creationMethod; 32 protected $repositoryUUID; 33 34 protected $description; 35 36 protected $viewPolicy; 37 38 private $unsavedChangesets = array(); 39 private $changesets = self::ATTACHABLE; 40 private $arcanistProject = self::ATTACHABLE; 41 private $revision = self::ATTACHABLE; 42 private $properties = array(); 43 44 public function getConfiguration() { 45 return array( 46 self::CONFIG_AUX_PHID => true, 47 self::CONFIG_COLUMN_SCHEMA => array( 48 'revisionID' => 'id?', 49 'authorPHID' => 'phid?', 50 'repositoryPHID' => 'phid?', 51 'sourceMachine' => 'text255?', 52 'sourcePath' => 'text255?', 53 'sourceControlSystem' => 'text64?', 54 'sourceControlBaseRevision' => 'text255?', 55 'sourceControlPath' => 'text255?', 56 'lintStatus' => 'uint32', 57 'unitStatus' => 'uint32', 58 'lineCount' => 'uint32', 59 'branch' => 'text255?', 60 'bookmark' => 'text255?', 61 'arcanistProjectPHID' => 'phid?', 62 'repositoryUUID' => 'text64?', 63 64 // T6203/NULLABILITY 65 // These should be non-null; all diffs should have a creation method 66 // and the description should just be empty. 67 'creationMethod' => 'text255?', 68 'description' => 'text255?', 69 ), 70 self::CONFIG_KEY_SCHEMA => array( 71 'revisionID' => array( 72 'columns' => array('revisionID'), 73 ), 74 ), 75 ) + parent::getConfiguration(); 76 } 77 78 public function generatePHID() { 79 return PhabricatorPHID::generateNewPHID( 80 DifferentialDiffPHIDType::TYPECONST); 81 } 82 83 public function addUnsavedChangeset(DifferentialChangeset $changeset) { 84 if ($this->changesets === null) { 85 $this->changesets = array(); 86 } 87 $this->unsavedChangesets[] = $changeset; 88 $this->changesets[] = $changeset; 89 return $this; 90 } 91 92 public function attachChangesets(array $changesets) { 93 assert_instances_of($changesets, 'DifferentialChangeset'); 94 $this->changesets = $changesets; 95 return $this; 96 } 97 98 public function getChangesets() { 99 return $this->assertAttached($this->changesets); 100 } 101 102 public function loadChangesets() { 103 if (!$this->getID()) { 104 return array(); 105 } 106 return id(new DifferentialChangeset())->loadAllWhere( 107 'diffID = %d', 108 $this->getID()); 109 } 110 111 public function attachArcanistProject( 112 PhabricatorRepositoryArcanistProject $project = null) { 113 $this->arcanistProject = $project; 114 return $this; 115 } 116 117 public function getArcanistProject() { 118 return $this->assertAttached($this->arcanistProject); 119 } 120 121 public function getArcanistProjectName() { 122 $name = ''; 123 if ($this->arcanistProject) { 124 $project = $this->getArcanistProject(); 125 $name = $project->getName(); 126 } 127 return $name; 128 } 129 130 public function save() { 131 $this->openTransaction(); 132 $ret = parent::save(); 133 foreach ($this->unsavedChangesets as $changeset) { 134 $changeset->setDiffID($this->getID()); 135 $changeset->save(); 136 } 137 $this->saveTransaction(); 138 return $ret; 139 } 140 141 public static function initializeNewDiff(PhabricatorUser $actor) { 142 $app = id(new PhabricatorApplicationQuery()) 143 ->setViewer($actor) 144 ->withClasses(array('PhabricatorDifferentialApplication')) 145 ->executeOne(); 146 $view_policy = $app->getPolicy( 147 DifferentialDefaultViewCapability::CAPABILITY); 148 149 $diff = id(new DifferentialDiff()) 150 ->setViewPolicy($view_policy); 151 152 return $diff; 153 } 154 155 public static function newFromRawChanges( 156 PhabricatorUser $actor, 157 array $changes) { 158 159 assert_instances_of($changes, 'ArcanistDiffChange'); 160 161 $diff = self::initializeNewDiff($actor); 162 // There may not be any changes; initialize the changesets list so that 163 // we don't throw later when accessing it. 164 $diff->attachChangesets(array()); 165 166 $lines = 0; 167 foreach ($changes as $change) { 168 if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) { 169 // If a user pastes a diff into Differential which includes a commit 170 // message (e.g., they ran `git show` to generate it), discard that 171 // change when constructing a DifferentialDiff. 172 continue; 173 } 174 175 $changeset = new DifferentialChangeset(); 176 $add_lines = 0; 177 $del_lines = 0; 178 $first_line = PHP_INT_MAX; 179 $hunks = $change->getHunks(); 180 if ($hunks) { 181 foreach ($hunks as $hunk) { 182 $dhunk = new DifferentialHunkModern(); 183 $dhunk->setOldOffset($hunk->getOldOffset()); 184 $dhunk->setOldLen($hunk->getOldLength()); 185 $dhunk->setNewOffset($hunk->getNewOffset()); 186 $dhunk->setNewLen($hunk->getNewLength()); 187 $dhunk->setChanges($hunk->getCorpus()); 188 $changeset->addUnsavedHunk($dhunk); 189 $add_lines += $hunk->getAddLines(); 190 $del_lines += $hunk->getDelLines(); 191 $added_lines = $hunk->getChangedLines('new'); 192 if ($added_lines) { 193 $first_line = min($first_line, head_key($added_lines)); 194 } 195 } 196 $lines += $add_lines + $del_lines; 197 } else { 198 // This happens when you add empty files. 199 $changeset->attachHunks(array()); 200 } 201 202 $metadata = $change->getAllMetadata(); 203 if ($first_line != PHP_INT_MAX) { 204 $metadata['line:first'] = $first_line; 205 } 206 207 $changeset->setOldFile($change->getOldPath()); 208 $changeset->setFilename($change->getCurrentPath()); 209 $changeset->setChangeType($change->getType()); 210 211 $changeset->setFileType($change->getFileType()); 212 $changeset->setMetadata($metadata); 213 $changeset->setOldProperties($change->getOldProperties()); 214 $changeset->setNewProperties($change->getNewProperties()); 215 $changeset->setAwayPaths($change->getAwayPaths()); 216 $changeset->setAddLines($add_lines); 217 $changeset->setDelLines($del_lines); 218 219 $diff->addUnsavedChangeset($changeset); 220 } 221 $diff->setLineCount($lines); 222 223 $parser = new DifferentialChangesetParser(); 224 $changesets = $parser->detectCopiedCode( 225 $diff->getChangesets(), 226 $min_width = 30, 227 $min_lines = 3); 228 $diff->attachChangesets($changesets); 229 230 return $diff; 231 } 232 233 234 public function getDiffDict() { 235 $dict = array( 236 'id' => $this->getID(), 237 'revisionID' => $this->getRevisionID(), 238 'dateCreated' => $this->getDateCreated(), 239 'dateModified' => $this->getDateModified(), 240 'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(), 241 'sourceControlPath' => $this->getSourceControlPath(), 242 'sourceControlSystem' => $this->getSourceControlSystem(), 243 'branch' => $this->getBranch(), 244 'bookmark' => $this->getBookmark(), 245 'creationMethod' => $this->getCreationMethod(), 246 'description' => $this->getDescription(), 247 'unitStatus' => $this->getUnitStatus(), 248 'lintStatus' => $this->getLintStatus(), 249 'changes' => array(), 250 'properties' => array(), 251 'projectName' => $this->getArcanistProjectName(), 252 ); 253 254 $dict['changes'] = $this->buildChangesList(); 255 256 $properties = id(new DifferentialDiffProperty())->loadAllWhere( 257 'diffID = %d', 258 $this->getID()); 259 foreach ($properties as $property) { 260 $dict['properties'][$property->getName()] = $property->getData(); 261 262 if ($property->getName() == 'local:commits') { 263 foreach ($property->getData() as $commit) { 264 $dict['authorName'] = $commit['author']; 265 $dict['authorEmail'] = idx($commit, 'authorEmail'); 266 break; 267 } 268 } 269 } 270 271 return $dict; 272 } 273 274 public function buildChangesList() { 275 $changes = array(); 276 foreach ($this->getChangesets() as $changeset) { 277 $hunks = array(); 278 foreach ($changeset->getHunks() as $hunk) { 279 $hunks[] = array( 280 'oldOffset' => $hunk->getOldOffset(), 281 'newOffset' => $hunk->getNewOffset(), 282 'oldLength' => $hunk->getOldLen(), 283 'newLength' => $hunk->getNewLen(), 284 'addLines' => null, 285 'delLines' => null, 286 'isMissingOldNewline' => null, 287 'isMissingNewNewline' => null, 288 'corpus' => $hunk->getChanges(), 289 ); 290 } 291 $change = array( 292 'id' => $changeset->getID(), 293 'metadata' => $changeset->getMetadata(), 294 'oldPath' => $changeset->getOldFile(), 295 'currentPath' => $changeset->getFilename(), 296 'awayPaths' => $changeset->getAwayPaths(), 297 'oldProperties' => $changeset->getOldProperties(), 298 'newProperties' => $changeset->getNewProperties(), 299 'type' => $changeset->getChangeType(), 300 'fileType' => $changeset->getFileType(), 301 'commitHash' => null, 302 'addLines' => $changeset->getAddLines(), 303 'delLines' => $changeset->getDelLines(), 304 'hunks' => $hunks, 305 ); 306 $changes[] = $change; 307 } 308 return $changes; 309 } 310 311 public function hasRevision() { 312 return $this->revision !== self::ATTACHABLE; 313 } 314 315 public function getRevision() { 316 return $this->assertAttached($this->revision); 317 } 318 319 public function attachRevision(DifferentialRevision $revision = null) { 320 $this->revision = $revision; 321 return $this; 322 } 323 324 public function attachProperty($key, $value) { 325 $this->properties[$key] = $value; 326 return $this; 327 } 328 329 public function getProperty($key) { 330 return $this->assertAttachedKey($this->properties, $key); 331 } 332 333 334 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 335 336 337 public function getCapabilities() { 338 return array( 339 PhabricatorPolicyCapability::CAN_VIEW, 340 ); 341 } 342 343 public function getPolicy($capability) { 344 if ($this->hasRevision()) { 345 return $this->getRevision()->getPolicy($capability); 346 } 347 348 return $this->viewPolicy; 349 } 350 351 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 352 if ($this->hasRevision()) { 353 return $this->getRevision()->hasAutomaticCapability($capability, $viewer); 354 } 355 356 return ($this->getAuthorPHID() == $viewer->getPhid()); 357 } 358 359 public function describeAutomaticCapability($capability) { 360 if ($this->hasRevision()) { 361 return pht( 362 'This diff is attached to a revision, and inherits its policies.'); 363 } 364 return pht('The author of a diff can see it.'); 365 } 366 367 368 369 /* -( HarbormasterBuildableInterface )------------------------------------- */ 370 371 372 public function getHarbormasterBuildablePHID() { 373 return $this->getPHID(); 374 } 375 376 public function getHarbormasterContainerPHID() { 377 if ($this->getRevisionID()) { 378 $revision = id(new DifferentialRevision())->load($this->getRevisionID()); 379 if ($revision) { 380 return $revision->getPHID(); 381 } 382 } 383 384 return null; 385 } 386 387 public function getBuildVariables() { 388 $results = array(); 389 390 $results['buildable.diff'] = $this->getID(); 391 $revision = $this->getRevision(); 392 $results['buildable.revision'] = $revision->getID(); 393 $repo = $revision->getRepository(); 394 395 if ($repo) { 396 $results['repository.callsign'] = $repo->getCallsign(); 397 $results['repository.vcs'] = $repo->getVersionControlSystem(); 398 $results['repository.uri'] = $repo->getPublicCloneURI(); 399 } 400 401 return $results; 402 } 403 404 public function getAvailableBuildVariables() { 405 return array( 406 'buildable.diff' => 407 pht('The differential diff ID, if applicable.'), 408 'buildable.revision' => 409 pht('The differential revision ID, if applicable.'), 410 'repository.callsign' => 411 pht('The callsign of the repository in Phabricator.'), 412 'repository.vcs' => 413 pht('The version control system, either "svn", "hg" or "git".'), 414 'repository.uri' => 415 pht('The URI to clone or checkout the repository from.'), 416 ); 417 } 418 419 420 /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 421 422 423 public function getApplicationTransactionEditor() { 424 if (!$this->getRevisionID()) { 425 return null; 426 } 427 return $this->getRevision()->getApplicationTransactionEditor(); 428 } 429 430 431 public function getApplicationTransactionObject() { 432 if (!$this->getRevisionID()) { 433 return null; 434 } 435 return $this->getRevision(); 436 } 437 438 public function getApplicationTransactionTemplate() { 439 if (!$this->getRevisionID()) { 440 return null; 441 } 442 return $this->getRevision()->getApplicationTransactionTemplate(); 443 } 444 445 446 /* -( PhabricatorDestructibleInterface )----------------------------------- */ 447 448 449 public function destroyObjectPermanently( 450 PhabricatorDestructionEngine $engine) { 451 452 $this->openTransaction(); 453 $this->delete(); 454 455 foreach ($this->loadChangesets() as $changeset) { 456 $changeset->delete(); 457 } 458 459 $properties = id(new DifferentialDiffProperty())->loadAllWhere( 460 'diffID = %d', 461 $this->getID()); 462 foreach ($properties as $prop) { 463 $prop->delete(); 464 } 465 466 $this->saveTransaction(); 467 } 468 469 }
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 |