Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php
12242 views
1
<?php
2
3
final class DiffusionHistoryQueryConduitAPIMethod
4
extends DiffusionQueryConduitAPIMethod {
5
6
private $parents = array();
7
8
public function getAPIMethodName() {
9
return 'diffusion.historyquery';
10
}
11
12
public function getMethodDescription() {
13
return pht(
14
'Returns history information for a repository at a specific '.
15
'commit and path.');
16
}
17
18
protected function defineReturnType() {
19
return 'array';
20
}
21
22
protected function defineCustomParamTypes() {
23
return array(
24
'commit' => 'required string',
25
'against' => 'optional string',
26
'path' => 'required string',
27
'offset' => 'required int',
28
'limit' => 'required int',
29
'needDirectChanges' => 'optional bool',
30
'needChildChanges' => 'optional bool',
31
);
32
}
33
34
protected function getResult(ConduitAPIRequest $request) {
35
$path_changes = parent::getResult($request);
36
37
return array(
38
'pathChanges' => mpull($path_changes, 'toDictionary'),
39
'parents' => $this->parents,
40
);
41
}
42
43
protected function getGitResult(ConduitAPIRequest $request) {
44
$drequest = $this->getDiffusionRequest();
45
$repository = $drequest->getRepository();
46
$commit_hash = $request->getValue('commit');
47
$against_hash = $request->getValue('against');
48
$offset = $request->getValue('offset');
49
$limit = $request->getValue('limit');
50
51
$path = $request->getValue('path');
52
if ($path === null || !strlen($path)) {
53
$path = null;
54
}
55
56
if ($against_hash !== null && strlen($against_hash)) {
57
$commit_range = "{$against_hash}..{$commit_hash}";
58
} else {
59
$commit_range = $commit_hash;
60
}
61
62
$argv = array();
63
64
$argv[] = '--skip';
65
$argv[] = $offset;
66
67
$argv[] = '--max-count';
68
$argv[] = $limit;
69
70
$argv[] = '--format=%H:%P';
71
72
$argv[] = gitsprintf('%s', $commit_range);
73
74
$argv[] = '--';
75
76
if ($path !== null) {
77
$argv[] = $path;
78
}
79
80
list($stdout) = $repository->execxLocalCommand(
81
'log %Ls',
82
$argv);
83
84
$lines = explode("\n", trim($stdout));
85
$lines = array_filter($lines);
86
87
$hash_list = array();
88
$parent_map = array();
89
foreach ($lines as $line) {
90
list($hash, $parents) = explode(':', $line);
91
$hash_list[] = $hash;
92
$parent_map[$hash] = preg_split('/\s+/', $parents);
93
}
94
95
$this->parents = $parent_map;
96
97
if (!$hash_list) {
98
return array();
99
}
100
101
return DiffusionQuery::loadHistoryForCommitIdentifiers(
102
$hash_list,
103
$drequest);
104
}
105
106
protected function getMercurialResult(ConduitAPIRequest $request) {
107
$drequest = $this->getDiffusionRequest();
108
$repository = $drequest->getRepository();
109
$commit_hash = $request->getValue('commit');
110
$path = $request->getValue('path');
111
$offset = $request->getValue('offset');
112
$limit = $request->getValue('limit');
113
114
$path = DiffusionPathIDQuery::normalizePath($path);
115
$path = ltrim($path, '/');
116
117
// NOTE: Older versions of Mercurial give different results for these
118
// commands (see T1268):
119
//
120
// $ hg log -- ''
121
// $ hg log
122
//
123
// All versions of Mercurial give different results for these commands
124
// (merge commits are excluded with the "." version):
125
//
126
// $ hg log -- .
127
// $ hg log
128
//
129
// If we don't have a path component in the query, omit it from the command
130
// entirely to avoid these inconsistencies.
131
132
// NOTE: When viewing the history of a file, we don't use "-b", because
133
// Mercurial stops history at the branchpoint but we're interested in all
134
// ancestors. When viewing history of a branch, we do use "-b", and thus
135
// stop history (this is more consistent with the Mercurial worldview of
136
// branches).
137
138
$path_args = array();
139
if (strlen($path)) {
140
$path_args[] = $path;
141
$revset_arg = hgsprintf(
142
'reverse(ancestors(%s))',
143
$commit_hash);
144
} else {
145
$revset_arg = hgsprintf(
146
'reverse(ancestors(%s)) and branch(%s)',
147
$commit_hash,
148
$drequest->getBranch());
149
}
150
151
$hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg');
152
if ($hg_analyzer->isMercurialTemplatePnodeAvailable()) {
153
$hg_log_template = '{node} {p1.node} {p2.node}\\n';
154
} else {
155
$hg_log_template = '{node} {p1node} {p2node}\\n';
156
}
157
158
list($stdout) = $repository->execxLocalCommand(
159
'log --template %s --limit %d --rev %s -- %Ls',
160
$hg_log_template,
161
($offset + $limit), // No '--skip' in Mercurial.
162
$revset_arg,
163
$path_args);
164
165
$lines = explode("\n", trim($stdout));
166
$lines = array_slice($lines, $offset);
167
168
$hash_list = array();
169
$parent_map = array();
170
171
$last = null;
172
foreach (array_reverse($lines) as $line) {
173
$parts = explode(' ', trim($line));
174
$hash = $parts[0];
175
$parents = array_slice($parts, 1, 2);
176
foreach ($parents as $parent) {
177
if (!preg_match('/^0+\z/', $parent)) {
178
$parent_map[$hash][] = $parent;
179
}
180
}
181
// This may happen for the zeroth commit in repository, both hashes
182
// are "000000000...".
183
if (empty($parent_map[$hash])) {
184
$parent_map[$hash] = array('...');
185
}
186
187
// The rendering code expects the first commit to be "mainline", like
188
// Git. Flip the order so it does the right thing.
189
$parent_map[$hash] = array_reverse($parent_map[$hash]);
190
191
$hash_list[] = $hash;
192
$last = $hash;
193
}
194
195
$hash_list = array_reverse($hash_list);
196
$this->parents = array_reverse($parent_map, true);
197
198
return DiffusionQuery::loadHistoryForCommitIdentifiers(
199
$hash_list,
200
$drequest);
201
}
202
203
protected function getSVNResult(ConduitAPIRequest $request) {
204
$drequest = $this->getDiffusionRequest();
205
$repository = $drequest->getRepository();
206
$commit = $request->getValue('commit');
207
$path = $request->getValue('path');
208
$offset = $request->getValue('offset');
209
$limit = $request->getValue('limit');
210
$need_direct_changes = $request->getValue('needDirectChanges');
211
$need_child_changes = $request->getValue('needChildChanges');
212
213
$conn_r = $repository->establishConnection('r');
214
215
$paths = queryfx_all(
216
$conn_r,
217
'SELECT id, path FROM %T WHERE pathHash IN (%Ls)',
218
PhabricatorRepository::TABLE_PATH,
219
array(md5('/'.trim($path, '/'))));
220
$paths = ipull($paths, 'id', 'path');
221
$path_id = idx($paths, '/'.trim($path, '/'));
222
223
if (!$path_id) {
224
return array();
225
}
226
227
$filter_query = qsprintf($conn_r, '');
228
if ($need_direct_changes) {
229
if ($need_child_changes) {
230
$filter_query = qsprintf(
231
$conn_r,
232
'AND (isDirect = 1 OR changeType = %s)',
233
DifferentialChangeType::TYPE_CHILD);
234
} else {
235
$filter_query = qsprintf(
236
$conn_r,
237
'AND (isDirect = 1)');
238
}
239
}
240
241
$history_data = queryfx_all(
242
$conn_r,
243
'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d
244
AND commitSequence <= %d
245
%Q
246
ORDER BY commitSequence DESC
247
LIMIT %d, %d',
248
PhabricatorRepository::TABLE_PATHCHANGE,
249
$repository->getID(),
250
$path_id,
251
$commit ? $commit : 0x7FFFFFFF,
252
$filter_query,
253
$offset,
254
$limit);
255
256
$commits = array();
257
$commit_data = array();
258
259
$commit_ids = ipull($history_data, 'commitID');
260
if ($commit_ids) {
261
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
262
'id IN (%Ld)',
263
$commit_ids);
264
if ($commits) {
265
$commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(
266
'commitID in (%Ld)',
267
$commit_ids);
268
$commit_data = mpull($commit_data, null, 'getCommitID');
269
}
270
}
271
272
$history = array();
273
foreach ($history_data as $row) {
274
$item = new DiffusionPathChange();
275
276
$commit = idx($commits, $row['commitID']);
277
if ($commit) {
278
$item->setCommit($commit);
279
$item->setCommitIdentifier($commit->getCommitIdentifier());
280
$data = idx($commit_data, $commit->getID());
281
if ($data) {
282
$item->setCommitData($data);
283
}
284
}
285
286
$item->setChangeType($row['changeType']);
287
$item->setFileType($row['fileType']);
288
289
$history[] = $item;
290
}
291
292
return $history;
293
}
294
295
}
296
297