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