@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 upstream/main 847 lines 24 kB view raw
1<?php 2 3final class DifferentialDiff 4 extends DifferentialDAO 5 implements 6 PhabricatorPolicyInterface, 7 PhabricatorExtendedPolicyInterface, 8 HarbormasterBuildableInterface, 9 HarbormasterCircleCIBuildableInterface, 10 HarbormasterBuildkiteBuildableInterface, 11 PhabricatorApplicationTransactionInterface, 12 PhabricatorDestructibleInterface, 13 PhabricatorConduitResultInterface { 14 15 protected $revisionID; 16 protected $authorPHID; 17 protected $repositoryPHID; 18 protected $commitPHID; 19 20 protected $sourceMachine; 21 protected $sourcePath; 22 23 protected $sourceControlSystem; 24 protected $sourceControlBaseRevision; 25 protected $sourceControlPath; 26 27 protected $lintStatus; 28 protected $unitStatus; 29 30 protected $lineCount; 31 32 protected $branch; 33 protected $bookmark; 34 35 protected $creationMethod; 36 protected $repositoryUUID; 37 38 protected $description; 39 40 protected $viewPolicy; 41 42 private $unsavedChangesets = array(); 43 private $changesets = self::ATTACHABLE; 44 private $revision = self::ATTACHABLE; 45 private $properties = self::ATTACHABLE; 46 private $buildable = self::ATTACHABLE; 47 48 private $unitMessages = self::ATTACHABLE; 49 50 protected function getConfiguration() { 51 return array( 52 self::CONFIG_AUX_PHID => true, 53 self::CONFIG_COLUMN_SCHEMA => array( 54 'revisionID' => 'id?', 55 'authorPHID' => 'phid?', 56 'repositoryPHID' => 'phid?', 57 'sourceMachine' => 'text255?', 58 'sourcePath' => 'text255?', 59 'sourceControlSystem' => 'text64?', 60 'sourceControlBaseRevision' => 'text255?', 61 'sourceControlPath' => 'text255?', 62 'lintStatus' => 'uint32', 63 'unitStatus' => 'uint32', 64 'lineCount' => 'uint32', 65 'branch' => 'text255?', 66 'bookmark' => 'text255?', 67 'repositoryUUID' => 'text64?', 68 'commitPHID' => 'phid?', 69 70 // T6203/NULLABILITY 71 // These should be non-null; all diffs should have a creation method 72 // and the description should just be empty. 73 'creationMethod' => 'text255?', 74 'description' => 'text255?', 75 ), 76 self::CONFIG_KEY_SCHEMA => array( 77 'revisionID' => array( 78 'columns' => array('revisionID'), 79 ), 80 'key_commit' => array( 81 'columns' => array('commitPHID'), 82 ), 83 ), 84 ) + parent::getConfiguration(); 85 } 86 87 public function generatePHID() { 88 return PhabricatorPHID::generateNewPHID( 89 DifferentialDiffPHIDType::TYPECONST); 90 } 91 92 public function addUnsavedChangeset(DifferentialChangeset $changeset) { 93 if ($this->changesets === null) { 94 $this->changesets = array(); 95 } 96 $this->unsavedChangesets[] = $changeset; 97 $this->changesets[] = $changeset; 98 return $this; 99 } 100 101 /** 102 * @param array<DifferentialChangeset> $changesets 103 */ 104 public function attachChangesets(array $changesets) { 105 assert_instances_of($changesets, DifferentialChangeset::class); 106 $this->changesets = $changesets; 107 return $this; 108 } 109 110 public function getChangesets() { 111 return $this->assertAttached($this->changesets); 112 } 113 114 public function loadChangesets() { 115 if (!$this->getID()) { 116 return array(); 117 } 118 $changesets = id(new DifferentialChangeset())->loadAllWhere( 119 'diffID = %d', 120 $this->getID()); 121 122 foreach ($changesets as $changeset) { 123 $changeset->attachDiff($this); 124 } 125 126 return $changesets; 127 } 128 129 public function save() { 130 $this->openTransaction(); 131 $ret = parent::save(); 132 foreach ($this->unsavedChangesets as $changeset) { 133 $changeset->setDiffID($this->getID()); 134 $changeset->save(); 135 } 136 $this->saveTransaction(); 137 return $ret; 138 } 139 140 public static function initializeNewDiff(PhabricatorUser $actor) { 141 $app = id(new PhabricatorApplicationQuery()) 142 ->setViewer($actor) 143 ->withClasses(array(PhabricatorDifferentialApplication::class)) 144 ->executeOne(); 145 $view_policy = $app->getPolicy( 146 DifferentialDefaultViewCapability::CAPABILITY); 147 148 $diff = id(new DifferentialDiff()) 149 ->setViewPolicy($view_policy); 150 151 return $diff; 152 } 153 154 /** 155 * @param PhabricatorUser $actor 156 * @param array<ArcanistDiffChange> $changes 157 */ 158 public static function newFromRawChanges( 159 PhabricatorUser $actor, 160 array $changes) { 161 162 assert_instances_of($changes, ArcanistDiffChange::class); 163 164 $diff = self::initializeNewDiff($actor); 165 return self::buildChangesetsFromRawChanges($diff, $changes); 166 } 167 168 /** 169 * @param array<ArcanistDiffChange> $changes 170 */ 171 public static function newEphemeralFromRawChanges(array $changes) { 172 assert_instances_of($changes, ArcanistDiffChange::class); 173 174 $diff = id(new DifferentialDiff())->makeEphemeral(); 175 return self::buildChangesetsFromRawChanges($diff, $changes); 176 } 177 178 /** 179 * @param array<ArcanistDiffChange> $changes 180 */ 181 private static function buildChangesetsFromRawChanges( 182 DifferentialDiff $diff, 183 array $changes) { 184 185 // There may not be any changes; initialize the changesets list so that 186 // we don't throw later when accessing it. 187 $diff->attachChangesets(array()); 188 189 $lines = 0; 190 foreach ($changes as $change) { 191 if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) { 192 // If a user pastes a diff into Differential which includes a commit 193 // message (e.g., they ran `git show` to generate it), discard that 194 // change when constructing a DifferentialDiff. 195 continue; 196 } 197 198 $changeset = new DifferentialChangeset(); 199 $add_lines = 0; 200 $del_lines = 0; 201 $first_line = PHP_INT_MAX; 202 $hunks = $change->getHunks(); 203 if ($hunks) { 204 foreach ($hunks as $hunk) { 205 $dhunk = new DifferentialHunk(); 206 $dhunk->setOldOffset($hunk->getOldOffset()); 207 $dhunk->setOldLen($hunk->getOldLength()); 208 $dhunk->setNewOffset($hunk->getNewOffset()); 209 $dhunk->setNewLen($hunk->getNewLength()); 210 $dhunk->setChanges($hunk->getCorpus()); 211 $changeset->addUnsavedHunk($dhunk); 212 $add_lines += $hunk->getAddLines(); 213 $del_lines += $hunk->getDelLines(); 214 $added_lines = $hunk->getChangedLines('new'); 215 if ($added_lines) { 216 $first_line = min($first_line, head_key($added_lines)); 217 } 218 } 219 $lines += $add_lines + $del_lines; 220 } else { 221 // This happens when you add empty files. 222 $changeset->attachHunks(array()); 223 } 224 225 $metadata = $change->getAllMetadata(); 226 if ($first_line != PHP_INT_MAX) { 227 $metadata['line:first'] = $first_line; 228 } 229 230 $changeset->setOldFile($change->getOldPath()); 231 $changeset->setFilename($change->getCurrentPath()); 232 $changeset->setChangeType($change->getType()); 233 234 $changeset->setFileType($change->getFileType()); 235 $changeset->setMetadata($metadata); 236 $changeset->setOldProperties($change->getOldProperties()); 237 $changeset->setNewProperties($change->getNewProperties()); 238 $changeset->setAwayPaths($change->getAwayPaths()); 239 $changeset->setAddLines($add_lines); 240 $changeset->setDelLines($del_lines); 241 242 $diff->addUnsavedChangeset($changeset); 243 } 244 $diff->setLineCount($lines); 245 246 $changesets = $diff->getChangesets(); 247 248 // TODO: This is "safe", but it would be better to propagate a real user 249 // down the stack. 250 $viewer = PhabricatorUser::getOmnipotentUser(); 251 252 id(new DifferentialChangesetEngine()) 253 ->setViewer($viewer) 254 ->rebuildChangesets($changesets); 255 256 return $diff; 257 } 258 259 public function getDiffDict() { 260 $dict = array( 261 'id' => $this->getID(), 262 'revisionID' => $this->getRevisionID(), 263 'dateCreated' => $this->getDateCreated(), 264 'dateModified' => $this->getDateModified(), 265 'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(), 266 'sourceControlPath' => $this->getSourceControlPath(), 267 'sourceControlSystem' => $this->getSourceControlSystem(), 268 'branch' => $this->getBranch(), 269 'bookmark' => $this->getBookmark(), 270 'creationMethod' => $this->getCreationMethod(), 271 'description' => $this->getDescription(), 272 'unitStatus' => $this->getUnitStatus(), 273 'lintStatus' => $this->getLintStatus(), 274 'changes' => array(), 275 ); 276 277 $dict['changes'] = $this->buildChangesList(); 278 279 return $dict + $this->getDiffAuthorshipDict(); 280 } 281 282 public function getDiffAuthorshipDict() { 283 $dict = array('properties' => array()); 284 285 $properties = id(new DifferentialDiffProperty())->loadAllWhere( 286 'diffID = %d', 287 $this->getID()); 288 foreach ($properties as $property) { 289 $dict['properties'][$property->getName()] = $property->getData(); 290 291 if ($property->getName() == 'local:commits') { 292 foreach ($property->getData() as $commit) { 293 $dict['authorName'] = $commit['author']; 294 $dict['authorEmail'] = idx($commit, 'authorEmail'); 295 break; 296 } 297 } 298 } 299 300 return $dict; 301 } 302 303 public function buildChangesList() { 304 $changes = array(); 305 foreach ($this->getChangesets() as $changeset) { 306 $hunks = array(); 307 foreach ($changeset->getHunks() as $hunk) { 308 $hunks[] = array( 309 'oldOffset' => $hunk->getOldOffset(), 310 'newOffset' => $hunk->getNewOffset(), 311 'oldLength' => $hunk->getOldLen(), 312 'newLength' => $hunk->getNewLen(), 313 'addLines' => null, 314 'delLines' => null, 315 'isMissingOldNewline' => null, 316 'isMissingNewNewline' => null, 317 'corpus' => $hunk->getChanges(), 318 ); 319 } 320 $change = array( 321 'id' => $changeset->getID(), 322 'metadata' => $changeset->getMetadata(), 323 'oldPath' => $changeset->getOldFile(), 324 'currentPath' => $changeset->getFilename(), 325 'awayPaths' => $changeset->getAwayPaths(), 326 'oldProperties' => $changeset->getOldProperties(), 327 'newProperties' => $changeset->getNewProperties(), 328 'type' => $changeset->getChangeType(), 329 'fileType' => $changeset->getFileType(), 330 'commitHash' => null, 331 'addLines' => $changeset->getAddLines(), 332 'delLines' => $changeset->getDelLines(), 333 'hunks' => $hunks, 334 ); 335 $changes[] = $change; 336 } 337 return $changes; 338 } 339 340 public function hasRevision() { 341 return $this->revision !== self::ATTACHABLE; 342 } 343 344 public function getRevision() { 345 return $this->assertAttached($this->revision); 346 } 347 348 public function attachRevision(?DifferentialRevision $revision = null) { 349 $this->revision = $revision; 350 return $this; 351 } 352 353 public function attachProperty($key, $value) { 354 if (!is_array($this->properties)) { 355 $this->properties = array(); 356 } 357 $this->properties[$key] = $value; 358 return $this; 359 } 360 361 public function getProperty($key) { 362 return $this->assertAttachedKey($this->properties, $key); 363 } 364 365 public function hasDiffProperty($key) { 366 $properties = $this->getDiffProperties(); 367 return array_key_exists($key, $properties); 368 } 369 370 public function attachDiffProperties(array $properties) { 371 $this->properties = $properties; 372 return $this; 373 } 374 375 public function getDiffProperties() { 376 return $this->assertAttached($this->properties); 377 } 378 379 public function attachBuildable(?HarbormasterBuildable $buildable = null) { 380 $this->buildable = $buildable; 381 return $this; 382 } 383 384 public function getBuildable() { 385 return $this->assertAttached($this->buildable); 386 } 387 388 public function getBuildTargetPHIDs() { 389 $buildable = $this->getBuildable(); 390 391 if (!$buildable) { 392 return array(); 393 } 394 395 $target_phids = array(); 396 foreach ($buildable->getBuilds() as $build) { 397 foreach ($build->getBuildTargets() as $target) { 398 $target_phids[] = $target->getPHID(); 399 } 400 } 401 402 return $target_phids; 403 } 404 405 public function loadCoverageMap(PhabricatorUser $viewer) { 406 $target_phids = $this->getBuildTargetPHIDs(); 407 if (!$target_phids) { 408 return array(); 409 } 410 411 $unit = id(new HarbormasterBuildUnitMessageQuery()) 412 ->setViewer($viewer) 413 ->withBuildTargetPHIDs($target_phids) 414 ->execute(); 415 416 $map = array(); 417 foreach ($unit as $message) { 418 $coverage = $message->getProperty('coverage', array()); 419 foreach ($coverage as $path => $coverage_data) { 420 $map[$path][] = $coverage_data; 421 } 422 } 423 424 foreach ($map as $path => $coverage_items) { 425 $map[$path] = ArcanistUnitTestResult::mergeCoverage($coverage_items); 426 } 427 428 return $map; 429 } 430 431 public function getURI() { 432 $id = $this->getID(); 433 return "/differential/diff/{$id}/"; 434 } 435 436 437 public function attachUnitMessages(array $unit_messages) { 438 $this->unitMessages = $unit_messages; 439 return $this; 440 } 441 442 443 public function getUnitMessages() { 444 return $this->assertAttached($this->unitMessages); 445 } 446 447 448/* -( PhabricatorPolicyInterface )----------------------------------------- */ 449 450 451 public function getCapabilities() { 452 return array( 453 PhabricatorPolicyCapability::CAN_VIEW, 454 ); 455 } 456 457 public function getPolicy($capability) { 458 if ($this->hasRevision()) { 459 return PhabricatorPolicies::getMostOpenPolicy(); 460 } 461 462 return $this->viewPolicy; 463 } 464 465 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 466 if ($this->hasRevision()) { 467 return $this->getRevision()->hasAutomaticCapability($capability, $viewer); 468 } 469 470 return ($this->getAuthorPHID() == $viewer->getPHID()); 471 } 472 473 public function describeAutomaticCapability($capability) { 474 if ($this->hasRevision()) { 475 return pht( 476 'This diff is attached to a revision, and inherits its policies.'); 477 } 478 479 return pht('The author of a diff can see it.'); 480 } 481 482 483/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ 484 485 486 public function getExtendedPolicy($capability, PhabricatorUser $viewer) { 487 $extended = array(); 488 489 switch ($capability) { 490 case PhabricatorPolicyCapability::CAN_VIEW: 491 if ($this->hasRevision()) { 492 $extended[] = array( 493 $this->getRevision(), 494 PhabricatorPolicyCapability::CAN_VIEW, 495 ); 496 } else if ($this->getRepositoryPHID()) { 497 $extended[] = array( 498 $this->getRepositoryPHID(), 499 PhabricatorPolicyCapability::CAN_VIEW, 500 ); 501 } 502 break; 503 } 504 505 return $extended; 506 } 507 508 509/* -( HarbormasterBuildableInterface )------------------------------------- */ 510 511 512 public function getHarbormasterBuildableDisplayPHID() { 513 $container_phid = $this->getHarbormasterContainerPHID(); 514 if ($container_phid) { 515 return $container_phid; 516 } 517 518 return $this->getHarbormasterBuildablePHID(); 519 } 520 521 public function getHarbormasterBuildablePHID() { 522 return $this->getPHID(); 523 } 524 525 public function getHarbormasterContainerPHID() { 526 if ($this->getRevisionID()) { 527 $revision = id(new DifferentialRevision())->load($this->getRevisionID()); 528 if ($revision) { 529 return $revision->getPHID(); 530 } 531 } 532 533 return null; 534 } 535 536 public function getBuildVariables() { 537 $results = array(); 538 539 $results['buildable.diff'] = $this->getID(); 540 if ($this->revisionID) { 541 $revision = $this->getRevision(); 542 $results['buildable.revision'] = $revision->getID(); 543 $repo = $revision->getRepository(); 544 545 if ($repo) { 546 $results['repository.callsign'] = $repo->getCallsign(); 547 $results['repository.phid'] = $repo->getPHID(); 548 $results['repository.vcs'] = $repo->getVersionControlSystem(); 549 $results['repository.uri'] = $repo->getPublicCloneURI(); 550 551 $results['repository.staging.uri'] = $repo->getStagingURI(); 552 $results['repository.staging.ref'] = $this->getStagingRef(); 553 } 554 } 555 556 return $results; 557 } 558 559 public function getAvailableBuildVariables() { 560 return array( 561 'buildable.diff' => 562 pht('The differential diff ID, if applicable.'), 563 'buildable.revision' => 564 pht('The differential revision ID, if applicable.'), 565 'repository.callsign' => 566 pht('The callsign of the repository.'), 567 'repository.phid' => 568 pht('The PHID of the repository.'), 569 'repository.vcs' => 570 pht('The version control system, either "svn", "hg" or "git".'), 571 'repository.uri' => 572 pht('The URI to clone or checkout the repository from.'), 573 'repository.staging.uri' => 574 pht('The URI of the staging repository.'), 575 'repository.staging.ref' => 576 pht('The ref name for this change in the staging repository.'), 577 ); 578 } 579 580 public function newBuildableEngine() { 581 return new DifferentialBuildableEngine(); 582 } 583 584 585/* -( HarbormasterCircleCIBuildableInterface )----------------------------- */ 586 587 588 public function getCircleCIGitHubRepositoryURI() { 589 $diff_phid = $this->getPHID(); 590 $repository_phid = $this->getRepositoryPHID(); 591 if (!$repository_phid) { 592 throw new Exception( 593 pht( 594 'This diff ("%s") is not associated with a repository. A diff '. 595 'must belong to a tracked repository to be built by CircleCI.', 596 $diff_phid)); 597 } 598 599 $repository = id(new PhabricatorRepositoryQuery()) 600 ->setViewer(PhabricatorUser::getOmnipotentUser()) 601 ->withPHIDs(array($repository_phid)) 602 ->executeOne(); 603 if (!$repository) { 604 throw new Exception( 605 pht( 606 'This diff ("%s") is associated with a repository ("%s") which '. 607 'could not be loaded.', 608 $diff_phid, 609 $repository_phid)); 610 } 611 612 $staging_uri = $repository->getStagingURI(); 613 if (!$staging_uri) { 614 throw new Exception( 615 pht( 616 'This diff ("%s") is associated with a repository ("%s") that '. 617 'does not have a Staging Area configured. You must configure a '. 618 'Staging Area to use CircleCI integration.', 619 $diff_phid, 620 $repository_phid)); 621 } 622 623 $path = HarbormasterCircleCIBuildStepImplementation::getGitHubPath( 624 $staging_uri); 625 if (!$path) { 626 throw new Exception( 627 pht( 628 'This diff ("%s") is associated with a repository ("%s") that '. 629 'does not have a Staging Area ("%s") that is hosted on GitHub. '. 630 'CircleCI can only build from GitHub, so the Staging Area for '. 631 'the repository must be hosted there.', 632 $diff_phid, 633 $repository_phid, 634 $staging_uri)); 635 } 636 637 return $staging_uri; 638 } 639 640 public function getCircleCIBuildIdentifierType() { 641 return 'tag'; 642 } 643 644 public function getCircleCIBuildIdentifier() { 645 $ref = $this->getStagingRef(); 646 $ref = preg_replace('(^refs/tags/)', '', $ref); 647 return $ref; 648 } 649 650 651/* -( HarbormasterBuildkiteBuildableInterface )---------------------------- */ 652 653 public function getBuildkiteBranch() { 654 $ref = $this->getStagingRef(); 655 656 // NOTE: Circa late January 2017, Buildkite fails with the error message 657 // "Tags have been disabled for this project" if we pass the "refs/tags/" 658 // prefix via the API and the project doesn't have GitHub tag builds 659 // enabled, even if GitHub builds are disabled. The tag builds fine 660 // without this prefix. 661 $ref = preg_replace('(^refs/tags/)', '', $ref); 662 663 return $ref; 664 } 665 666 public function getBuildkiteCommit() { 667 return 'HEAD'; 668 } 669 670 671 public function getStagingRef() { 672 // TODO: We're just hoping to get lucky. Instead, `arc` should store 673 // where it sent changes and we should only provide staging details 674 // if we reasonably believe they are accurate. 675 return 'refs/tags/phabricator/diff/'.$this->getID(); 676 } 677 678 public function loadTargetBranch() { 679 // TODO: This is sketchy, but just eat the query cost until this can get 680 // cleaned up. 681 682 // For now, we're only returning a target if there's exactly one and it's 683 // a branch, since we don't support landing to more esoteric targets like 684 // tags yet. 685 686 $property = id(new DifferentialDiffProperty())->loadOneWhere( 687 'diffID = %d AND name = %s', 688 $this->getID(), 689 'arc:onto'); 690 if (!$property) { 691 return null; 692 } 693 694 $data = $property->getData(); 695 696 if (!$data) { 697 return null; 698 } 699 700 if (!is_array($data)) { 701 return null; 702 } 703 704 if (count($data) != 1) { 705 return null; 706 } 707 708 $onto = head($data); 709 if (!is_array($onto)) { 710 return null; 711 } 712 713 $type = idx($onto, 'type'); 714 if ($type != 'branch') { 715 return null; 716 } 717 718 return idx($onto, 'name'); 719 } 720 721 722/* -( PhabricatorApplicationTransactionInterface )------------------------- */ 723 724 725 public function getApplicationTransactionEditor() { 726 return new DifferentialDiffEditor(); 727 } 728 729 public function getApplicationTransactionTemplate() { 730 return new DifferentialDiffTransaction(); 731 } 732 733 734/* -( PhabricatorDestructibleInterface )----------------------------------- */ 735 736 737 public function destroyObjectPermanently( 738 PhabricatorDestructionEngine $engine) { 739 740 $viewer = $engine->getViewer(); 741 742 $this->openTransaction(); 743 $this->delete(); 744 745 foreach ($this->loadChangesets() as $changeset) { 746 $engine->destroyObject($changeset); 747 } 748 749 $properties = id(new DifferentialDiffProperty())->loadAllWhere( 750 'diffID = %d', 751 $this->getID()); 752 foreach ($properties as $prop) { 753 $prop->delete(); 754 } 755 756 $viewstate_query = id(new DifferentialViewStateQuery()) 757 ->setViewer($viewer) 758 ->withObjectPHIDs(array($this->getPHID())); 759 $viewstates = new PhabricatorQueryIterator($viewstate_query); 760 foreach ($viewstates as $viewstate) { 761 $viewstate->delete(); 762 } 763 764 $this->saveTransaction(); 765 } 766 767 768/* -( PhabricatorConduitResultInterface )---------------------------------- */ 769 770 771 public function getFieldSpecificationsForConduit() { 772 return array( 773 id(new PhabricatorConduitSearchFieldSpecification()) 774 ->setKey('revisionPHID') 775 ->setType('phid') 776 ->setDescription(pht('Associated revision PHID.')), 777 id(new PhabricatorConduitSearchFieldSpecification()) 778 ->setKey('authorPHID') 779 ->setType('phid') 780 ->setDescription(pht('Revision author PHID.')), 781 id(new PhabricatorConduitSearchFieldSpecification()) 782 ->setKey('repositoryPHID') 783 ->setType('phid') 784 ->setDescription(pht('Associated repository PHID.')), 785 id(new PhabricatorConduitSearchFieldSpecification()) 786 ->setKey('refs') 787 ->setType('map<string, wild>') 788 ->setDescription(pht('List of related VCS references.')), 789 ); 790 } 791 792 public function getFieldValuesForConduit() { 793 $refs = array(); 794 795 $branch = $this->getBranch(); 796 if (phutil_nonempty_string($branch)) { 797 $refs[] = array( 798 'type' => 'branch', 799 'name' => $branch, 800 ); 801 } 802 803 $onto = $this->loadTargetBranch(); 804 if (phutil_nonempty_string($onto)) { 805 $refs[] = array( 806 'type' => 'onto', 807 'name' => $onto, 808 ); 809 } 810 811 $base = $this->getSourceControlBaseRevision(); 812 if ($base !== null && strlen($base)) { 813 $refs[] = array( 814 'type' => 'base', 815 'identifier' => $base, 816 ); 817 } 818 819 $bookmark = $this->getBookmark(); 820 if (phutil_nonempty_string($bookmark)) { 821 $refs[] = array( 822 'type' => 'bookmark', 823 'name' => $bookmark, 824 ); 825 } 826 827 $revision_phid = null; 828 if ($this->getRevisionID()) { 829 $revision_phid = $this->getRevision()->getPHID(); 830 } 831 832 return array( 833 'revisionPHID' => $revision_phid, 834 'authorPHID' => $this->getAuthorPHID(), 835 'repositoryPHID' => $this->getRepositoryPHID(), 836 'refs' => $refs, 837 ); 838 } 839 840 public function getConduitSearchAttachments() { 841 return array( 842 id(new DifferentialCommitsSearchEngineAttachment()) 843 ->setAttachmentKey('commits'), 844 ); 845 } 846 847}