From a201d8672daa2159b431d7bd6e6b0edb3a105eb5 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 17 Jun 2024 11:25:41 +0300 Subject: [PATCH] Added the "reparse" command for specific revision data reparsing --- CHANGELOG.md | 1 + README.md | 23 +++ src/SVNBuddy/Command/ReparseCommand.php | 81 ++++++++ src/SVNBuddy/Container.php | 4 + .../DatabaseCollectorPlugin/BugsPlugin.php | 35 +++- .../Plugin/IOverwriteAwarePlugin.php | 33 ++++ .../AbstractRepositoryCollectorPlugin.php | 39 +++- .../MergesPlugin.php | 19 +- .../RepositoryCollectorPlugin/PathsPlugin.php | 10 + .../SummaryPlugin.php | 20 +- .../Plugin/TOverwriteAwarePlugin.php | 40 ++++ .../RevisionLog/RepositoryFiller.php | 48 +++++ .../Repository/RevisionLog/RevisionLog.php | 176 ++++++++++++++---- .../Plugin/AbstractPluginTestCase.php | 14 ++ .../RevisionLog/Plugin/BugsPluginTest.php | 73 ++++++++ .../RevisionLog/Plugin/MergesPluginTest.php | 34 ++++ .../RevisionLog/Plugin/SummaryPluginTest.php | 31 +++ .../RevisionLog/RepositoryFillerTest.php | 73 ++++++++ 18 files changed, 707 insertions(+), 47 deletions(-) create mode 100644 src/SVNBuddy/Command/ReparseCommand.php create mode 100644 src/SVNBuddy/Repository/RevisionLog/Plugin/IOverwriteAwarePlugin.php create mode 100644 src/SVNBuddy/Repository/RevisionLog/Plugin/TOverwriteAwarePlugin.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 477c0a3..23d944c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added - The `commit` command now highlights the line with a commited revision number. +- Added `reparse` command for reparsing data of a given revision (e.g. when it's log message was changed in repository). ### Changed ... diff --git a/README.md b/README.md index 64428cd..95e3b28 100755 --- a/README.md +++ b/README.md @@ -760,6 +760,29 @@ Displays project meta information in following format: ![project meta information](docs/images/SvnBuddy_ProjectCommand_ShowMetaOption.png) +### The "reparse" command + +CReparses given revision. + +#### Arguments + +* `path` - Working copy path [default: "`.`"] + + +#### Options + +* `-r`, `--revision=REVISION` - Reparse specified revision + + +#### Examples + +``` +svn-buddy.phar reparse --revision 12345 +``` + +Re-reads and reparses 12345 revision information. + + ### The "self-update" command Updates application to most recent version. Following update channels are available: diff --git a/src/SVNBuddy/Command/ReparseCommand.php b/src/SVNBuddy/Command/ReparseCommand.php new file mode 100644 index 0000000..2dfd1b8 --- /dev/null +++ b/src/SVNBuddy/Command/ReparseCommand.php @@ -0,0 +1,81 @@ + + * @link https://github.com/console-helpers/svn-buddy + */ + +namespace ConsoleHelpers\SVNBuddy\Command; + + +use ConsoleHelpers\ConsoleKit\Exception\CommandException; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\RevisionLog; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class ReparseCommand extends AbstractCommand +{ + + /** + * Revision log + * + * @var RevisionLog + */ + private $_revisionLog; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('reparse') + ->setDescription('Reparses given revision') + ->addArgument( + 'path', + InputArgument::OPTIONAL, + 'Working copy path', + '.' + ) + ->addOption( + 'revision', + 'r', + InputOption::VALUE_REQUIRED, + 'Reparse specified revision' + ); + + parent::configure(); + } + + /** + * {@inheritdoc} + */ + public function initialize(InputInterface $input, OutputInterface $output) + { + parent::initialize($input, $output); + + $this->_revisionLog = $this->getRevisionLog($this->getWorkingCopyUrl()); + } + + /** + * {@inheritdoc} + * + * @throws CommandException When mandatory "revision" option wasn't given. + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $revision = $this->io->getOption('revision'); + + if ( !$revision ) { + throw new CommandException('The "revision" option is mandatory.'); + } + + $this->_revisionLog->reparse($revision); + } + +} diff --git a/src/SVNBuddy/Container.php b/src/SVNBuddy/Container.php index 439dd10..30dd8d8 100644 --- a/src/SVNBuddy/Container.php +++ b/src/SVNBuddy/Container.php @@ -27,6 +27,7 @@ use ConsoleHelpers\SVNBuddy\Command\LogCommand; use ConsoleHelpers\SVNBuddy\Command\MergeCommand; use ConsoleHelpers\SVNBuddy\Command\ProjectCommand; +use ConsoleHelpers\SVNBuddy\Command\ReparseCommand; use ConsoleHelpers\SVNBuddy\Command\RevertCommand; use ConsoleHelpers\SVNBuddy\Command\SearchCommand; use ConsoleHelpers\SVNBuddy\Command\SelfUpdateCommand; @@ -284,6 +285,9 @@ protected function addCommandFactories() $this->commandFactories['revert'] = function () { return new RevertCommand(); }; + $this->commandFactories['reparse'] = function () { + return new ReparseCommand(); + }; $this->commandFactories['log'] = function () { return new LogCommand(); }; diff --git a/src/SVNBuddy/Repository/RevisionLog/Plugin/DatabaseCollectorPlugin/BugsPlugin.php b/src/SVNBuddy/Repository/RevisionLog/Plugin/DatabaseCollectorPlugin/BugsPlugin.php index 522e4c9..accf543 100644 --- a/src/SVNBuddy/Repository/RevisionLog/Plugin/DatabaseCollectorPlugin/BugsPlugin.php +++ b/src/SVNBuddy/Repository/RevisionLog/Plugin/DatabaseCollectorPlugin/BugsPlugin.php @@ -14,13 +14,19 @@ use Aura\Sql\ExtendedPdoInterface; use ConsoleHelpers\SVNBuddy\Repository\Connector\Connector; use ConsoleHelpers\SVNBuddy\Repository\Parser\LogMessageParserFactory; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IOverwriteAwarePlugin; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\TOverwriteAwarePlugin; use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\RepositoryFiller; -class BugsPlugin extends AbstractDatabaseCollectorPlugin +class BugsPlugin extends AbstractDatabaseCollectorPlugin implements IOverwriteAwarePlugin { + use TOverwriteAwarePlugin; + const STATISTIC_BUG_ADDED_TO_COMMIT = 'bug_added_to_commit'; + const STATISTIC_BUG_REMOVED_FROM_COMMIT = 'bug_removed_from_commit'; + /** * Repository url. * @@ -83,7 +89,7 @@ public function getName() public function defineStatisticTypes() { return array( - self::STATISTIC_BUG_ADDED_TO_COMMIT, + self::STATISTIC_BUG_ADDED_TO_COMMIT, self::STATISTIC_BUG_REMOVED_FROM_COMMIT, ); } @@ -101,12 +107,35 @@ public function doProcess($from_revision, $to_revision) $last_revision = $this->getLastRevision(); - if ( $to_revision > $last_revision ) { + if ( $this->isOverwriteMode() ) { + $this->remove($from_revision, $to_revision); + $this->detectBugs($from_revision, $to_revision); + } + elseif ( $to_revision > $last_revision ) { $this->detectBugs($last_revision + 1, $to_revision); + } + + if ( $to_revision > $last_revision ) { $this->setLastRevision($to_revision); } } + /** + * Removes changes plugin made based on a given revision. + * + * @param integer $from_revision From revision. + * @param integer $to_revision To revision. + * + * @return void + */ + protected function remove($from_revision, $to_revision) + { + for ( $revision = $from_revision; $revision <= $to_revision; $revision++ ) { + $bug_count = $this->repositoryFiller->removeBugsFromCommit($revision); + $this->recordStatistic(self::STATISTIC_BUG_REMOVED_FROM_COMMIT, $bug_count); + } + } + /** * Populate "BugRegExp" column for projects without it. * diff --git a/src/SVNBuddy/Repository/RevisionLog/Plugin/IOverwriteAwarePlugin.php b/src/SVNBuddy/Repository/RevisionLog/Plugin/IOverwriteAwarePlugin.php new file mode 100644 index 0000000..b474612 --- /dev/null +++ b/src/SVNBuddy/Repository/RevisionLog/Plugin/IOverwriteAwarePlugin.php @@ -0,0 +1,33 @@ + + * @link https://github.com/console-helpers/svn-buddy + */ + +namespace ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin; + + +interface IOverwriteAwarePlugin +{ + + /** + * Sets overwrite mode. + * + * @param boolean $overwrite_mode Overwrite mode. + * + * @return void + */ + public function setOverwriteMode($overwrite_mode); + + /** + * Determines if overwrite mode is enabled. + * + * @return boolean + */ + public function isOverwriteMode(); + +} diff --git a/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/AbstractRepositoryCollectorPlugin.php b/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/AbstractRepositoryCollectorPlugin.php index a63e7a9..39b9439 100644 --- a/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/AbstractRepositoryCollectorPlugin.php +++ b/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/AbstractRepositoryCollectorPlugin.php @@ -12,6 +12,7 @@ use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\AbstractPlugin; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IOverwriteAwarePlugin; abstract class AbstractRepositoryCollectorPlugin extends AbstractPlugin implements IRepositoryCollectorPlugin { @@ -30,16 +31,31 @@ public function parse(\SimpleXMLElement $log) $last_processed_revision = null; $last_revision = $this->getLastRevision(); - foreach ( $log->logentry as $log_entry ) { - $revision = (int)$log_entry['revision']; + if ( $this instanceof IOverwriteAwarePlugin && $this->isOverwriteMode() ) { + foreach ( $log->logentry as $log_entry ) { + $revision = (int)$log_entry['revision']; - // Don't handle same revision twice. - if ( $revision <= $last_revision ) { - continue; + $this->remove($revision); + $this->doParse($revision, $log_entry); + + // When revision appeared only after overwrite parsing process. + if ( $revision > $last_revision ) { + $last_processed_revision = $revision; + } } + } + else { + foreach ( $log->logentry as $log_entry ) { + $revision = (int)$log_entry['revision']; + + // Don't handle same revision twice. + if ( $revision <= $last_revision ) { + continue; + } - $this->doParse($revision, $log_entry); - $last_processed_revision = $revision; + $this->doParse($revision, $log_entry); + $last_processed_revision = $revision; + } } if ( isset($last_processed_revision) ) { @@ -61,6 +77,15 @@ public function parse(\SimpleXMLElement $log) */ abstract protected function doParse($revision, \SimpleXMLElement $log_entry); + /** + * Removes changes plugin made based on a given revision. + * + * @param integer $revision Revision. + * + * @return void + */ + abstract protected function remove($revision); + /** * Returns revision query flags. * diff --git a/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/MergesPlugin.php b/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/MergesPlugin.php index b65e0fb..8df71a3 100644 --- a/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/MergesPlugin.php +++ b/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/MergesPlugin.php @@ -11,13 +11,19 @@ namespace ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\RepositoryCollectorPlugin; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IOverwriteAwarePlugin; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\TOverwriteAwarePlugin; use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\RevisionLog; -class MergesPlugin extends AbstractRepositoryCollectorPlugin +class MergesPlugin extends AbstractRepositoryCollectorPlugin implements IOverwriteAwarePlugin { + use TOverwriteAwarePlugin; + const STATISTIC_MERGE_ADDED = 'merge_added'; + const STATISTIC_MERGE_DELETED = 'merge_deleted'; + /** * Returns plugin name. * @@ -46,7 +52,7 @@ public function getRevisionQueryFlags() public function defineStatisticTypes() { return array( - self::STATISTIC_MERGE_ADDED, + self::STATISTIC_MERGE_ADDED, self::STATISTIC_MERGE_DELETED, ); } @@ -70,6 +76,15 @@ protected function doParse($revision, \SimpleXMLElement $log_entry) $this->recordStatistic(self::STATISTIC_MERGE_ADDED, count($merged_revisions)); } + /** + * @inheritDoc + */ + protected function remove($revision) + { + $merged_revisions_count = $this->repositoryFiller->removeMergeCommit($revision); + $this->recordStatistic(self::STATISTIC_MERGE_DELETED, $merged_revisions_count); + } + /** * Find revisions by collected data. * diff --git a/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/PathsPlugin.php b/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/PathsPlugin.php index dcf07f1..80b0d86 100644 --- a/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/PathsPlugin.php +++ b/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/PathsPlugin.php @@ -241,6 +241,16 @@ protected function doParse($revision, \SimpleXMLElement $log_entry) } } + /** + * @inheritDoc + * + * @throws \RuntimeException When attempting to remove plugin collected data. + */ + protected function remove($revision) + { + throw new \RuntimeException('Not supported.'); + } + /** * Sorts paths to move parent folders above their sub-folders. * diff --git a/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/SummaryPlugin.php b/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/SummaryPlugin.php index e3121a4..fe05b72 100644 --- a/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/SummaryPlugin.php +++ b/src/SVNBuddy/Repository/RevisionLog/Plugin/RepositoryCollectorPlugin/SummaryPlugin.php @@ -11,11 +11,18 @@ namespace ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\RepositoryCollectorPlugin; -class SummaryPlugin extends AbstractRepositoryCollectorPlugin +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IOverwriteAwarePlugin; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\TOverwriteAwarePlugin; + +class SummaryPlugin extends AbstractRepositoryCollectorPlugin implements IOverwriteAwarePlugin { + use TOverwriteAwarePlugin; + const STATISTIC_COMMIT_ADDED = 'commit_added'; + const STATISTIC_COMMIT_REMOVED = 'commit_removed'; + /** * Returns plugin name. * @@ -34,7 +41,7 @@ public function getName() public function defineStatisticTypes() { return array( - self::STATISTIC_COMMIT_ADDED, + self::STATISTIC_COMMIT_ADDED, self::STATISTIC_COMMIT_REMOVED, ); } @@ -58,6 +65,15 @@ protected function doParse($revision, \SimpleXMLElement $log_entry) $this->recordStatistic(self::STATISTIC_COMMIT_ADDED); } + /** + * @inheritDoc + */ + protected function remove($revision) + { + $this->repositoryFiller->removeCommit($revision); + $this->recordStatistic(self::STATISTIC_COMMIT_REMOVED); + } + /** * Find revisions by collected data. * diff --git a/src/SVNBuddy/Repository/RevisionLog/Plugin/TOverwriteAwarePlugin.php b/src/SVNBuddy/Repository/RevisionLog/Plugin/TOverwriteAwarePlugin.php new file mode 100644 index 0000000..d08f9ce --- /dev/null +++ b/src/SVNBuddy/Repository/RevisionLog/Plugin/TOverwriteAwarePlugin.php @@ -0,0 +1,40 @@ + + * @link https://github.com/console-helpers/svn-buddy + */ + +namespace ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin; + + +trait TOverwriteAwarePlugin +{ + + /** + * Overwrite mode. + * + * @var boolean + */ + private $_overwriteMode = false; + + /** + * @inheritDoc + */ + public function setOverwriteMode($overwrite_mode) + { + $this->_overwriteMode = $overwrite_mode; + } + + /** + * @inheritDoc + */ + public function isOverwriteMode() + { + return $this->_overwriteMode; + } + +} diff --git a/src/SVNBuddy/Repository/RevisionLog/RepositoryFiller.php b/src/SVNBuddy/Repository/RevisionLog/RepositoryFiller.php index 4018722..621f3e7 100644 --- a/src/SVNBuddy/Repository/RevisionLog/RepositoryFiller.php +++ b/src/SVNBuddy/Repository/RevisionLog/RepositoryFiller.php @@ -135,6 +135,22 @@ public function addCommit($revision, $author, $date, $message) )); } + /** + * Removes commit. + * + * @param integer $revision Revision. + * + * @return void + */ + public function removeCommit($revision) + { + $sql = 'DELETE FROM Commits + WHERE Revision = :revision'; + $this->database->perform($sql, array( + 'revision' => $revision, + )); + } + /** * Adds commit to project. * @@ -362,6 +378,21 @@ public function addBugsToCommit(array $bugs, $revision) } } + /** + * Removes commit with bugs. + * + * @param integer $revision Revision. + * + * @return integer + */ + public function removeBugsFromCommit($revision) + { + $sql = 'DELETE FROM CommitBugs + WHERE Revision = :revision'; + + return $this->database->fetchAffected($sql, array('revision' => $revision)); + } + /** * Adds merge commit. * @@ -382,6 +413,23 @@ public function addMergeCommit($revision, array $merged_revisions) } } + /** + * Removes merge commit. + * + * @param integer $revision Revision. + * + * @return integer + */ + public function removeMergeCommit($revision) + { + $sql = 'DELETE FROM Merges + WHERE MergeRevision = :merge_revision'; + + return $this->database->fetchAffected($sql, array( + 'merge_revision' => $revision, + )); + } + /** * Adds ref to project. * diff --git a/src/SVNBuddy/Repository/RevisionLog/RevisionLog.php b/src/SVNBuddy/Repository/RevisionLog/RevisionLog.php index 5d2bac6..ea95e0e 100644 --- a/src/SVNBuddy/Repository/RevisionLog/RevisionLog.php +++ b/src/SVNBuddy/Repository/RevisionLog/RevisionLog.php @@ -14,6 +14,7 @@ use ConsoleHelpers\ConsoleKit\ConsoleIO; use ConsoleHelpers\SVNBuddy\Repository\Connector\Connector; use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\DatabaseCollectorPlugin\IDatabaseCollectorPlugin; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IOverwriteAwarePlugin; use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IPlugin; use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\RepositoryCollectorPlugin\IRepositoryCollectorPlugin; use ConsoleHelpers\SVNBuddy\Repository\RevisionUrlBuilder; @@ -141,6 +142,24 @@ public function refresh($is_migration) } } + /** + * Reparses a revision. + * + * @param integer $revision Revision. + * + * @return void + * @throws \LogicException When no plugins are registered. + */ + public function reparse($revision) + { + if ( !$this->_plugins ) { + throw new \LogicException('Please register at least one revision log plugin.'); + } + + $this->_databaseReady(); + $this->_queryRevisionData($revision, $revision, true); + } + /** * Reports to each plugin, that database is ready for usage. * @@ -180,26 +199,53 @@ private function _getAggregateRevision($function) * * @param integer $from_revision From revision. * @param integer $to_revision To revision. + * @param boolean $overwrite Overwrite. * * @return void */ - private function _queryRevisionData($from_revision, $to_revision) + private function _queryRevisionData($from_revision, $to_revision, $overwrite = false) { - $range_start = $from_revision; + $this->_useRepositoryCollectorPlugins($from_revision, $to_revision, $overwrite); + $this->_useDatabaseCollectorPlugins($from_revision, $to_revision, $overwrite); + if ( isset($this->_io) && $this->_io->isVerbose() ) { + $this->_displayPluginActivityStatistics(); + } + } + + /** + * Use repository collector plugins. + * + * @param integer $from_revision From revision. + * @param integer $to_revision To revision. + * @param boolean $overwrite Overwrite. + * + * @return void + */ + private function _useRepositoryCollectorPlugins($from_revision, $to_revision, $overwrite = false) + { // The "io" isn't set during autocomplete. if ( isset($this->_io) ) { // Create progress bar for repository plugins, where data amount is known upfront. $progress_bar = $this->_io->createProgressBar(ceil(($to_revision - $from_revision) / 200) + 1); - $progress_bar->setMessage(' * Reading missing revisions:'); + $progress_bar->setMessage( + $overwrite ? '* Reparsing revisions:' : ' * Reading missing revisions:' + ); $progress_bar->setFormat( '%message% %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:-10s%' ); $progress_bar->start(); } - $log_command_arguments = $this->_getLogCommandArguments(); - $is_verbose = isset($this->_io) && $this->_io->isVerbose(); + $plugins = $this->getRepositoryCollectorPlugins($overwrite); + + if ( $overwrite ) { + $this->setPluginsOverwriteMode($plugins, true); + } + + $range_start = $from_revision; + $cache_duration = $overwrite ? null : '10 years'; + $log_command_arguments = $this->_getLogCommandArguments($plugins); while ( $range_start <= $to_revision ) { $range_end = min($range_start + 199, $to_revision); @@ -210,10 +256,10 @@ private function _queryRevisionData($from_revision, $to_revision) $log_command_arguments ); $command = $this->_repositoryConnector->getCommand('log', $command_arguments); - $command->setCacheDuration('10 years'); + $command->setCacheDuration($cache_duration); $svn_log = $command->run(); - $this->_parseLog($svn_log); + $this->_parseLog($svn_log, $plugins); $range_start = $range_end + 1; @@ -222,45 +268,74 @@ private function _queryRevisionData($from_revision, $to_revision) } } + // Remove progress bar of repository plugins. if ( isset($progress_bar) ) { - // Remove progress bar of repository plugins. $progress_bar->clear(); unset($progress_bar); + } + if ( $overwrite ) { + $this->setPluginsOverwriteMode($plugins, false); + } + } + + /** + * Use database collector plugins. + * + * @param integer $from_revision From revision. + * @param integer $to_revision To revision. + * @param boolean $overwrite Overwrite. + * + * @return void + */ + private function _useDatabaseCollectorPlugins($from_revision, $to_revision, $overwrite = false) + { + $plugins = $this->getDatabaseCollectorPlugins($overwrite); + + if ( $overwrite ) { + $this->setPluginsOverwriteMode($plugins, true); + } + + // The "io" isn't set during autocomplete. + if ( isset($this->_io) ) { // Create progress bar for database plugins, where data amount isn't known upfront. $progress_bar = $this->_io->createProgressBar(); - $progress_bar->setMessage(' * Reading missing revisions:'); + $progress_bar->setMessage( + $overwrite ? '* Reparsing revisions:' : ' * Reading missing revisions:' + ); $progress_bar->setFormat('%message% %current% [%bar%] %elapsed:6s% %memory:-10s%'); $progress_bar->start(); - foreach ( $this->getDatabaseCollectorPlugins() as $plugin ) { + foreach ( $plugins as $plugin ) { $plugin->process($from_revision, $to_revision, $progress_bar); } } else { - foreach ( $this->getDatabaseCollectorPlugins() as $plugin ) { + foreach ( $plugins as $plugin ) { $plugin->process($from_revision, $to_revision); } } + if ( $overwrite ) { + $this->setPluginsOverwriteMode($plugins, false); + } + if ( isset($progress_bar) ) { $progress_bar->finish(); $this->_io->writeln(''); } - - if ( $is_verbose ) { - $this->_displayPluginActivityStatistics(); - } } /** * Returns arguments for "log" command. * + * @param IRepositoryCollectorPlugin[] $plugins Plugins. + * * @return array */ - private function _getLogCommandArguments() + private function _getLogCommandArguments(array $plugins) { - $query_flags = $this->_getRevisionQueryFlags(); + $query_flags = $this->_getRevisionQueryFlags($plugins); $ret = array('-r', '{revision_range}', '--xml'); @@ -280,13 +355,15 @@ private function _getLogCommandArguments() /** * Returns revision query flags. * + * @param IRepositoryCollectorPlugin[] $plugins Plugins. + * * @return array */ - private function _getRevisionQueryFlags() + private function _getRevisionQueryFlags(array $plugins) { $ret = array(); - foreach ( $this->getRepositoryCollectorPlugins() as $plugin ) { + foreach ( $plugins as $plugin ) { $ret = array_merge($ret, $plugin->getRevisionQueryFlags()); } @@ -296,13 +373,14 @@ private function _getRevisionQueryFlags() /** * Parses output of "svn log" command. * - * @param \SimpleXMLElement $log Log. + * @param \SimpleXMLElement $log Log. + * @param IRepositoryCollectorPlugin[] $plugins Plugins. * * @return void */ - private function _parseLog(\SimpleXMLElement $log) + private function _parseLog(\SimpleXMLElement $log, array $plugins) { - foreach ( $this->getRepositoryCollectorPlugins() as $plugin ) { + foreach ( $plugins as $plugin ) { $plugin->parse($log); } } @@ -430,39 +508,56 @@ public function getBugsFromRevisions(array $revisions) /** * Returns repository collector plugins. * + * @param boolean $overwrite_mode Overwrite mode. + * * @return IRepositoryCollectorPlugin[] */ - protected function getRepositoryCollectorPlugins() + protected function getRepositoryCollectorPlugins($overwrite_mode) { - return $this->getPluginsByInterface( - 'ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\RepositoryCollectorPlugin\IRepositoryCollectorPlugin' - ); + $plugins = $this->getPluginsByInterface(IRepositoryCollectorPlugin::class); + + if ( !$overwrite_mode ) { + return $plugins; + } + + return $this->getPluginsByInterface(IOverwriteAwarePlugin::class, $plugins); } /** * Returns database collector plugins. * + * @param boolean $overwrite_mode Overwrite mode. + * * @return IDatabaseCollectorPlugin[] */ - protected function getDatabaseCollectorPlugins() + protected function getDatabaseCollectorPlugins($overwrite_mode) { - return $this->getPluginsByInterface( - 'ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\DatabaseCollectorPlugin\IDatabaseCollectorPlugin' - ); + $plugins = $this->getPluginsByInterface(IDatabaseCollectorPlugin::class); + + if ( !$overwrite_mode ) { + return $plugins; + } + + return $this->getPluginsByInterface(IOverwriteAwarePlugin::class, $plugins); } /** * Returns plugin list filtered by interface. * - * @param string $interface Interface name. + * @param string $interface Interface name. + * @param IPlugin[] $plugins Plugins. * * @return IPlugin[] */ - protected function getPluginsByInterface($interface) + protected function getPluginsByInterface($interface, array $plugins = array()) { + if ( !$plugins ) { + $plugins = $this->_plugins; + } + $ret = array(); - foreach ( $this->_plugins as $plugin ) { + foreach ( $plugins as $plugin ) { if ( $plugin instanceof $interface ) { $ret[] = $plugin; } @@ -471,6 +566,21 @@ protected function getPluginsByInterface($interface) return $ret; } + /** + * Sets overwrite mode. + * + * @param IOverwriteAwarePlugin[] $plugins Plugins. + * @param boolean $overwrite_mode Overwrite mode. + * + * @return void + */ + protected function setPluginsOverwriteMode(array $plugins, $overwrite_mode) + { + foreach ( $plugins as $plugin ) { + $plugin->setOverwriteMode($overwrite_mode); + } + } + /** * Returns project path. * diff --git a/tests/SVNBuddy/Repository/RevisionLog/Plugin/AbstractPluginTestCase.php b/tests/SVNBuddy/Repository/RevisionLog/Plugin/AbstractPluginTestCase.php index d3b0e7e..577319d 100644 --- a/tests/SVNBuddy/Repository/RevisionLog/Plugin/AbstractPluginTestCase.php +++ b/tests/SVNBuddy/Repository/RevisionLog/Plugin/AbstractPluginTestCase.php @@ -12,6 +12,7 @@ use ConsoleHelpers\SVNBuddy\Database\DatabaseCache; +use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IOverwriteAwarePlugin; use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IPlugin; use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\RepositoryFiller; use Tests\ConsoleHelpers\SVNBuddy\Repository\RevisionLog\AbstractDatabaseAwareTestCase; @@ -60,6 +61,19 @@ protected function setupTest() $this->plugin = $this->createPlugin(); } + public function testSetOverwriteMode() + { + if ( !($this->plugin instanceof IOverwriteAwarePlugin) ) { + $this->markTestSkipped( + 'The "' . $this->plugin->getName() . '" plugin doesn\'t support an overwrite mode.' + ); + } + + $this->assertFalse($this->plugin->isOverwriteMode()); + $this->plugin->setOverwriteMode(true); + $this->assertTrue($this->plugin->isOverwriteMode()); + } + public function testGetLastRevisionEmpty() { $this->assertEquals(0, $this->plugin->getLastRevision()); diff --git a/tests/SVNBuddy/Repository/RevisionLog/Plugin/BugsPluginTest.php b/tests/SVNBuddy/Repository/RevisionLog/Plugin/BugsPluginTest.php index 09be2cd..ae182e8 100644 --- a/tests/SVNBuddy/Repository/RevisionLog/Plugin/BugsPluginTest.php +++ b/tests/SVNBuddy/Repository/RevisionLog/Plugin/BugsPluginTest.php @@ -389,6 +389,79 @@ public function testProcessCommitFromProjectWithoutBugTracking() )); } + /** + * @dataProvider overwriteModeDataProvider + */ + public function testOverwriteMode($overwrite_mode, array $commit_bugs_table_contents, array $statistics) + { + $this->commitBuilder + ->addCommit(100, 'user', 0, 'message1') + ->addBugs(array('BUG1', 'BUG2', 'BUG3')) + ->addPath('A', '/path/to/project-one/', '', '/path/to/project-one/') + ->addPath('A', '/path/to/project-one/trunk/', 'trunk', '/path/to/project-one/'); + $this->commitBuilder->build(); + + // Assuming that project id would be "1". + $this->filler->setProjectBugRegexp(1, 'OK'); + + if ( $overwrite_mode ) { + $log_message_parser = $this->prophesize('ConsoleHelpers\SVNBuddy\Repository\Parser\LogMessageParser'); + $log_message_parser->parse('message1')->willReturn(array('BUG4', 'BUG5'))->shouldBeCalled(); + $this->logMessageParserFactory->getLogMessageParser('OK')->willReturn($log_message_parser); + } + + $this->setLastRevision(200); + $this->plugin->setOverwriteMode($overwrite_mode); + $this->plugin->process(100, 100); + + $this->assertTableContent('CommitBugs', $commit_bugs_table_contents); + $this->assertStatistics($statistics); + } + + public static function overwriteModeDataProvider() + { + return array( + 'no overwrite' => array( + false, + array( + array( + 'Revision' => '100', + 'Bug' => 'BUG1', + ), + array( + 'Revision' => '100', + 'Bug' => 'BUG2', + ), + array( + 'Revision' => '100', + 'Bug' => 'BUG3', + ), + ), + array(), + ), + 'overwrite' => array( + true, + array( + array( + 'Revision' => '100', + 'Bug' => 'BUG4', + ), + array( + 'Revision' => '100', + 'Bug' => 'BUG5', + ), + ), + array( + BugsPlugin::STATISTIC_BUG_REMOVED_FROM_COMMIT => 3, + BugsPlugin::STATISTIC_BUG_ADDED_TO_COMMIT => 2, + ), + ), + ); + $this->assertStatistics(array( + BugsPlugin::STATISTIC_BUG_ADDED_TO_COMMIT => 2, + )); + } + public function testProcessMultipleCommitsSameBug() { $this->commitBuilder diff --git a/tests/SVNBuddy/Repository/RevisionLog/Plugin/MergesPluginTest.php b/tests/SVNBuddy/Repository/RevisionLog/Plugin/MergesPluginTest.php index 315d519..db2fbb4 100644 --- a/tests/SVNBuddy/Repository/RevisionLog/Plugin/MergesPluginTest.php +++ b/tests/SVNBuddy/Repository/RevisionLog/Plugin/MergesPluginTest.php @@ -68,6 +68,40 @@ public function testParse($fixture_file) )); } + /** + * @dataProvider parseDataProvider + */ + public function testParseWithOverwriteMode($fixture_file) + { + $this->commitBuilder + ->addCommit(100, 'user', 123, 'msg') + ->addMergedCommits(array(10, 20, 30)); + $this->commitBuilder->build(); + $this->setLastRevision(100); + + $this->plugin->setOverwriteMode(true); + $this->plugin->parse($this->getFixture($fixture_file)); + + $this->assertTableContent( + 'Merges', + array( + array( + 'MergeRevision' => '100', + 'MergedRevision' => '50', + ), + array( + 'MergeRevision' => '100', + 'MergedRevision' => '60', + ), + ) + ); + + $this->assertStatistics(array( + MergesPlugin::STATISTIC_MERGE_DELETED => 3, + MergesPlugin::STATISTIC_MERGE_ADDED => 2, + )); + } + public static function parseDataProvider() { return array( diff --git a/tests/SVNBuddy/Repository/RevisionLog/Plugin/SummaryPluginTest.php b/tests/SVNBuddy/Repository/RevisionLog/Plugin/SummaryPluginTest.php index 63833ba..372fcd7 100644 --- a/tests/SVNBuddy/Repository/RevisionLog/Plugin/SummaryPluginTest.php +++ b/tests/SVNBuddy/Repository/RevisionLog/Plugin/SummaryPluginTest.php @@ -66,6 +66,37 @@ public function testParse($fixture_file) )); } + /** + * @dataProvider parseDataProvider + */ + public function testParseWithOverwriteMode($fixture_file) + { + $this->commitBuilder + ->addCommit(100, 'user-a', 123, 'msg-a'); + $this->commitBuilder->build(); + $this->setLastRevision(100); + + $this->plugin->setOverwriteMode(true); + $this->plugin->parse($this->getFixture($fixture_file)); + + $this->assertTableContent( + 'Commits', + array( + array( + 'Revision' => '100', + 'Author' => 'user', + 'Date' => '1461350740', + 'Message' => 'message', + ), + ) + ); + + $this->assertStatistics(array( + SummaryPlugin::STATISTIC_COMMIT_REMOVED => 1, + SummaryPlugin::STATISTIC_COMMIT_ADDED => 1, + )); + } + public static function parseDataProvider() { return array( diff --git a/tests/SVNBuddy/Repository/RevisionLog/RepositoryFillerTest.php b/tests/SVNBuddy/Repository/RevisionLog/RepositoryFillerTest.php index 18c406b..5efc992 100644 --- a/tests/SVNBuddy/Repository/RevisionLog/RepositoryFillerTest.php +++ b/tests/SVNBuddy/Repository/RevisionLog/RepositoryFillerTest.php @@ -152,6 +152,26 @@ public function testAddCommit() ); } + public function testRemoveCommit() + { + $this->repositoryFiller->addCommit(100, 'user-a', 123, 'msg1'); + $this->repositoryFiller->addCommit(101, 'user-b', 124, 'msg2'); + + $this->repositoryFiller->removeCommit(100); + + $this->assertTableContent( + 'Commits', + array( + array( + 'Revision' => '101', + 'Author' => 'user-b', + 'Date' => '124', + 'Message' => 'msg2', + ), + ) + ); + } + public function testAddCommitToProject() { $project_id = $this->repositoryFiller->addProject('/path/to/project/'); @@ -505,6 +525,30 @@ public function testAddBugsToCommit() ); } + public function testRemoveBugsFromCommit() + { + $this->repositoryFiller->addCommit(100, 'user', 123, 'msg'); + $this->repositoryFiller->addBugsToCommit(array('AA', 'BB'), 100); + $this->repositoryFiller->addCommit(101, 'user', 123, 'msg'); + $this->repositoryFiller->addBugsToCommit(array('BB', 'CC'), 101); + + $this->repositoryFiller->removeBugsFromCommit(100); + + $this->assertTableContent( + 'CommitBugs', + array( + array( + 'Revision' => '101', + 'Bug' => 'BB', + ), + array( + 'Revision' => '101', + 'Bug' => 'CC', + ), + ) + ); + } + public function testAddMergeCommit() { $this->repositoryFiller->addCommit(100, 'user', 123, 'msg'); @@ -527,6 +571,35 @@ public function testAddMergeCommit() ); } + public function testRemoveMergeCommit() + { + $this->repositoryFiller->addCommit(100, 'user', 123, 'msg'); + $this->repositoryFiller->addCommit(200, 'user', 123, 'msg'); + $this->repositoryFiller->addCommit(300, 'user', 123, 'msg'); + $this->repositoryFiller->addMergeCommit(300, array(100, 200)); + + $this->repositoryFiller->addCommit(101, 'user', 123, 'msg'); + $this->repositoryFiller->addCommit(201, 'user', 123, 'msg'); + $this->repositoryFiller->addCommit(301, 'user', 123, 'msg'); + $this->repositoryFiller->addMergeCommit(301, array(101, 201)); + + $this->repositoryFiller->removeMergeCommit(300); + + $this->assertTableContent( + 'Merges', + array( + array( + 'MergeRevision' => '301', + 'MergedRevision' => '101', + ), + array( + 'MergeRevision' => '301', + 'MergedRevision' => '201', + ), + ) + ); + } + public function testAddRefToProject() { $project_id = $this->repositoryFiller->addProject('/project/');