Skip to content

Commit e14136c

Browse files
committed
Correct corrupted database in multi-project setup
1 parent 30ec906 commit e14136c

File tree

4 files changed

+128
-9
lines changed

4 files changed

+128
-9
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55
## [Unreleased]
66
### Added
77
- Added support for Terminal with a light theme. To enable specify `light` as a value of `theme` config setting in `~/.svn-buddy/config.json` file.
8+
- Added the `?` possible value for the `--bugs` options of the `log` command to show revisions without a bug.
89

910
### Changed
1011
- Show executed SVN commands in real time (when started; how long was executed) in verbose mode (the `-v` flag).
@@ -13,7 +14,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1314
### Fixed
1415
- Handle cases, when `svn log ... --use-merge-history ...` command timeout-out.
1516
- The `merge` command wasn't doing auto-commit, when alternative working directly was specified.
16-
- The `current revision` highlighting style is now correctly applied to the `Bug-ID` column in the `log` command results.
17+
- The `current revision` highlighting style is now correctly applied to the `Bug-ID` column in the `log` command results.
18+
- Created a semi-automatic migration script for unlinking commits from projects they don't belong.
1719

1820
## [0.8.0] - 2024-12-18
1921
### Added
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\MigrationContext;
4+
5+
return function (MigrationContext $context) {
6+
$db = $context->getDatabase();
7+
8+
// Get commits, that belong to multiple projects.
9+
$sql = 'SELECT cp.Revision, GROUP_CONCAT(cp.ProjectId)
10+
FROM CommitProjects cp
11+
JOIN Commits c ON c.Revision = cp.Revision
12+
GROUP BY cp.Revision
13+
HAVING COUNT(*) > 1';
14+
$multi_project_commits = $db->fetchPairs($sql);
15+
16+
if ( !$multi_project_commits ) {
17+
return;
18+
}
19+
20+
$to_reparse = array();
21+
$revision_log = $context->getrevisionLog();
22+
23+
$revision_count = count($multi_project_commits);
24+
echo sprintf('Found %d commits with multiple projects.', $revision_count) . PHP_EOL;
25+
26+
foreach ( $multi_project_commits as $revision => $project_ids ) {
27+
echo 'Processing ' . $revision . ' revision... ';
28+
29+
$sql = 'SELECT Id, Path
30+
FROM Projects
31+
WHERE Id IN (:project_ids)';
32+
$project_paths = $db->fetchPairs($sql, array('project_ids' => explode(',', $project_ids)));
33+
34+
$log = $revision_log
35+
->getCommand(
36+
'log',
37+
array('--revision', $revision, '--xml', '--verbose', '{repository_url}')
38+
)
39+
->run();
40+
41+
$projects_found = array();
42+
43+
// Determine the actual project, where the commit belongs based on its paths.
44+
foreach ( $log->logentry->paths->path as $path ) {
45+
foreach ( $project_paths as $project_id => $project_path ) {
46+
if ( strpos($path, $project_path) === 0 ) {
47+
$projects_found[$project_id] = true;
48+
break;
49+
}
50+
}
51+
}
52+
53+
// Leave only projects that were found in the SVN repository.
54+
foreach ( array_keys($projects_found) as $project_id ) {
55+
unset($project_paths[$project_id]);
56+
}
57+
58+
if ( !$project_paths ) {
59+
echo 'Skipped.' . PHP_EOL;
60+
continue;
61+
}
62+
63+
// Remove incorrect associations.
64+
foreach ( $project_paths as $project_id => $project_path ) {
65+
$sql = 'SELECT pa.Id
66+
FROM CommitPaths cp
67+
JOIN Paths pa ON pa.Id = cp.PathId
68+
WHERE cp.Revision = :revision AND pa.ProjectPath = :project_path';
69+
$path_id = $db->fetchValue($sql, array('revision' => $revision, 'project_path' => $project_path));
70+
71+
$sql = 'DELETE FROM CommitPaths
72+
WHERE Revision = :revision AND PathId = :path_id';
73+
$db->perform($sql, array('revision' => $revision, 'path_id' => $path_id));
74+
75+
$sql = 'DELETE FROM CommitProjects
76+
WHERE Revision = :revision AND ProjectId = :project_id';
77+
$db->perform($sql, array('revision' => $revision, 'project_id' => $project_id));
78+
}
79+
80+
echo 'Fixed.' . PHP_EOL;
81+
$to_reparse[] = $revision;
82+
}
83+
84+
foreach ( array_chunk($to_reparse, 10) as $to_reparse_chunk ) {
85+
echo sprintf(
86+
'Run the "%s reparse -r %s" command.' . PHP_EOL,
87+
reset($_SERVER['argv']),
88+
implode(',', $to_reparse_chunk)
89+
);
90+
}
91+
};

src/SVNBuddy/Repository/RevisionLog/Plugin/DatabaseCollectorPlugin/BugsPlugin.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -421,11 +421,21 @@ public function find(array $criteria, $project_path)
421421

422422
$project_id = $this->getProject($project_path);
423423

424-
$sql = 'SELECT DISTINCT cb.Revision
425-
FROM CommitBugs cb
426-
JOIN CommitProjects cp ON cp.Revision = cb.Revision
427-
WHERE cp.ProjectId = :project_id AND cb.Bug IN (:bugs)';
428-
$bug_revisions = $this->database->fetchCol($sql, array('project_id' => $project_id, 'bugs' => $criteria));
424+
if ( $criteria === array('?') ) {
425+
$sql = 'SELECT DISTINCT c.Revision, c.Message
426+
FROM Commits c
427+
JOIN CommitProjects cp ON cp.Revision = c.Revision
428+
LEFT JOIN CommitBugs cb ON cb.Revision = c.Revision
429+
WHERE cp.ProjectId = :project_id AND cb.Revision IS NULL';
430+
$bug_revisions = $this->database->fetchCol($sql, array('project_id' => $project_id));
431+
}
432+
else {
433+
$sql = 'SELECT DISTINCT cb.Revision
434+
FROM CommitBugs cb
435+
JOIN CommitProjects cp ON cp.Revision = cb.Revision
436+
WHERE cp.ProjectId = :project_id AND cb.Bug IN (:bugs)';
437+
$bug_revisions = $this->database->fetchCol($sql, array('project_id' => $project_id, 'bugs' => $criteria));
438+
}
429439

430440
sort($bug_revisions, SORT_NUMERIC);
431441

src/SVNBuddy/Repository/RevisionLog/RevisionLog.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313

1414
use ConsoleHelpers\ConsoleKit\ConsoleIO;
15+
use ConsoleHelpers\SVNBuddy\Repository\Connector\Command;
1516
use ConsoleHelpers\SVNBuddy\Repository\Connector\Connector;
1617
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\DatabaseCollectorPlugin\IDatabaseCollectorPlugin;
1718
use ConsoleHelpers\SVNBuddy\Repository\RevisionLog\Plugin\IOverwriteAwarePlugin;
@@ -297,11 +298,11 @@ private function _useRepositoryCollectorPlugins($from_revision, $to_revision, $o
297298
$range_end = min($range_start + ($batch_size - 1), $to_revision);
298299

299300
$command_arguments = str_replace(
300-
array('{revision_range}', '{repository_url}'),
301-
array($range_start . ':' . $range_end, $this->_repositoryRootUrl),
301+
'{revision_range}',
302+
$range_start . ':' . $range_end,
302303
$log_command_arguments
303304
);
304-
$command = $this->_repositoryConnector->getCommand('log', $command_arguments);
305+
$command = $this->getCommand('log', $command_arguments);
305306
$command->setCacheDuration($cache_duration)->setIdleTimeoutRecovery(true);
306307
$svn_log = $command->run();
307308

@@ -325,6 +326,21 @@ private function _useRepositoryCollectorPlugins($from_revision, $to_revision, $o
325326
}
326327
}
327328

329+
/**
330+
* Builds the command.
331+
*
332+
* @param string $name Command name.
333+
* @param array $arguments Arguments.
334+
*
335+
* @return Command
336+
*/
337+
public function getCommand($name, array $arguments)
338+
{
339+
$arguments = str_replace('{repository_url}', $this->_repositoryRootUrl, $arguments);
340+
341+
return $this->_repositoryConnector->getCommand($name, $arguments);
342+
}
343+
328344
/**
329345
* Use database collector plugins.
330346
*

0 commit comments

Comments
 (0)