diff options
author | Knut Petter Svendsen <knutpett@pvv.org> | 2013-12-05 13:30:20 +0100 |
---|---|---|
committer | Knut Petter Svendsen <knutpett@pvv.org> | 2013-12-05 13:47:25 +0100 |
commit | 0bb76dc984f022ddc7508f783d67d5fb82d6df98 (patch) | |
tree | 54c4a15e873c63a496a285d31a054b148a368343 | |
parent | 6277bdbff6cb0af84653558cb384d6d542550471 (diff) | |
download | qt-creator-0bb76dc984f022ddc7508f783d67d5fb82d6df98.tar.gz |
ClearCase: Improved performance for indexing dynamic views
For dynamic views we only at initial sync/indexing check for checked out
files. When a file is opened it will be reindexed if needed. The very time
consuming recursive listing of all vobs is not needed as for snapshot
views.
Change-Id: I83d4ab70efdd311b6f3239ab45569c6d1810e10f
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
-rw-r--r-- | src/plugins/clearcase/clearcaseplugin.cpp | 143 | ||||
-rw-r--r-- | src/plugins/clearcase/clearcaseplugin.h | 9 | ||||
-rw-r--r-- | src/plugins/clearcase/clearcasesync.cpp | 172 | ||||
-rw-r--r-- | src/plugins/clearcase/clearcasesync.h | 19 |
4 files changed, 294 insertions, 49 deletions
diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index a7794900dd..f2251acca8 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -239,6 +239,44 @@ QString ClearCasePlugin::getDriveLetterOfPath(const QString &directory) return dir.path(); } +void ClearCasePlugin::updateStatusForFile(const QString &absFile) +{ + setStatus(absFile, getFileStatus(absFile), false); +} + +FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const +{ + QTC_CHECK(!fileName.isEmpty()); + + const QDir viewRootDir = QFileInfo(fileName).dir(); + const QString viewRoot = viewRootDir.path(); + + QStringList args(QLatin1String("ls")); + args << fileName; + QString buffer = runCleartoolSync(viewRoot, args); + + const int atatpos = buffer.indexOf(QLatin1String("@@")); + if (atatpos != -1) { // probably managed file + // find first whitespace. anything before that is not interesting + const int wspos = buffer.indexOf(QRegExp(QLatin1String("\\s"))); + const QString absFile = + viewRootDir.absoluteFilePath( + QDir::fromNativeSeparators(buffer.left(atatpos))); + + QTC_CHECK(QFile(absFile).exists()); + QTC_CHECK(!absFile.isEmpty()); + + if (buffer.lastIndexOf(QLatin1String("CHECKEDOUT"), wspos) != -1) + return FileStatus::CheckedOut; + else + return FileStatus::CheckedIn; + } else { + QTC_CHECK(QFile(fileName).exists()); + QTC_CHECK(!fileName.isEmpty()); + return FileStatus::NotManaged; + } +} + /// /// Check if the directory is managed by ClearCase. /// @@ -676,7 +714,16 @@ QStringList ClearCasePlugin::ccGetActiveVobs() const return res; } -// file must be relative to topLevel, and using '/' path separator +void ClearCasePlugin::checkAndReIndexUnknownFile(const QString &file) +{ + if (isDynamic()) { + // reindex unknown files + if (m_statusMap->value(file, FileStatus(FileStatus::Unknown)).status == FileStatus::Unknown) + updateStatusForFile(file); + } +} + +// file must be absolute, and using '/' path separator FileStatus ClearCasePlugin::vcsStatus(const QString &file) const { return m_statusMap->value(file, FileStatus(FileStatus::Unknown)); @@ -732,7 +779,8 @@ void ClearCasePlugin::updateStatusActions() bool hasFile = currentState().hasFile(); if (hasFile) { QString absoluteFileName = currentState().currentFile(); - fileStatus = m_statusMap->value(absoluteFileName, FileStatus(FileStatus::Unknown)); + checkAndReIndexUnknownFile(absoluteFileName); + fileStatus = vcsStatus(absoluteFileName); if (Constants::debug) qDebug() << Q_FUNC_INFO << absoluteFileName << ", status = " @@ -776,6 +824,7 @@ void ClearCasePlugin::updateActions(VcsBase::VcsBasePlugin::ActionState as) m_annotateCurrentAction->setParameter(fileName); m_addFileAction->setParameter(fileName); m_updateIndexAction->setEnabled(!m_settings.disableIndexer); + updateStatusActions(); } @@ -796,6 +845,7 @@ void ClearCasePlugin::addCurrentFile() // Set the FileStatus of file given in absolute path void ClearCasePlugin::setStatus(const QString &file, FileStatus::Status status, bool update) { + QTC_CHECK(!file.isEmpty()); m_statusMap->insert(file, FileStatus(status, QFileInfo(file).permissions())); if (update && currentState().currentFile() == file) @@ -934,7 +984,7 @@ void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringLis if ((m_settings.diffType == GraphicalDiff) && (files.count() == 1)) { const QString file = files.first(); const QString absFilePath = workingDir + QLatin1Char('/') + file; - if (m_statusMap->value(absFilePath).status == FileStatus::Hijacked) + if (vcsStatus(absFilePath).status == FileStatus::Hijacked) diffGraphical(ccGetFileVersion(workingDir, file), file); else diffGraphical(file); @@ -948,7 +998,7 @@ void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringLis QString result; foreach (const QString &file, files) { const QString absFilePath = workingDir + QLatin1Char('/') + file; - if (m_statusMap->value(QDir::fromNativeSeparators(absFilePath)).status == FileStatus::Hijacked) + if (vcsStatus(QDir::fromNativeSeparators(absFilePath)).status == FileStatus::Hijacked) result += diffExternal(ccGetFileVersion(workingDir, file), file); else result += diffExternal(file); @@ -1240,7 +1290,8 @@ void ClearCasePlugin::viewStatus() m_viewData = ccGetView(m_topLevel); QTC_ASSERT(!m_viewData.name.isEmpty() && !m_settings.disableIndexer, return); VcsBase::VcsBaseOutputWindow *outputwindow = VcsBase::VcsBaseOutputWindow::instance(); - outputwindow->appendCommand(QLatin1String("Indexed files status (C=Checked Out, H=Hijacked, ?=Missing)")); + outputwindow->appendCommand(QLatin1String("Indexed files status (C=Checked Out, " + "H=Hijacked, ?=Missing)")); bool anymod = false; for (StatusMap::ConstIterator it = m_statusMap->constBegin(); it != m_statusMap->constEnd(); @@ -1486,14 +1537,14 @@ bool ClearCasePlugin::vcsOpen(const QString &workingDir, const QString &fileName CheckOutDialog coDialog(title, m_viewData.isUcm); if (!m_settings.disableIndexer && - (fi.isWritable() || m_statusMap->value(absPath).status == FileStatus::Unknown)) + (fi.isWritable() || vcsStatus(absPath).status == FileStatus::Unknown)) QtConcurrent::run(&sync, QStringList(absPath)).waitForFinished(); - if (m_statusMap->value(absPath).status == FileStatus::CheckedOut) { + if (vcsStatus(absPath).status == FileStatus::CheckedOut) { QMessageBox::information(0, tr("ClearCase Checkout"), tr("File is already checked out.")); return true; } // Only snapshot views can have hijacked files - bool isHijacked = (!m_viewData.isDynamic && (m_statusMap->value(absPath).status & FileStatus::Hijacked)); + bool isHijacked = (!m_viewData.isDynamic && (vcsStatus(absPath).status & FileStatus::Hijacked)); if (!isHijacked) coDialog.hideHijack(); if (coDialog.exec() == QDialog::Accepted) { @@ -1888,9 +1939,8 @@ bool ClearCasePlugin::ccCheckUcm(const QString &viewname, const QString &working bool ClearCasePlugin::managesFile(const QString &workingDirectory, const QString &fileName) const { - QStringList args; - args << QLatin1String("ls") << fileName; - return runCleartoolSync(workingDirectory, args).contains(QLatin1String("@@")); + QString absFile = QFileInfo(QDir(workingDirectory), fileName).absoluteFilePath(); + return getFileStatus(absFile) != FileStatus::NotManaged; } ViewData ClearCasePlugin::ccGetView(const QString &workingDir) const @@ -2216,9 +2266,34 @@ void ClearCasePlugin::testFileNotManaged() ccSync.verifyFileNotManaged(); } +void ClearCasePlugin::testFileCheckedOutDynamicView() +{ + ClearCasePlugin *plugin = ClearCasePlugin::instance(); + plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + + ClearCaseSync ccSync(plugin, plugin->m_statusMap); + ccSync.verifyFileCheckedOutDynamicView(); +} + +void ClearCasePlugin::testFileCheckedInDynamicView() +{ + ClearCasePlugin *plugin = ClearCasePlugin::instance(); + plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + ClearCaseSync ccSync(plugin, plugin->m_statusMap); + ccSync.verifyFileCheckedInDynamicView(); +} + +void ClearCasePlugin::testFileNotManagedDynamicView() +{ + ClearCasePlugin *plugin = ClearCasePlugin::instance(); + plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + ClearCaseSync ccSync(plugin, plugin->m_statusMap); + ccSync.verifyFileNotManagedDynamicView(); +} + namespace { /** - * @brief Convenience class which also properly cleans up editors + * @brief Convenience class which also properly cleans up editors and temp files */ class TestCase { @@ -2250,7 +2325,11 @@ public: { Core::EditorManager::closeEditor(m_editor, false); QCoreApplication::processEvents(); // process any pending events - QVERIFY(QFile::remove(m_fileName)); + + QFile file(m_fileName); + if (!file.isWritable()) // Windows can't delete read only files + file.setPermissions(file.permissions() | QFile::WriteUser); + QVERIFY(file.remove()); ClearCasePlugin::instance()->setFakeCleartool(false); } @@ -2313,6 +2392,44 @@ void ClearCasePlugin::testStatusActions() QCOMPARE(m_diffActivityAction->isEnabled(), diffActivityAction); } +void ClearCasePlugin::testVcsStatusDynamicReadonlyNotManaged() +{ + // File is not in map, and is read-only + ClearCasePlugin::instance(); + m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + + const QString fileName = QDir::currentPath() + QLatin1String("/readonly_notmanaged_file.cpp"); + + m_viewData.isDynamic = true; + TestCase testCase(fileName); + + QFile::setPermissions(fileName, QFile::ReadOwner | + QFile::ReadUser | + QFile::ReadGroup | + QFile::ReadOther); + + m_viewData = testCase.dummyViewData(); + m_viewData.isDynamic = true; + + QCOMPARE(vcsStatus(fileName).status, FileStatus::NotManaged); + +} + +void ClearCasePlugin::testVcsStatusDynamicNotManaged() +{ + ClearCasePlugin::instance(); + m_statusMap = QSharedPointer<StatusMap>(new StatusMap); + + const QString fileName = QDir::currentPath() + QLatin1String("/notmanaged_file.cpp"); + + m_viewData.isDynamic = true; + TestCase testCase(fileName); + + m_viewData = testCase.dummyViewData(); + m_viewData.isDynamic = true; + + QCOMPARE(vcsStatus(fileName).status, FileStatus::NotManaged); +} #endif } // namespace Internal diff --git a/src/plugins/clearcase/clearcaseplugin.h b/src/plugins/clearcase/clearcaseplugin.h index a34d0659fe..7ce1dbf79f 100644 --- a/src/plugins/clearcase/clearcaseplugin.h +++ b/src/plugins/clearcase/clearcaseplugin.h @@ -158,6 +158,7 @@ public: bool ccFileOp(const QString &workingDir, const QString &title, const QStringList &args, const QString &fileName, const QString &file2 = QString()); FileStatus vcsStatus(const QString &file) const; + void checkAndReIndexUnknownFile(const QString &file); QString currentView() const { return m_viewData.name; } QString viewRoot() const { return m_viewData.root; } void refreshActivities(); @@ -211,8 +212,13 @@ private slots: void testFileStatusParsing_data(); void testFileStatusParsing(); void testFileNotManaged(); + void testFileCheckedOutDynamicView(); + void testFileCheckedInDynamicView(); + void testFileNotManagedDynamicView(); void testStatusActions_data(); void testStatusActions(); + void testVcsStatusDynamicReadonlyNotManaged(); + void testVcsStatusDynamicNotManaged(); #endif protected: @@ -255,6 +261,9 @@ private: int timeOut, QTextCodec *outputCodec = 0); static QString getDriveLetterOfPath(const QString &directory); + FileStatus::Status getFileStatus(const QString &fileName) const; + void updateStatusForFile(const QString &absFile); + ClearCaseSettings m_settings; QString m_checkInMessageFileName; diff --git a/src/plugins/clearcase/clearcasesync.cpp b/src/plugins/clearcase/clearcasesync.cpp index 969e7e91e2..769c4707bd 100644 --- a/src/plugins/clearcase/clearcasesync.cpp +++ b/src/plugins/clearcase/clearcasesync.cpp @@ -50,8 +50,7 @@ ClearCaseSync::ClearCaseSync(ClearCasePlugin *plugin, QSharedPointer<StatusMap> { } -QStringList ClearCaseSync::updateStatusHotFiles(const QString &viewRoot, - const bool isDynamic, int &total) +QStringList ClearCaseSync::updateStatusHotFiles(const QString &viewRoot, int &total) { QStringList hotFiles; // find all files whose permissions changed OR hijacked files @@ -64,31 +63,31 @@ QStringList ClearCaseSync::updateStatusHotFiles(const QString &viewRoot, hotFiles.append(it.key()); it.value().status = FileStatus::Unknown; ++total; - } else if (isDynamic && !fi.isWritable()) { // assume a read only file is checked in - it.value().status = FileStatus::CheckedIn; - ++total; } } return hotFiles; } -void ClearCaseSync::updateStatus(const QDir &viewRootDir, const bool isDynamic, +// Set status for all files to unknown until we're done indexing +void ClearCaseSync::invalidateStatus(const QDir &viewRootDir, const QStringList &files) { foreach (const QString &file, files) { - if (isDynamic) { // assume a read only file is checked in - const QFileInfo fi(viewRootDir, file); - if (!fi.isWritable()) - m_plugin->setStatus(fi.absoluteFilePath(), FileStatus::CheckedIn, false); - } else { m_plugin->setStatus(viewRootDir.absoluteFilePath(file), FileStatus::Unknown, false); - } } } -void ClearCaseSync::processLine(const QDir &viewRootDir, const QString &buffer) +void ClearCaseSync::invalidateStatusAllFiles() +{ + const StatusMap::ConstIterator send = m_statusMap->end(); + for (StatusMap::ConstIterator it = m_statusMap->begin(); it != send; ++it) + m_plugin->setStatus(it.key(), FileStatus::Unknown, false); +} + +void ClearCaseSync::processCleartoolLsLine(const QDir &viewRootDir, const QString &buffer) { const int atatpos = buffer.indexOf(QLatin1String("@@")); + if (atatpos == -1) return; @@ -98,6 +97,7 @@ void ClearCaseSync::processLine(const QDir &viewRootDir, const QString &buffer) viewRootDir.absoluteFilePath( QDir::fromNativeSeparators(buffer.left(atatpos))); QTC_CHECK(QFile(absFile).exists()); + QTC_CHECK(!absFile.isEmpty()); QString ccState; const QRegExp reState(QLatin1String("^\\s*\\[[^\\]]*\\]")); // [hijacked]; [loaded but missing] @@ -132,38 +132,26 @@ void ClearCaseSync::updateStatusForNotManagedFiles(const QStringList &files) } } -void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) +void ClearCaseSync::syncSnapshotView(QFutureInterface<void> &future, QStringList &files, + const ClearCaseSettings &settings) { - ClearCaseSettings settings = m_plugin->settings(); - if (settings.disableIndexer) - return; + QString view = m_plugin->currentView(); - const QString program = settings.ccBinaryPath; - if (program.isEmpty()) - return; int totalFileCount = files.size(); const bool hot = (totalFileCount < 10); int processed = 0; - QString view = m_plugin->currentView(); - if (view.isEmpty()) - emit updateStreamAndView(); if (!hot) totalFileCount = settings.totalFiles.value(view, totalFileCount); - // refresh activities list - if (m_plugin->isUcm()) - m_plugin->refreshActivities(); - - const bool isDynamic = m_plugin->isDynamic(); const QString viewRoot = m_plugin->viewRoot(); const QDir viewRootDir(viewRoot); QStringList args(QLatin1String("ls")); if (hot) { - files << updateStatusHotFiles(viewRoot, isDynamic, totalFileCount); + files << updateStatusHotFiles(viewRoot, totalFileCount); args << files; } else { - updateStatus(viewRootDir, isDynamic, files); + invalidateStatus(viewRootDir, files); args << QLatin1String("-recurse"); QStringList vobs; @@ -181,6 +169,8 @@ void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) QProcess process; process.setWorkingDirectory(viewRoot); + const QString program = settings.ccBinaryPath; + process.start(program, args); if (!process.waitForStarted()) return; @@ -192,7 +182,7 @@ void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) const QString line = QString::fromLocal8Bit(process.readLine().constData()); buffer += line; if (buffer.endsWith(QLatin1Char('\n')) || process.atEnd()) { - processLine(viewRootDir, buffer); + processCleartoolLsLine(viewRootDir, buffer); buffer.clear(); future.setProgressValue(qMin(totalFileCount, ++processed)); } @@ -212,6 +202,82 @@ void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) process.waitForFinished(); } +void ClearCaseSync::processCleartoolLscheckoutLine(const QString &buffer) +{ + QString absFile = buffer.trimmed(); + m_plugin->setStatus(absFile, FileStatus::CheckedOut, true); +} + +/// +/// Update the file status for dynamic views. +/// +void ClearCaseSync::syncDynamicView(QFutureInterface<void> &future, + const ClearCaseSettings& settings) +{ + // Always invalidate status for all files + invalidateStatusAllFiles(); + + QStringList args(QLatin1String("lscheckout")); + args << QLatin1String("-avobs") + << QLatin1String("-me") + << QLatin1String("-cview") + << QLatin1String("-s"); + + const QString viewRoot = m_plugin->viewRoot(); + + QProcess process; + process.setWorkingDirectory(viewRoot); + + const QString program = settings.ccBinaryPath; + process.start(program, args); + if (!process.waitForStarted()) + return; + + QString buffer; + int processed = 0; + while (process.waitForReadyRead() && !future.isCanceled()) { + while (process.state() == QProcess::Running && + process.bytesAvailable() && !future.isCanceled()) { + const QString line = QString::fromLocal8Bit(process.readLine().constData()); + buffer += line; + if (buffer.endsWith(QLatin1Char('\n')) || process.atEnd()) { + processCleartoolLscheckoutLine(buffer); + buffer.clear(); + future.setProgressValue(++processed); + } + } + } + + if (process.state() == QProcess::Running) + process.kill(); + + process.waitForFinished(); +} + +void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files) +{ + ClearCaseSettings settings = m_plugin->settings(); + if (settings.disableIndexer) + return; + + const QString program = settings.ccBinaryPath; + if (program.isEmpty()) + return; + + // refresh activities list + if (m_plugin->isUcm()) + m_plugin->refreshActivities(); + + QString view = m_plugin->currentView(); + if (view.isEmpty()) + emit updateStreamAndView(); + + if (m_plugin->isDynamic()) + syncDynamicView(future, settings); + else + syncSnapshotView(future, files, settings); +} + #ifdef WITH_TESTS namespace { class TempFile @@ -243,14 +309,14 @@ void ClearCaseSync::verifyParseStatus(const QString &fileName, const FileStatus::Status status) { QCOMPARE(m_statusMap->count(), 0); - processLine(QDir(QLatin1String("/")), cleartoolLsLine); + processCleartoolLsLine(QDir(QLatin1String("/")), cleartoolLsLine); if (status == FileStatus::CheckedIn) { // The algorithm doesn't store checked in files in the index, unless it was there already QCOMPARE(m_statusMap->count(), 0); QCOMPARE(m_statusMap->contains(fileName), false); m_plugin->setStatus(fileName, FileStatus::Unknown, false); - processLine(QDir(QLatin1String("/")), cleartoolLsLine); + processCleartoolLsLine(QDir(QLatin1String("/")), cleartoolLsLine); } QCOMPARE(m_statusMap->count(), 1); @@ -274,6 +340,46 @@ void ClearCaseSync::verifyFileNotManaged() QCOMPARE(m_statusMap->value(fileName).status, FileStatus::NotManaged); } +void ClearCaseSync::verifyFileCheckedOutDynamicView() +{ + QCOMPARE(m_statusMap->count(), 0); + + QString fileName(QLatin1String("/hello.C")); + processCleartoolLscheckoutLine(fileName); + + QCOMPARE(m_statusMap->count(), 1); + + QVERIFY(m_statusMap->contains(fileName)); + QCOMPARE(m_statusMap->value(fileName).status, FileStatus::CheckedOut); + + QVERIFY(!m_statusMap->contains(QLatin1String(("notexisting")))); +} + +void ClearCaseSync::verifyFileCheckedInDynamicView() +{ + QCOMPARE(m_statusMap->count(), 0); + + QString fileName(QLatin1String("/hello.C")); + + // checked in files are not kept in the index + QCOMPARE(m_statusMap->count(), 0); + QCOMPARE(m_statusMap->contains(fileName), false); +} + +void ClearCaseSync::verifyFileNotManagedDynamicView() +{ + QCOMPARE(m_statusMap->count(), 0); + TempFile temp(QDir::currentPath() + QLatin1String("/notmanaged.cpp")); + const QString fileName = temp.fileName(); + + updateStatusForNotManagedFiles(QStringList(fileName)); + + QCOMPARE(m_statusMap->count(), 1); + + QVERIFY(m_statusMap->contains(fileName)); + QCOMPARE(m_statusMap->value(fileName).status, FileStatus::NotManaged); +} + #endif diff --git a/src/plugins/clearcase/clearcasesync.h b/src/plugins/clearcase/clearcasesync.h index cf97dd3254..1a4e4f9f77 100644 --- a/src/plugins/clearcase/clearcasesync.h +++ b/src/plugins/clearcase/clearcasesync.h @@ -42,11 +42,19 @@ public: explicit ClearCaseSync(ClearCasePlugin *plugin, QSharedPointer<StatusMap> statusMap); void run(QFutureInterface<void> &future, QStringList &files); - QStringList updateStatusHotFiles(const QString &viewRoot, const bool isDynamic, int &total); - void updateStatus(const QDir &viewRootDir, const bool isDynamic, const QStringList &files); - void processLine(const QDir &viewRootDir, const QString &buffer); + QStringList updateStatusHotFiles(const QString &viewRoot, int &total); + void invalidateStatus(const QDir &viewRootDir, const QStringList &files); + void invalidateStatusAllFiles(); + void processCleartoolLsLine(const QDir &viewRootDir, const QString &buffer); void updateTotalFilesCount(const QString view, ClearCaseSettings settings, const int processed); void updateStatusForNotManagedFiles(const QStringList &files); + + void syncDynamicView(QFutureInterface<void> &future, + const ClearCaseSettings &settings); + void syncSnapshotView(QFutureInterface<void> &future, QStringList &files, + const ClearCaseSettings &settings); + + void processCleartoolLscheckoutLine(const QString &buffer); signals: void updateStreamAndView(); @@ -59,6 +67,11 @@ public slots: void verifyParseStatus(const QString &fileName, const QString &cleartoolLsLine, const FileStatus::Status); void verifyFileNotManaged(); + + void verifyFileCheckedOutDynamicView(); + void verifyFileCheckedInDynamicView(); + void verifyFileNotManagedDynamicView(); + #endif }; |