Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php
12256 views
1
<?php
2
3
final class DifferentialRevisionUpdateTransaction
4
extends DifferentialRevisionTransactionType {
5
6
const TRANSACTIONTYPE = 'differential:update';
7
const EDITKEY = 'update';
8
9
public function generateOldValue($object) {
10
return $object->getActiveDiffPHID();
11
}
12
13
public function generateNewValue($object, $value) {
14
// See T13290. If we're updating the revision in response to a commit but
15
// the revision is already closed, return the old value so we no-op this
16
// transaction. We don't want to attach more than one commit-diff to a
17
// revision.
18
19
// Although we can try to bail out earlier so we don't generate this
20
// transaction in the first place, we may race another worker and end up
21
// trying to apply it anyway. Here, we have a lock on the object and can
22
// be certain about the object state.
23
24
if ($this->isCommitUpdate()) {
25
if ($object->isClosed()) {
26
return $this->generateOldValue($object);
27
}
28
}
29
30
return $value;
31
}
32
33
public function applyInternalEffects($object, $value) {
34
$should_review = $this->shouldRequestReviewAfterUpdate($object);
35
if ($should_review) {
36
// If we're updating a non-broadcasting revision, put it back in draft
37
// rather than moving it directly to "Needs Review".
38
if ($object->getShouldBroadcast()) {
39
$new_status = DifferentialRevisionStatus::NEEDS_REVIEW;
40
} else {
41
$new_status = DifferentialRevisionStatus::DRAFT;
42
}
43
$object->setModernRevisionStatus($new_status);
44
}
45
46
$editor = $this->getEditor();
47
$diff = $editor->requireDiff($value);
48
49
$this->updateRevisionLineCounts($object, $diff);
50
51
$object->setRepositoryPHID($diff->getRepositoryPHID());
52
$object->setActiveDiffPHID($diff->getPHID());
53
$object->attachActiveDiff($diff);
54
}
55
56
private function shouldRequestReviewAfterUpdate($object) {
57
if ($this->isCommitUpdate()) {
58
return false;
59
}
60
61
$should_update =
62
$object->isNeedsRevision() ||
63
$object->isChangePlanned() ||
64
$object->isAbandoned();
65
if ($should_update) {
66
return true;
67
}
68
69
return false;
70
}
71
72
public function applyExternalEffects($object, $value) {
73
$editor = $this->getEditor();
74
$diff = $editor->requireDiff($value);
75
76
// TODO: This can race with diff updates, particularly those from
77
// Harbormaster. See discussion in T8650.
78
$diff->setRevisionID($object->getID());
79
$diff->save();
80
}
81
82
public function didCommitTransaction($object, $value) {
83
$editor = $this->getEditor();
84
$diff = $editor->requireDiff($value);
85
$omnipotent = PhabricatorUser::getOmnipotentUser();
86
87
// If there are any outstanding buildables for this diff, tell
88
// Harbormaster that their containers need to be updated. This is
89
// common, because `arc` creates buildables so it can upload lint
90
// and unit results.
91
92
$buildables = id(new HarbormasterBuildableQuery())
93
->setViewer($omnipotent)
94
->withManualBuildables(false)
95
->withBuildablePHIDs(array($diff->getPHID()))
96
->execute();
97
foreach ($buildables as $buildable) {
98
$buildable->sendMessage(
99
$this->getActor(),
100
HarbormasterMessageType::BUILDABLE_CONTAINER,
101
true);
102
}
103
104
// See T13455. If users have set view properites on a diff and the diff
105
// is then attached to a revision, attempt to copy their view preferences
106
// to the revision.
107
108
DifferentialViewState::copyViewStatesToObject(
109
$diff->getPHID(),
110
$object->getPHID());
111
}
112
113
public function getColor() {
114
return 'sky';
115
}
116
117
public function getIcon() {
118
return 'fa-refresh';
119
}
120
121
public function getActionName() {
122
if ($this->isCreateTransaction()) {
123
return pht('Request');
124
} else {
125
return pht('Updated');
126
}
127
}
128
129
public function getActionStrength() {
130
return 200;
131
}
132
133
public function getTitle() {
134
$old = $this->getOldValue();
135
$new = $this->getNewValue();
136
137
if ($this->isCommitUpdate()) {
138
return pht(
139
'This revision was automatically updated to reflect the '.
140
'committed changes.');
141
}
142
143
// NOTE: Very, very old update transactions did not have a new value or
144
// did not use a diff PHID as a new value. This was changed years ago,
145
// but wasn't migrated. We might consider migrating if this causes issues.
146
147
return pht(
148
'%s updated this revision to %s.',
149
$this->renderAuthor(),
150
$this->renderNewHandle());
151
}
152
153
public function getTitleForFeed() {
154
return pht(
155
'%s updated the diff for %s.',
156
$this->renderAuthor(),
157
$this->renderObject());
158
}
159
160
public function validateTransactions($object, array $xactions) {
161
$errors = array();
162
163
$diff_phid = null;
164
foreach ($xactions as $xaction) {
165
$diff_phid = $xaction->getNewValue();
166
167
$diff = id(new DifferentialDiffQuery())
168
->withPHIDs(array($diff_phid))
169
->setViewer($this->getActor())
170
->executeOne();
171
if (!$diff) {
172
$errors[] = $this->newInvalidError(
173
pht(
174
'Specified diff ("%s") does not exist.',
175
$diff_phid),
176
$xaction);
177
continue;
178
}
179
180
$is_attached =
181
($diff->getRevisionID()) &&
182
($diff->getRevisionID() == $object->getID());
183
if ($is_attached) {
184
$is_active = ($diff_phid == $object->getActiveDiffPHID());
185
} else {
186
$is_active = false;
187
}
188
189
if ($is_attached) {
190
if ($is_active) {
191
// This is a no-op: we're reattaching the current active diff to the
192
// revision it is already attached to. This is valid and will just
193
// be dropped later on in the process.
194
} else {
195
// At least for now, there's no support for "undoing" a diff and
196
// reverting to an older proposed change without just creating a
197
// new diff from whole cloth.
198
$errors[] = $this->newInvalidError(
199
pht(
200
'You can not update this revision with the specified diff '.
201
'("%s") because this diff is already attached to the revision '.
202
'as an older version of the change.',
203
$diff_phid),
204
$xaction);
205
continue;
206
}
207
} else if ($diff->getRevisionID()) {
208
$errors[] = $this->newInvalidError(
209
pht(
210
'You can not update this revision with the specified diff ("%s") '.
211
'because the diff is already attached to another revision.',
212
$diff_phid),
213
$xaction);
214
continue;
215
}
216
}
217
218
if (!$diff_phid && !$object->getActiveDiffPHID()) {
219
$errors[] = $this->newInvalidError(
220
pht(
221
'You must specify an initial diff when creating a revision.'));
222
}
223
224
return $errors;
225
}
226
227
public function isCommitUpdate() {
228
return (bool)$this->getMetadataValue('isCommitUpdate');
229
}
230
231
private function updateRevisionLineCounts(
232
DifferentialRevision $revision,
233
DifferentialDiff $diff) {
234
235
$revision->setLineCount($diff->getLineCount());
236
237
$conn = $revision->establishConnection('r');
238
239
$row = queryfx_one(
240
$conn,
241
'SELECT SUM(addLines) A, SUM(delLines) D FROM %T
242
WHERE diffID = %d',
243
id(new DifferentialChangeset())->getTableName(),
244
$diff->getID());
245
246
if ($row) {
247
$revision->setAddedLineCount((int)$row['A']);
248
$revision->setRemovedLineCount((int)$row['D']);
249
}
250
}
251
252
public function getTransactionTypeForConduit($xaction) {
253
return 'update';
254
}
255
256
public function getFieldValuesForConduit($xaction, $data) {
257
$commit_phids = $xaction->getMetadataValue('commitPHIDs', array());
258
259
return array(
260
'old' => $xaction->getOldValue(),
261
'new' => $xaction->getNewValue(),
262
'commitPHIDs' => $commit_phids,
263
);
264
}
265
266
}
267
268