console-helpers /
svn-buddy
| 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\ConsoleKit\Exception\CommandException; |
||||
| 15 | use ConsoleHelpers\SVNBuddy\Config\AbstractConfigSetting; |
||||
| 16 | use ConsoleHelpers\SVNBuddy\Config\IntegerConfigSetting; |
||||
| 17 | use ConsoleHelpers\SVNBuddy\Config\RegExpsConfigSetting; |
||||
| 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 | 'author', |
||||
| 154 | null, |
||||
| 155 | InputOption::VALUE_REQUIRED, |
||||
| 156 | 'Show revisions, made by a given author' |
||||
| 157 | ) |
||||
| 158 | ->addOption( |
||||
| 159 | 'with-full-message', |
||||
| 160 | 'f', |
||||
| 161 | InputOption::VALUE_NONE, |
||||
| 162 | 'Shows non-truncated commit messages' |
||||
| 163 | ) |
||||
| 164 | ->addOption( |
||||
| 165 | 'with-details', |
||||
| 166 | 'd', |
||||
| 167 | InputOption::VALUE_NONE, |
||||
| 168 | 'Shows detailed revision information, e.g. paths affected' |
||||
| 169 | ) |
||||
| 170 | ->addOption( |
||||
| 171 | 'with-summary', |
||||
| 172 | 's', |
||||
| 173 | InputOption::VALUE_NONE, |
||||
| 174 | 'Shows number of added/changed/removed paths in the revision' |
||||
| 175 | ) |
||||
| 176 | ->addOption( |
||||
| 177 | 'with-refs', |
||||
| 178 | null, |
||||
| 179 | InputOption::VALUE_NONE, |
||||
| 180 | 'Shows revision refs' |
||||
| 181 | ) |
||||
| 182 | ->addOption( |
||||
| 183 | 'with-merge-oracle', |
||||
| 184 | null, |
||||
| 185 | InputOption::VALUE_NONE, |
||||
| 186 | 'Shows number of paths in the revision, that can cause conflict upon merging' |
||||
| 187 | ) |
||||
| 188 | ->addOption( |
||||
| 189 | 'with-merge-status', |
||||
| 190 | null, |
||||
| 191 | InputOption::VALUE_NONE, |
||||
| 192 | 'Shows merge revisions affecting this revision' |
||||
| 193 | ) |
||||
| 194 | ->addOption( |
||||
| 195 | 'max-count', |
||||
| 196 | null, |
||||
| 197 | InputOption::VALUE_REQUIRED, |
||||
| 198 | 'Limit the number of revisions to output' |
||||
| 199 | ) |
||||
| 200 | ->addOption( |
||||
| 201 | 'aggregate', |
||||
| 202 | 'a', |
||||
| 203 | InputOption::VALUE_NONE, |
||||
| 204 | 'Aggregate displayed revisions by bugs' |
||||
| 205 | ); |
||||
| 206 | |||||
| 207 | parent::configure(); |
||||
| 208 | } |
||||
| 209 | |||||
| 210 | /** |
||||
| 211 | * Return possible values for the named option |
||||
| 212 | * |
||||
| 213 | * @param string $optionName Option name. |
||||
| 214 | * @param CompletionContext $context Completion context. |
||||
| 215 | * |
||||
| 216 | * @return array |
||||
| 217 | */ |
||||
| 218 | public function completeOptionValues($optionName, CompletionContext $context) |
||||
| 219 | { |
||||
| 220 | $ret = parent::completeOptionValues($optionName, $context); |
||||
| 221 | |||||
| 222 | if ( $optionName === 'refs' ) { |
||||
| 223 | return $this->getAllRefs(); |
||||
| 224 | } |
||||
| 225 | elseif ( $optionName === 'action' ) { |
||||
| 226 | return $this->getAllActions(); |
||||
| 227 | } |
||||
| 228 | elseif ( $optionName === 'kind' ) { |
||||
| 229 | return $this->getAllKinds(); |
||||
| 230 | } |
||||
| 231 | |||||
| 232 | return $ret; |
||||
| 233 | } |
||||
| 234 | |||||
| 235 | /** |
||||
| 236 | * {@inheritdoc} |
||||
| 237 | */ |
||||
| 238 | public function initialize(InputInterface $input, OutputInterface $output) |
||||
| 239 | { |
||||
| 240 | parent::initialize($input, $output); |
||||
| 241 | |||||
| 242 | $this->_revisionLog = $this->getRevisionLog($this->getWorkingCopyUrl()); |
||||
| 243 | } |
||||
| 244 | |||||
| 245 | /** |
||||
| 246 | * {@inheritdoc} |
||||
| 247 | * |
||||
| 248 | * @throws CommandException When specified revisions are not present in current project. |
||||
| 249 | * @throws CommandException When project contains no associated revisions. |
||||
| 250 | * @throws CommandException When bugs from "--bugs" option are not found. |
||||
| 251 | */ |
||||
| 252 | protected function execute(InputInterface $input, OutputInterface $output) |
||||
| 253 | { |
||||
| 254 | $searching_start = microtime(true); |
||||
| 255 | $bugs = $this->getList($this->io->getOption('bugs')); |
||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 256 | $revisions = $this->getList($this->io->getOption('revisions')); |
||||
| 257 | |||||
| 258 | $missing_revisions = array(); |
||||
| 259 | $revisions_by_path = $this->getRevisionsByPath(); |
||||
| 260 | |||||
| 261 | if ( $revisions || $bugs ) { |
||||
|
0 ignored issues
–
show
The expression
$revisions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
The expression
$bugs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 262 | if ( $revisions ) { |
||||
|
0 ignored issues
–
show
The expression
$revisions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 263 | $revisions = $this->_revisionListParser->expandRanges($revisions); |
||||
| 264 | |||||
| 265 | // Don't check missing revisions from bugs, because they can affect different repository paths. |
||||
| 266 | $missing_revisions = array_diff($revisions, $revisions_by_path); |
||||
| 267 | } |
||||
| 268 | |||||
| 269 | if ( $bugs ) { |
||||
|
0 ignored issues
–
show
The expression
$bugs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 270 | $revisions_from_bugs = $this->_revisionLog->find('bugs', $bugs); |
||||
| 271 | |||||
| 272 | if ( !$revisions_from_bugs ) { |
||||
|
0 ignored issues
–
show
The expression
$revisions_from_bugs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 273 | throw new CommandException('Specified bugs aren\'t mentioned in any of revisions'); |
||||
| 274 | } |
||||
| 275 | |||||
| 276 | $revisions = array_merge($revisions, $revisions_from_bugs); |
||||
| 277 | } |
||||
| 278 | |||||
| 279 | // Only show revisions on given path. |
||||
| 280 | $revisions_by_path = array_intersect($revisions_by_path, $revisions); |
||||
| 281 | } |
||||
| 282 | |||||
| 283 | $merged_by = $this->getList($this->io->getOption('merged-by')); |
||||
| 284 | |||||
| 285 | if ( $merged_by ) { |
||||
|
0 ignored issues
–
show
The expression
$merged_by of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 286 | $merged_by = $this->_revisionListParser->expandRanges($merged_by); |
||||
| 287 | $revisions_by_path = $this->_revisionLog->find('merges', $merged_by); |
||||
| 288 | } |
||||
| 289 | |||||
| 290 | if ( $this->io->getOption('merges') ) { |
||||
| 291 | $revisions_by_path = array_intersect($revisions_by_path, $this->_revisionLog->find('merges', 'all_merges')); |
||||
| 292 | } |
||||
| 293 | elseif ( $this->io->getOption('no-merges') ) { |
||||
| 294 | $revisions_by_path = array_diff($revisions_by_path, $this->_revisionLog->find('merges', 'all_merges')); |
||||
| 295 | } |
||||
| 296 | |||||
| 297 | if ( $this->io->getOption('merged') ) { |
||||
| 298 | $revisions_by_path = array_intersect($revisions_by_path, $this->_revisionLog->find('merges', 'all_merged')); |
||||
| 299 | } |
||||
| 300 | elseif ( $this->io->getOption('not-merged') ) { |
||||
| 301 | $revisions_by_path = array_diff($revisions_by_path, $this->_revisionLog->find('merges', 'all_merged')); |
||||
| 302 | } |
||||
| 303 | |||||
| 304 | $action = $this->io->getOption('action'); |
||||
| 305 | |||||
| 306 | if ( $action ) { |
||||
| 307 | if ( !in_array($action, $this->getAllActions()) ) { |
||||
| 308 | throw new CommandException('The "' . $action . '" action is unknown.'); |
||||
|
0 ignored issues
–
show
Are you sure
$action of type string|string[]|true can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 309 | } |
||||
| 310 | |||||
| 311 | $revisions_by_path = array_intersect( |
||||
| 312 | $revisions_by_path, |
||||
| 313 | $this->_revisionLog->find('paths', 'action:' . $action) |
||||
| 314 | ); |
||||
| 315 | } |
||||
| 316 | |||||
| 317 | $kind = $this->io->getOption('kind'); |
||||
| 318 | |||||
| 319 | if ( $kind ) { |
||||
| 320 | if ( !in_array($kind, $this->getAllKinds()) ) { |
||||
| 321 | throw new CommandException('The "' . $kind . '" kind is unknown.'); |
||||
|
0 ignored issues
–
show
Are you sure
$kind of type string|string[]|true can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 322 | } |
||||
| 323 | |||||
| 324 | $revisions_by_path = array_intersect( |
||||
| 325 | $revisions_by_path, |
||||
| 326 | $this->_revisionLog->find('paths', 'kind:' . $kind) |
||||
| 327 | ); |
||||
| 328 | } |
||||
| 329 | |||||
| 330 | $author = $this->io->getOption('author'); |
||||
| 331 | |||||
| 332 | if ( $author ) { |
||||
| 333 | $revisions_by_path = array_intersect( |
||||
| 334 | $revisions_by_path, |
||||
| 335 | $this->_revisionLog->find('summary', 'author:' . $author) |
||||
| 336 | ); |
||||
| 337 | } |
||||
| 338 | |||||
| 339 | if ( $missing_revisions ) { |
||||
| 340 | throw new CommandException($this->getMissingRevisionsErrorMessage($missing_revisions)); |
||||
| 341 | } |
||||
| 342 | elseif ( !$revisions_by_path ) { |
||||
|
0 ignored issues
–
show
The expression
$revisions_by_path of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 343 | throw new CommandException('No matching revisions found.'); |
||||
| 344 | } |
||||
| 345 | |||||
| 346 | rsort($revisions_by_path, SORT_NUMERIC); |
||||
| 347 | |||||
| 348 | if ( $bugs || $revisions ) { |
||||
|
0 ignored issues
–
show
The expression
$revisions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
The expression
$bugs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 349 | // Don't limit revisions, when provided explicitly by user. |
||||
| 350 | $revisions_by_path_with_limit = $revisions_by_path; |
||||
| 351 | } |
||||
| 352 | else { |
||||
| 353 | // Apply limit only, when no explicit bugs/revisions are set. |
||||
| 354 | $revisions_by_path_with_limit = array_slice($revisions_by_path, 0, $this->getMaxCount()); |
||||
| 355 | } |
||||
| 356 | |||||
| 357 | $revisions_by_path_count = count($revisions_by_path); |
||||
| 358 | $revisions_by_path_with_limit_count = count($revisions_by_path_with_limit); |
||||
| 359 | |||||
| 360 | if ( $revisions_by_path_with_limit_count === $revisions_by_path_count ) { |
||||
| 361 | $this->io->writeln(sprintf( |
||||
| 362 | ' * Showing <info>%d</info> revision(-s) in %s:', |
||||
| 363 | $revisions_by_path_with_limit_count, |
||||
| 364 | $this->getRevisionLogIdentifier() |
||||
| 365 | )); |
||||
| 366 | } |
||||
| 367 | else { |
||||
| 368 | $this->io->writeln(sprintf( |
||||
| 369 | ' * Showing <info>%d</info> of <info>%d</info> revision(-s) in %s:', |
||||
| 370 | $revisions_by_path_with_limit_count, |
||||
| 371 | $revisions_by_path_count, |
||||
| 372 | $this->getRevisionLogIdentifier() |
||||
| 373 | )); |
||||
| 374 | } |
||||
| 375 | |||||
| 376 | $searching_duration = microtime(true) - $searching_start; |
||||
| 377 | |||||
| 378 | $printing_start = microtime(true); |
||||
| 379 | $this->printRevisions($revisions_by_path_with_limit); |
||||
| 380 | $printing_duration = microtime(true) - $printing_start; |
||||
| 381 | |||||
| 382 | $debug_info = 'Generation Time: <info>' . round($searching_duration + $printing_duration, 2) . 's</info>'; |
||||
| 383 | $debug_info .= ' ('; |
||||
| 384 | $debug_info .= 'searching: <info>' . round($searching_duration, 2) . 's</info>;'; |
||||
| 385 | $debug_info .= ' printing: <info>' . round($printing_duration, 2) . 's</info>'; |
||||
| 386 | $debug_info .= ').'; |
||||
| 387 | |||||
| 388 | $this->io->writeln( |
||||
| 389 | $debug_info |
||||
| 390 | ); |
||||
| 391 | } |
||||
| 392 | |||||
| 393 | /** |
||||
| 394 | * Returns all refs. |
||||
| 395 | * |
||||
| 396 | * @return array |
||||
| 397 | */ |
||||
| 398 | protected function getAllRefs() |
||||
| 399 | { |
||||
| 400 | $ret = parent::getAllRefs(); |
||||
| 401 | $ret[] = self::ALL_REFS; |
||||
| 402 | |||||
| 403 | return $ret; |
||||
| 404 | } |
||||
| 405 | |||||
| 406 | /** |
||||
| 407 | * Returns all actions. |
||||
| 408 | * |
||||
| 409 | * @return array |
||||
| 410 | */ |
||||
| 411 | protected function getAllActions() |
||||
| 412 | { |
||||
| 413 | return array('A', 'M', 'R', 'D'); |
||||
| 414 | } |
||||
| 415 | |||||
| 416 | /** |
||||
| 417 | * Returns all actions. |
||||
| 418 | * |
||||
| 419 | * @return array |
||||
| 420 | */ |
||||
| 421 | protected function getAllKinds() |
||||
| 422 | { |
||||
| 423 | return array('dir', 'file'); |
||||
| 424 | } |
||||
| 425 | |||||
| 426 | /** |
||||
| 427 | * Returns revision log identifier. |
||||
| 428 | * |
||||
| 429 | * @return string |
||||
| 430 | */ |
||||
| 431 | protected function getRevisionLogIdentifier() |
||||
| 432 | { |
||||
| 433 | $ret = '<info>' . $this->_revisionLog->getProjectPath() . '</info> project'; |
||||
| 434 | |||||
| 435 | $ref_name = $this->_revisionLog->getRefName(); |
||||
| 436 | |||||
| 437 | if ( $ref_name ) { |
||||
| 438 | $ret .= ' (ref: <info>' . $ref_name . '</info>)'; |
||||
| 439 | } |
||||
| 440 | else { |
||||
| 441 | $ret .= ' (all refs)'; |
||||
| 442 | } |
||||
| 443 | |||||
| 444 | return $ret; |
||||
| 445 | } |
||||
| 446 | |||||
| 447 | /** |
||||
| 448 | * Shows error about missing revisions. |
||||
| 449 | * |
||||
| 450 | * @param array $missing_revisions Missing revisions. |
||||
| 451 | * |
||||
| 452 | * @return string |
||||
| 453 | */ |
||||
| 454 | protected function getMissingRevisionsErrorMessage(array $missing_revisions) |
||||
| 455 | { |
||||
| 456 | $refs = $this->io->getOption('refs'); |
||||
| 457 | $missing_revisions = implode(', ', $missing_revisions); |
||||
| 458 | |||||
| 459 | if ( $refs ) { |
||||
| 460 | $revision_source = 'in "' . $refs . '" ref(-s)'; |
||||
|
0 ignored issues
–
show
Are you sure
$refs of type string|string[]|true can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 461 | } |
||||
| 462 | else { |
||||
| 463 | $revision_source = 'at "' . $this->getWorkingCopyUrl() . '" url'; |
||||
| 464 | } |
||||
| 465 | |||||
| 466 | return 'The ' . $missing_revisions . ' revision(-s) not found ' . $revision_source . '.'; |
||||
| 467 | } |
||||
| 468 | |||||
| 469 | /** |
||||
| 470 | * Returns list of revisions by path. |
||||
| 471 | * |
||||
| 472 | * @return array |
||||
| 473 | * @throws CommandException When given path doesn't exist. |
||||
| 474 | * @throws CommandException When given refs doesn't exist. |
||||
| 475 | */ |
||||
| 476 | protected function getRevisionsByPath() |
||||
| 477 | { |
||||
| 478 | // When "$path" points to deleted path the "$wc_path" will be parent folder of it (hopefully existing folder). |
||||
| 479 | $path = $this->io->getArgument('path'); |
||||
| 480 | $wc_path = $this->getWorkingCopyPath(); |
||||
| 481 | |||||
| 482 | $refs = $this->getList($this->io->getOption('refs')); |
||||
|
0 ignored issues
–
show
It seems like
$this->io->getOption('refs') can also be of type string[]; however, parameter $string of ConsoleHelpers\SVNBuddy\...tractCommand::getList() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 483 | $relative_path = $this->repositoryConnector->getRelativePath($wc_path); |
||||
| 484 | |||||
| 485 | if ( !$this->repositoryConnector->isUrl($wc_path) ) { |
||||
| 486 | $relative_path .= $this->_getPathDifference($wc_path, $path); |
||||
|
0 ignored issues
–
show
It seems like
$path can also be of type string[]; however, parameter $sub_path of ConsoleHelpers\SVNBuddy\...d::_getPathDifference() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 487 | } |
||||
| 488 | |||||
| 489 | $relative_path = $this->_crossReferencePathFromRepository($relative_path); |
||||
| 490 | |||||
| 491 | if ( $relative_path === null ) { |
||||
| 492 | throw new CommandException( |
||||
| 493 | 'The "' . $path . '" path not found in "' . $this->_revisionLog->getProjectPath() . '" project.' |
||||
|
0 ignored issues
–
show
Are you sure
$path of type null|string|string[] can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 494 | ); |
||||
| 495 | } |
||||
| 496 | |||||
| 497 | if ( $refs ) { |
||||
|
0 ignored issues
–
show
The expression
$refs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 498 | $incorrect_refs = array_diff($refs, $this->getAllRefs()); |
||||
| 499 | |||||
| 500 | if ( $incorrect_refs ) { |
||||
|
0 ignored issues
–
show
The expression
$incorrect_refs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 Loading history...
|
|||||
| 501 | throw new CommandException( |
||||
| 502 | 'The following refs are unknown: "' . implode('", "', $incorrect_refs) . '".' |
||||
| 503 | ); |
||||
| 504 | } |
||||
| 505 | |||||
| 506 | return $this->_revisionLog->find('refs', $refs); |
||||
| 507 | } |
||||
| 508 | |||||
| 509 | return $this->_revisionLog->find('paths', $relative_path); |
||||
| 510 | } |
||||
| 511 | |||||
| 512 | /** |
||||
| 513 | * Returns difference between 2 paths. |
||||
| 514 | * |
||||
| 515 | * @param string $main_path Main path. |
||||
| 516 | * @param string $sub_path Sub path. |
||||
| 517 | * |
||||
| 518 | * @return string |
||||
| 519 | */ |
||||
| 520 | private function _getPathDifference($main_path, $sub_path) |
||||
| 521 | { |
||||
| 522 | if ( $sub_path === '.' || strpos($sub_path, '../') !== false ) { |
||||
| 523 | $sub_path = realpath($sub_path); |
||||
| 524 | } |
||||
| 525 | |||||
| 526 | $adapted_sub_path = $sub_path; |
||||
| 527 | |||||
| 528 | do { |
||||
| 529 | $sub_path_pos = strpos($main_path, $adapted_sub_path); |
||||
| 530 | |||||
| 531 | if ( $sub_path_pos !== false ) { |
||||
| 532 | break; |
||||
| 533 | } |
||||
| 534 | |||||
| 535 | $adapted_sub_path = dirname($adapted_sub_path); |
||||
| 536 | } while ( $adapted_sub_path !== '.' ); |
||||
| 537 | |||||
| 538 | // No sub-matches. |
||||
| 539 | if ( !strlen($adapted_sub_path) ) { |
||||
| 540 | return ''; |
||||
| 541 | } |
||||
| 542 | |||||
| 543 | return str_replace($adapted_sub_path, '', $sub_path); |
||||
| 544 | } |
||||
| 545 | |||||
| 546 | /** |
||||
| 547 | * Determines path kind from repository. |
||||
| 548 | * |
||||
| 549 | * @param string $path Path. |
||||
| 550 | * |
||||
| 551 | * @return string|null |
||||
| 552 | */ |
||||
| 553 | private function _crossReferencePathFromRepository($path) |
||||
| 554 | { |
||||
| 555 | $path = rtrim($path, '/'); |
||||
| 556 | $try_paths = array($path, $path . '/'); |
||||
| 557 | |||||
| 558 | foreach ( $try_paths as $try_path ) { |
||||
| 559 | if ( $this->_revisionLog->find('paths', 'exact:' . $try_path) ) { |
||||
| 560 | return $try_path; |
||||
| 561 | } |
||||
| 562 | } |
||||
| 563 | |||||
| 564 | return null; |
||||
| 565 | } |
||||
| 566 | |||||
| 567 | /** |
||||
| 568 | * Returns displayed revision limit. |
||||
| 569 | * |
||||
| 570 | * @return integer |
||||
| 571 | */ |
||||
| 572 | protected function getMaxCount() |
||||
| 573 | { |
||||
| 574 | $max_count = $this->io->getOption('max-count'); |
||||
| 575 | |||||
| 576 | if ( $max_count !== null ) { |
||||
| 577 | return $max_count; |
||||
|
0 ignored issues
–
show
|
|||||
| 578 | } |
||||
| 579 | |||||
| 580 | return $this->getSetting(self::SETTING_LOG_LIMIT); |
||||
| 581 | } |
||||
| 582 | |||||
| 583 | /** |
||||
| 584 | * Prints revisions. |
||||
| 585 | * |
||||
| 586 | * @param array $revisions Revisions. |
||||
| 587 | * |
||||
| 588 | * @return void |
||||
| 589 | */ |
||||
| 590 | protected function printRevisions(array $revisions) |
||||
| 591 | { |
||||
| 592 | $column_mapping = array( |
||||
| 593 | 'with-full-message' => RevisionPrinter::COLUMN_FULL_MESSAGE, |
||||
| 594 | 'with-details' => RevisionPrinter::COLUMN_DETAILS, |
||||
| 595 | 'with-summary' => RevisionPrinter::COLUMN_SUMMARY, |
||||
| 596 | 'with-refs' => RevisionPrinter::COLUMN_REFS, |
||||
| 597 | 'with-merge-oracle' => RevisionPrinter::COLUMN_MERGE_ORACLE, |
||||
| 598 | 'with-merge-status' => RevisionPrinter::COLUMN_MERGE_STATUS, |
||||
| 599 | ); |
||||
| 600 | |||||
| 601 | foreach ( $column_mapping as $option_name => $column ) { |
||||
| 602 | if ( $this->io->getOption($option_name) ) { |
||||
| 603 | $this->_revisionPrinter->withColumn($column); |
||||
| 604 | } |
||||
| 605 | } |
||||
| 606 | |||||
| 607 | $this->_revisionPrinter->setMergeConflictRegExps($this->getSetting(self::SETTING_LOG_MERGE_CONFLICT_REGEXPS)); |
||||
| 608 | $this->_revisionPrinter->setLogMessageLimit($this->getSetting(self::SETTING_LOG_MESSAGE_LIMIT)); |
||||
| 609 | |||||
| 610 | $wc_path = $this->getWorkingCopyPath(); |
||||
| 611 | |||||
| 612 | if ( !$this->repositoryConnector->isUrl($wc_path) ) { |
||||
| 613 | $this->_revisionPrinter->setCurrentRevision( |
||||
| 614 | $this->repositoryConnector->getLastRevision($wc_path) |
||||
| 615 | ); |
||||
| 616 | } |
||||
| 617 | |||||
| 618 | if ( $this->io->getOption('aggregate') ) { |
||||
| 619 | $this->_revisionPrinter->setAggregateByBug(true); |
||||
| 620 | } |
||||
| 621 | |||||
| 622 | $this->_revisionPrinter->printRevisions($this->_revisionLog, $revisions, $this->io->getOutput()); |
||||
| 623 | } |
||||
| 624 | |||||
| 625 | /** |
||||
| 626 | * Returns list of config settings. |
||||
| 627 | * |
||||
| 628 | * @return AbstractConfigSetting[] |
||||
| 629 | */ |
||||
| 630 | public function getConfigSettings() |
||||
| 631 | { |
||||
| 632 | return array( |
||||
| 633 | new IntegerConfigSetting(self::SETTING_LOG_LIMIT, 10), |
||||
| 634 | new IntegerConfigSetting(self::SETTING_LOG_MESSAGE_LIMIT, 68), |
||||
| 635 | new RegExpsConfigSetting(self::SETTING_LOG_MERGE_CONFLICT_REGEXPS, '#/composer\.lock$#'), |
||||
| 636 | ); |
||||
| 637 | } |
||||
| 638 | |||||
| 639 | /** |
||||
| 640 | * Returns option names, that makes sense to use in aggregation mode. |
||||
| 641 | * |
||||
| 642 | * @return array |
||||
| 643 | */ |
||||
| 644 | public function getAggregatedOptions() |
||||
| 645 | { |
||||
| 646 | return array( |
||||
| 647 | 'merges', 'no-merges', 'merged', 'not-merged', 'action', |
||||
| 648 | 'kind', 'author', 'with-full-message', 'with-details', 'with-summary', |
||||
| 649 | 'with-refs', 'with-merge-oracle', 'with-merge-status', 'max-count', |
||||
| 650 | ); |
||||
| 651 | } |
||||
| 652 | |||||
| 653 | } |
||||
| 654 |