1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of the SVN-Buddy library. |
4
|
|
|
* For the full copyright and license information, please view |
5
|
|
|
* the LICENSE file that was distributed with this source code. |
6
|
|
|
* |
7
|
|
|
* @copyright Alexander Obuhovich <[email protected]> |
8
|
|
|
* @link https://github.com/console-helpers/svn-buddy |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace ConsoleHelpers\SVNBuddy\Command; |
12
|
|
|
|
13
|
|
|
|
14
|
|
|
use ConsoleHelpers\SVNBuddy\Config\AbstractConfigSetting; |
15
|
|
|
use ConsoleHelpers\SVNBuddy\Config\IntegerConfigSetting; |
16
|
|
|
use ConsoleHelpers\SVNBuddy\Config\RegExpsConfigSetting; |
17
|
|
|
use ConsoleHelpers\ConsoleKit\Exception\CommandException; |
18
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\Parser\RevisionListParser; |
19
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\RevisionLog; |
20
|
|
|
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\RevisionPrinter; |
21
|
|
|
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; |
22
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
23
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
24
|
|
|
use Symfony\Component\Console\Input\InputOption; |
25
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
26
|
|
|
|
27
|
|
|
class LogCommand extends AbstractCommand implements IAggregatorAwareCommand, IConfigAwareCommand |
28
|
|
|
{ |
29
|
|
|
|
30
|
|
|
const SETTING_LOG_LIMIT = 'log.limit'; |
31
|
|
|
|
32
|
|
|
const SETTING_LOG_MESSAGE_LIMIT = 'log.message-limit'; |
33
|
|
|
|
34
|
|
|
const SETTING_LOG_MERGE_CONFLICT_REGEXPS = 'log.merge-conflict-regexps'; |
35
|
|
|
|
36
|
|
|
const ALL_REFS = 'all'; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Revision list parser. |
40
|
|
|
* |
41
|
|
|
* @var RevisionListParser |
42
|
|
|
*/ |
43
|
|
|
private $_revisionListParser; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Revision log |
47
|
|
|
* |
48
|
|
|
* @var RevisionLog |
49
|
|
|
*/ |
50
|
|
|
private $_revisionLog; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Revision printer. |
54
|
|
|
* |
55
|
|
|
* @var RevisionPrinter |
56
|
|
|
*/ |
57
|
|
|
private $_revisionPrinter; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Prepare dependencies. |
61
|
|
|
* |
62
|
|
|
* @return void |
63
|
|
|
*/ |
64
|
|
|
protected function prepareDependencies() |
65
|
|
|
{ |
66
|
|
|
parent::prepareDependencies(); |
67
|
|
|
|
68
|
|
|
$container = $this->getContainer(); |
69
|
|
|
|
70
|
|
|
$this->_revisionListParser = $container['revision_list_parser']; |
71
|
|
|
$this->_revisionPrinter = $container['revision_printer']; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* {@inheritdoc} |
76
|
|
|
*/ |
77
|
|
|
protected function configure() |
78
|
|
|
{ |
79
|
|
|
$this->pathAcceptsUrl = true; |
80
|
|
|
|
81
|
|
|
$this |
82
|
|
|
->setName('log') |
83
|
|
|
->setDescription( |
84
|
|
|
'Show the log messages for a set of revisions, bugs, paths, refs, etc.' |
85
|
|
|
) |
86
|
|
|
->addArgument( |
87
|
|
|
'path', |
88
|
|
|
InputArgument::OPTIONAL, |
89
|
|
|
'Working copy path or URL', |
90
|
|
|
'.' |
91
|
|
|
) |
92
|
|
|
->addOption( |
93
|
|
|
'revisions', |
94
|
|
|
'r', |
95
|
|
|
InputOption::VALUE_REQUIRED, |
96
|
|
|
'List of revision(-s) and/or revision range(-s), e.g. <comment>53324</comment>, <comment>1224-4433</comment>' |
97
|
|
|
) |
98
|
|
|
->addOption( |
99
|
|
|
'bugs', |
100
|
|
|
'b', |
101
|
|
|
InputOption::VALUE_REQUIRED, |
102
|
|
|
'List of bug(-s), e.g. <comment>JRA-1234</comment>, <comment>43644</comment>' |
103
|
|
|
) |
104
|
|
|
->addOption( |
105
|
|
|
'refs', |
106
|
|
|
null, |
107
|
|
|
InputOption::VALUE_REQUIRED, |
108
|
|
|
'List of refs, e.g. <comment>trunk</comment>, <comment>branches/branch-name</comment>, <comment>tags/tag-name</comment> or <comment>all</comment> for all refs' |
109
|
|
|
) |
110
|
|
|
->addOption( |
111
|
|
|
'merges', |
112
|
|
|
null, |
113
|
|
|
InputOption::VALUE_NONE, |
114
|
|
|
'Show merge revisions only' |
115
|
|
|
) |
116
|
|
|
->addOption( |
117
|
|
|
'no-merges', |
118
|
|
|
null, |
119
|
|
|
InputOption::VALUE_NONE, |
120
|
|
|
'Hide merge revisions' |
121
|
|
|
) |
122
|
|
|
->addOption( |
123
|
|
|
'merged', |
124
|
|
|
null, |
125
|
|
|
InputOption::VALUE_NONE, |
126
|
|
|
'Shows only revisions, that were merged at least once' |
127
|
|
|
) |
128
|
|
|
->addOption( |
129
|
|
|
'not-merged', |
130
|
|
|
null, |
131
|
|
|
InputOption::VALUE_NONE, |
132
|
|
|
'Shows only revisions, that were not merged' |
133
|
|
|
) |
134
|
|
|
->addOption( |
135
|
|
|
'merged-by', |
136
|
|
|
null, |
137
|
|
|
InputOption::VALUE_REQUIRED, |
138
|
|
|
'Show revisions merged by list of revision(-s) and/or revision range(-s)' |
139
|
|
|
) |
140
|
|
|
->addOption( |
141
|
|
|
'action', |
142
|
|
|
null, |
143
|
|
|
InputOption::VALUE_REQUIRED, |
144
|
|
|
'Show revisions, whose paths were affected by specified action, e.g. <comment>A</comment>, <comment>M</comment>, <comment>R</comment>, <comment>D</comment>' |
145
|
|
|
) |
146
|
|
|
->addOption( |
147
|
|
|
'kind', |
148
|
|
|
null, |
149
|
|
|
InputOption::VALUE_REQUIRED, |
150
|
|
|
'Show revisions, whose paths match specified kind, e.g. <comment>dir</comment> or <comment>file</comment>' |
151
|
|
|
) |
152
|
|
|
->addOption( |
153
|
|
|
'with-details', |
154
|
|
|
'd', |
155
|
|
|
InputOption::VALUE_NONE, |
156
|
|
|
'Shows detailed revision information, e.g. paths affected' |
157
|
|
|
) |
158
|
|
|
->addOption( |
159
|
|
|
'with-summary', |
160
|
|
|
's', |
161
|
|
|
InputOption::VALUE_NONE, |
162
|
|
|
'Shows number of added/changed/removed paths in the revision' |
163
|
|
|
) |
164
|
|
|
->addOption( |
165
|
|
|
'with-refs', |
166
|
|
|
null, |
167
|
|
|
InputOption::VALUE_NONE, |
168
|
|
|
'Shows revision refs' |
169
|
|
|
) |
170
|
|
|
->addOption( |
171
|
|
|
'with-merge-oracle', |
172
|
|
|
null, |
173
|
|
|
InputOption::VALUE_NONE, |
174
|
|
|
'Shows number of paths in the revision, that can cause conflict upon merging' |
175
|
|
|
) |
176
|
|
|
->addOption( |
177
|
|
|
'with-merge-status', |
178
|
|
|
null, |
179
|
|
|
InputOption::VALUE_NONE, |
180
|
|
|
'Shows merge revisions affecting this revision' |
181
|
|
|
) |
182
|
|
|
->addOption( |
183
|
|
|
'max-count', |
184
|
|
|
null, |
185
|
|
|
InputOption::VALUE_REQUIRED, |
186
|
|
|
'Limit the number of revisions to output' |
187
|
|
|
); |
188
|
|
|
|
189
|
|
|
parent::configure(); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Return possible values for the named option |
194
|
|
|
* |
195
|
|
|
* @param string $optionName Option name. |
196
|
|
|
* @param CompletionContext $context Completion context. |
197
|
|
|
* |
198
|
|
|
* @return array |
199
|
|
|
*/ |
200
|
|
|
public function completeOptionValues($optionName, CompletionContext $context) |
201
|
|
|
{ |
202
|
|
|
$ret = parent::completeOptionValues($optionName, $context); |
203
|
|
|
|
204
|
|
|
if ( $optionName === 'refs' ) { |
205
|
|
|
return $this->getAllRefs(); |
206
|
|
|
} |
207
|
|
|
elseif ( $optionName === 'action' ) { |
208
|
|
|
return $this->getAllActions(); |
209
|
|
|
} |
210
|
|
|
elseif ( $optionName === 'kind' ) { |
211
|
|
|
return $this->getAllKinds(); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
return $ret; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* {@inheritdoc} |
219
|
|
|
*/ |
220
|
|
|
public function initialize(InputInterface $input, OutputInterface $output) |
221
|
|
|
{ |
222
|
|
|
parent::initialize($input, $output); |
223
|
|
|
|
224
|
|
|
$this->_revisionLog = $this->getRevisionLog($this->getWorkingCopyUrl()); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* {@inheritdoc} |
229
|
|
|
* |
230
|
|
|
* @throws \RuntimeException When both "--bugs" and "--revisions" options were specified. |
231
|
|
|
* @throws CommandException When specified revisions are not present in current project. |
232
|
|
|
* @throws CommandException When project contains no associated revisions. |
233
|
|
|
*/ |
234
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
235
|
|
|
{ |
236
|
|
|
$searching_start = microtime(true); |
237
|
|
|
$bugs = $this->getList($this->io->getOption('bugs')); |
238
|
|
|
$revisions = $this->getList($this->io->getOption('revisions')); |
239
|
|
|
|
240
|
|
|
if ( $bugs && $revisions ) { |
|
|
|
|
241
|
|
|
throw new \RuntimeException('The "--bugs" and "--revisions" options are mutually exclusive.'); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
$missing_revisions = array(); |
245
|
|
|
$revisions_by_path = $this->getRevisionsByPath(); |
246
|
|
|
|
247
|
|
|
if ( $revisions ) { |
|
|
|
|
248
|
|
|
$revisions = $this->_revisionListParser->expandRanges($revisions); |
249
|
|
|
$revisions_by_path = array_intersect($revisions_by_path, $revisions); |
250
|
|
|
$missing_revisions = array_diff($revisions, $revisions_by_path); |
251
|
|
|
} |
252
|
|
|
elseif ( $bugs ) { |
|
|
|
|
253
|
|
|
// Only show bug-related revisions on given path. The $missing_revisions is always empty. |
254
|
|
|
$revisions_from_bugs = $this->_revisionLog->find('bugs', $bugs); |
255
|
|
|
$revisions_by_path = array_intersect($revisions_by_path, $revisions_from_bugs); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$merged_by = $this->getList($this->io->getOption('merged-by')); |
259
|
|
|
|
260
|
|
|
if ( $merged_by ) { |
|
|
|
|
261
|
|
|
$merged_by = $this->_revisionListParser->expandRanges($merged_by); |
262
|
|
|
$revisions_by_path = $this->_revisionLog->find('merges', $merged_by); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
if ( $this->io->getOption('merges') ) { |
266
|
|
|
$revisions_by_path = array_intersect($revisions_by_path, $this->_revisionLog->find('merges', 'all_merges')); |
267
|
|
|
} |
268
|
|
|
elseif ( $this->io->getOption('no-merges') ) { |
269
|
|
|
$revisions_by_path = array_diff($revisions_by_path, $this->_revisionLog->find('merges', 'all_merges')); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
if ( $this->io->getOption('merged') ) { |
273
|
|
|
$revisions_by_path = array_intersect($revisions_by_path, $this->_revisionLog->find('merges', 'all_merged')); |
274
|
|
|
} |
275
|
|
|
elseif ( $this->io->getOption('not-merged') ) { |
276
|
|
|
$revisions_by_path = array_diff($revisions_by_path, $this->_revisionLog->find('merges', 'all_merged')); |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
$action = $this->io->getOption('action'); |
280
|
|
|
|
281
|
|
|
if ( $action ) { |
282
|
|
|
if ( !in_array($action, $this->getAllActions()) ) { |
283
|
|
|
throw new CommandException('The "' . $action . '" action is unknown.'); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
$revisions_by_path = array_intersect( |
287
|
|
|
$revisions_by_path, |
288
|
|
|
$this->_revisionLog->find('paths', 'action:' . $action) |
289
|
|
|
); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
$kind = $this->io->getOption('kind'); |
293
|
|
|
|
294
|
|
|
if ( $kind ) { |
295
|
|
|
if ( !in_array($kind, $this->getAllKinds()) ) { |
296
|
|
|
throw new CommandException('The "' . $kind . '" kind is unknown.'); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
$revisions_by_path = array_intersect( |
300
|
|
|
$revisions_by_path, |
301
|
|
|
$this->_revisionLog->find('paths', 'kind:' . $kind) |
302
|
|
|
); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
if ( $missing_revisions ) { |
|
|
|
|
306
|
|
|
throw new CommandException($this->getMissingRevisionsErrorMessage($missing_revisions)); |
307
|
|
|
} |
308
|
|
|
elseif ( !$revisions_by_path ) { |
|
|
|
|
309
|
|
|
throw new CommandException('No matching revisions found.'); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
rsort($revisions_by_path, SORT_NUMERIC); |
313
|
|
|
|
314
|
|
|
if ( $bugs || $revisions ) { |
|
|
|
|
315
|
|
|
// Don't limit revisions, when provided explicitly by user. |
316
|
|
|
$revisions_by_path_with_limit = $revisions_by_path; |
317
|
|
|
} |
318
|
|
|
else { |
319
|
|
|
// Apply limit only, when no explicit bugs/revisions are set. |
320
|
|
|
$revisions_by_path_with_limit = array_slice($revisions_by_path, 0, $this->getMaxCount()); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
$revisions_by_path_count = count($revisions_by_path); |
324
|
|
|
$revisions_by_path_with_limit_count = count($revisions_by_path_with_limit); |
325
|
|
|
|
326
|
|
|
if ( $revisions_by_path_with_limit_count === $revisions_by_path_count ) { |
327
|
|
|
$this->io->writeln(sprintf( |
328
|
|
|
' * Showing <info>%d</info> revision(-s) in %s:', |
329
|
|
|
$revisions_by_path_with_limit_count, |
330
|
|
|
$this->getRevisionLogIdentifier() |
331
|
|
|
)); |
332
|
|
|
} |
333
|
|
|
else { |
334
|
|
|
$this->io->writeln(sprintf( |
335
|
|
|
' * Showing <info>%d</info> of <info>%d</info> revision(-s) in %s:', |
336
|
|
|
$revisions_by_path_with_limit_count, |
337
|
|
|
$revisions_by_path_count, |
338
|
|
|
$this->getRevisionLogIdentifier() |
339
|
|
|
)); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
$searching_duration = microtime(true) - $searching_start; |
343
|
|
|
|
344
|
|
|
$printing_start = microtime(true); |
345
|
|
|
$this->printRevisions($revisions_by_path_with_limit); |
346
|
|
|
$printing_duration = microtime(true) - $printing_start; |
347
|
|
|
|
348
|
|
|
$debug_info = 'Generation Time: <info>' . round($searching_duration + $printing_duration, 2) . 's</info>'; |
349
|
|
|
$debug_info .= ' ('; |
350
|
|
|
$debug_info .= 'searching: <info>' . round($searching_duration, 2) . 's</info>;'; |
351
|
|
|
$debug_info .= ' printing: <info>' . round($printing_duration, 2) . 's</info>'; |
352
|
|
|
$debug_info .= ').'; |
353
|
|
|
|
354
|
|
|
$this->io->writeln( |
355
|
|
|
$debug_info |
356
|
|
|
); |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* Returns all refs. |
361
|
|
|
* |
362
|
|
|
* @return array |
363
|
|
|
*/ |
364
|
|
|
protected function getAllRefs() |
365
|
|
|
{ |
366
|
|
|
$ret = parent::getAllRefs(); |
367
|
|
|
$ret[] = self::ALL_REFS; |
368
|
|
|
|
369
|
|
|
return $ret; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* Returns all actions. |
374
|
|
|
* |
375
|
|
|
* @return array |
376
|
|
|
*/ |
377
|
|
|
protected function getAllActions() |
378
|
|
|
{ |
379
|
|
|
return array('A', 'M', 'R', 'D'); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* Returns all actions. |
384
|
|
|
* |
385
|
|
|
* @return array |
386
|
|
|
*/ |
387
|
|
|
protected function getAllKinds() |
388
|
|
|
{ |
389
|
|
|
return array('dir', 'file'); |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* Returns revision log identifier. |
394
|
|
|
* |
395
|
|
|
* @return string |
396
|
|
|
*/ |
397
|
|
|
protected function getRevisionLogIdentifier() |
398
|
|
|
{ |
399
|
|
|
$ret = '<info>' . $this->_revisionLog->getProjectPath() . '</info> project'; |
400
|
|
|
|
401
|
|
|
$ref_name = $this->_revisionLog->getRefName(); |
402
|
|
|
|
403
|
|
|
if ( $ref_name ) { |
404
|
|
|
$ret .= ' (ref: <info>' . $ref_name . '</info>)'; |
405
|
|
|
} |
406
|
|
|
else { |
407
|
|
|
$ret .= ' (all refs)'; |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
return $ret; |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* Shows error about missing revisions. |
415
|
|
|
* |
416
|
|
|
* @param array $missing_revisions Missing revisions. |
417
|
|
|
* |
418
|
|
|
* @return string |
419
|
|
|
*/ |
420
|
|
|
protected function getMissingRevisionsErrorMessage(array $missing_revisions) |
421
|
|
|
{ |
422
|
|
|
$refs = $this->io->getOption('refs'); |
423
|
|
|
$missing_revisions = implode(', ', $missing_revisions); |
424
|
|
|
|
425
|
|
|
if ( $refs ) { |
426
|
|
|
$revision_source = 'in "' . $refs . '" ref(-s)'; |
427
|
|
|
} |
428
|
|
|
else { |
429
|
|
|
$revision_source = 'at "' . $this->getWorkingCopyUrl() . '" url'; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
return 'The ' . $missing_revisions . ' revision(-s) not found ' . $revision_source . '.'; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* Returns list of revisions by path. |
437
|
|
|
* |
438
|
|
|
* @return array |
439
|
|
|
* @throws CommandException When given path doesn't exist. |
440
|
|
|
* @throws CommandException When given refs doesn't exist. |
441
|
|
|
*/ |
442
|
|
|
protected function getRevisionsByPath() |
443
|
|
|
{ |
444
|
|
|
// When "$path" points to deleted path the "$wc_path" will be parent folder of it (hopefully existing folder). |
445
|
|
|
$path = $this->io->getArgument('path'); |
446
|
|
|
$wc_path = $this->getWorkingCopyPath(); |
447
|
|
|
|
448
|
|
|
$refs = $this->getList($this->io->getOption('refs')); |
449
|
|
|
$relative_path = $this->repositoryConnector->getRelativePath($wc_path); |
450
|
|
|
|
451
|
|
|
if ( !$this->repositoryConnector->isUrl($wc_path) ) { |
452
|
|
|
$relative_path .= $this->_getPathDifference($wc_path, $path); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
$relative_path = $this->_crossReferencePathFromRepository($relative_path); |
456
|
|
|
|
457
|
|
|
if ( $relative_path === null ) { |
458
|
|
|
throw new CommandException( |
459
|
|
|
'The "' . $path . '" path not found in "' . $this->_revisionLog->getProjectPath() . '" project.' |
460
|
|
|
); |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
if ( $refs ) { |
|
|
|
|
464
|
|
|
$incorrect_refs = array_diff($refs, $this->getAllRefs()); |
465
|
|
|
|
466
|
|
|
if ( $incorrect_refs ) { |
|
|
|
|
467
|
|
|
throw new CommandException( |
468
|
|
|
'The following refs are unknown: "' . implode('", "', $incorrect_refs) . '".' |
469
|
|
|
); |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
return $this->_revisionLog->find('refs', $refs); |
473
|
|
|
} |
474
|
|
|
|
475
|
|
|
return $this->_revisionLog->find('paths', $relative_path); |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* Returns difference between 2 paths. |
480
|
|
|
* |
481
|
|
|
* @param string $main_path Main path. |
482
|
|
|
* @param string $sub_path Sub path. |
483
|
|
|
* |
484
|
|
|
* @return string |
485
|
|
|
*/ |
486
|
|
|
private function _getPathDifference($main_path, $sub_path) |
487
|
|
|
{ |
488
|
|
|
if ( $sub_path === '.' || strpos($sub_path, '../') !== false ) { |
489
|
|
|
$sub_path = realpath($sub_path); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
$adapted_sub_path = $sub_path; |
493
|
|
|
|
494
|
|
|
do { |
495
|
|
|
$sub_path_pos = strpos($main_path, $adapted_sub_path); |
496
|
|
|
|
497
|
|
|
if ( $sub_path_pos !== false ) { |
498
|
|
|
break; |
499
|
|
|
} |
500
|
|
|
|
501
|
|
|
$adapted_sub_path = dirname($adapted_sub_path); |
502
|
|
|
} while ( $adapted_sub_path !== '.' ); |
503
|
|
|
|
504
|
|
|
// No sub-matches. |
505
|
|
|
if ( !strlen($adapted_sub_path) ) { |
506
|
|
|
return ''; |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
return str_replace($adapted_sub_path, '', $sub_path); |
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
/** |
513
|
|
|
* Determines path kind from repository. |
514
|
|
|
* |
515
|
|
|
* @param string $path Path. |
516
|
|
|
* |
517
|
|
|
* @return string|null |
518
|
|
|
*/ |
519
|
|
|
private function _crossReferencePathFromRepository($path) |
520
|
|
|
{ |
521
|
|
|
$path = rtrim($path, '/'); |
522
|
|
|
$try_paths = array($path, $path . '/'); |
523
|
|
|
|
524
|
|
|
foreach ( $try_paths as $try_path ) { |
525
|
|
|
if ( $this->_revisionLog->find('paths', 'exact:' . $try_path) ) { |
526
|
|
|
return $try_path; |
527
|
|
|
} |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
return null; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* Returns displayed revision limit. |
535
|
|
|
* |
536
|
|
|
* @return integer |
537
|
|
|
*/ |
538
|
|
|
protected function getMaxCount() |
539
|
|
|
{ |
540
|
|
|
$max_count = $this->io->getOption('max-count'); |
541
|
|
|
|
542
|
|
|
if ( $max_count !== null ) { |
543
|
|
|
return $max_count; |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
return $this->getSetting(self::SETTING_LOG_LIMIT); |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
/** |
550
|
|
|
* Prints revisions. |
551
|
|
|
* |
552
|
|
|
* @param array $revisions Revisions. |
553
|
|
|
* |
554
|
|
|
* @return void |
555
|
|
|
*/ |
556
|
|
|
protected function printRevisions(array $revisions) |
557
|
|
|
{ |
558
|
|
|
$column_mapping = array( |
559
|
|
|
'with-details' => RevisionPrinter::COLUMN_DETAILS, |
560
|
|
|
'with-summary' => RevisionPrinter::COLUMN_SUMMARY, |
561
|
|
|
'with-refs' => RevisionPrinter::COLUMN_REFS, |
562
|
|
|
'with-merge-oracle' => RevisionPrinter::COLUMN_MERGE_ORACLE, |
563
|
|
|
'with-merge-status' => RevisionPrinter::COLUMN_MERGE_STATUS, |
564
|
|
|
); |
565
|
|
|
|
566
|
|
|
foreach ( $column_mapping as $option_name => $column ) { |
567
|
|
|
if ( $this->io->getOption($option_name) ) { |
568
|
|
|
$this->_revisionPrinter->withColumn($column); |
569
|
|
|
} |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
$this->_revisionPrinter->setMergeConflictRegExps($this->getSetting(self::SETTING_LOG_MERGE_CONFLICT_REGEXPS)); |
573
|
|
|
$this->_revisionPrinter->setLogMessageLimit($this->getSetting(self::SETTING_LOG_MESSAGE_LIMIT)); |
574
|
|
|
|
575
|
|
|
$wc_path = $this->getWorkingCopyPath(); |
576
|
|
|
|
577
|
|
|
if ( !$this->repositoryConnector->isUrl($wc_path) ) { |
578
|
|
|
$this->_revisionPrinter->setCurrentRevision( |
579
|
|
|
$this->repositoryConnector->getLastRevision($wc_path) |
580
|
|
|
); |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
$this->_revisionPrinter->printRevisions($this->_revisionLog, $revisions, $this->io->getOutput()); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* Returns list of config settings. |
588
|
|
|
* |
589
|
|
|
* @return AbstractConfigSetting[] |
590
|
|
|
*/ |
591
|
|
|
public function getConfigSettings() |
592
|
|
|
{ |
593
|
|
|
return array( |
594
|
|
|
new IntegerConfigSetting(self::SETTING_LOG_LIMIT, 10), |
595
|
|
|
new IntegerConfigSetting(self::SETTING_LOG_MESSAGE_LIMIT, 68), |
596
|
|
|
new RegExpsConfigSetting(self::SETTING_LOG_MERGE_CONFLICT_REGEXPS, '#/composer\.lock$#'), |
597
|
|
|
); |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
/** |
601
|
|
|
* Returns option names, that makes sense to use in aggregation mode. |
602
|
|
|
* |
603
|
|
|
* @return array |
604
|
|
|
*/ |
605
|
|
|
public function getAggregatedOptions() |
606
|
|
|
{ |
607
|
|
|
return array( |
608
|
|
|
'merges', 'no-merges', 'merged', 'not-merged', 'action', |
609
|
|
|
'kind', 'with-details', 'with-summary', 'with-refs', |
610
|
|
|
'with-merge-oracle', 'with-merge-status', 'max-count', |
611
|
|
|
); |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
} |
615
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.