ClearCase: Fix checking if a directory is managed

Now both dynamic and snapshot views are supported under both
Unix/Linux and Windows.

The implementation is based on "cleartool pwv" which gets the
working view.

Change-Id: I0d21d2c84fae4a641a3bac8b1087cfeffb89c447
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Knut Petter Svendsen <knutpett@pvv.org>
This commit is contained in:
Knut Petter Svendsen
2013-04-18 22:25:23 +02:00
committed by Eike Ziller
parent bedc477943
commit c222a4f623
4 changed files with 195 additions and 85 deletions

View File

@@ -118,7 +118,6 @@ static const char CMD_ID_UPDATEINDEX[] = "ClearCase.UpdateIndex";
static const char CMD_ID_UPDATE_VIEW[] = "ClearCase.UpdateView"; static const char CMD_ID_UPDATE_VIEW[] = "ClearCase.UpdateView";
static const char CMD_ID_CHECKIN_ALL[] = "ClearCase.CheckInAll"; static const char CMD_ID_CHECKIN_ALL[] = "ClearCase.CheckInAll";
static const char CMD_ID_STATUS[] = "ClearCase.Status"; static const char CMD_ID_STATUS[] = "ClearCase.Status";
static const char *CLEARCASE_ROOT_FILES[] = { "view.dat", ".view.dat" };
static const VcsBase::VcsBaseEditorParameters editorParameters[] = { static const VcsBase::VcsBaseEditorParameters editorParameters[] = {
{ {
@@ -219,58 +218,141 @@ bool ClearCasePlugin::isCheckInEditorOpen() const
return !m_checkInMessageFileName.isEmpty(); return !m_checkInMessageFileName.isEmpty();
} }
static QString ccFindRepositoryForDirectory(const QString &dirS) /// Files in this directories are under ClearCase control
QStringList ClearCasePlugin::getVobList() const
{ {
const QString home = QDir::homePath(); QStringList args(QLatin1String("lsvob"));
args << QLatin1String("-s");
const ClearCaseResponse response =
runCleartool(currentState().topLevel(), args, m_settings.timeOutMS(), SilentRun);
QDir directory(dirS); return response.stdOut.split(QLatin1Char('\n'), QString::SkipEmptyParts);
do { }
const QString absDirPath = directory.absolutePath();
if (directory.isRoot() || absDirPath == home)
break;
for (uint i = 0; i < sizeof(CLEARCASE_ROOT_FILES) / sizeof(*CLEARCASE_ROOT_FILES); ++i) /// Get the drive letter of a path
if (QFileInfo(directory, QLatin1String(CLEARCASE_ROOT_FILES[i])).isFile()) /// Necessary since QDir(directory).rootPath() returns C:/ in all cases
return absDirPath; QString ClearCasePlugin::getDriveLetterOfPath(const QString &directory)
} while (directory.cdUp()); {
// cdUp until we get just the drive letter
QDir dir(directory);
while (dir.cdUp())
{ }
return dir.path();
}
///
/// Check if the directory is managed by ClearCase.
///
/// There are 6 cases to consider for accessing ClearCase views:
///
/// 1) Windows: dynamic view under M:\<view_tag> (working dir view)
/// 2) Windows: dynamic view under Z:\ (similar to unix "set view" by using "subst" or "net use")
/// 3) Windows: snapshot view
/// 4) Unix: dynamic view under /view/<view_tag> (working dir view)
/// 5) Unix: dynamic view which are set view (transparent access in a shell process)
/// 6) Unix: snapshot view
///
/// Note: the drive letters M: and Z: can be chosen by the user. /view is the "view-root"
/// directory and is not configurable, while VOB names and mount points are configurable
/// by the ClearCase admin.
///
/// Note: All cases except #5 have a root directory, i.e., all files reside under a directory.
/// For #5 files are "mounted" and access is transparent (e.g., under /vobs).
///
/// For a view named "myview" and a VOB named "vobA" topLevels would be:
/// 1) M:/myview/vobA
/// 2) Z:/vobA
/// 3) c:/snapshots/myview/vobA
/// 4) /view/myview/vobs/vobA
/// 5) /vobs/vobA/
/// 6) /home/<username>/snapshots/myview/vobs/vobA
///
/// Note: The VOB directory is used as toplevel although the directory one up could have been
/// used on cases execpt 5. For case 5 it would have been /, which we don't want.
///
/// "cleartool pwv" returns the values for "set view" and "working directory view", also for
/// snapshot views.
///
/// \returns The ClearCase topLevel/VOB directory for this directory
QString ClearCasePlugin::ccManagesDirectory(const QString &directory) const
{
QStringList args(QLatin1String("pwv"));
const ClearCaseResponse response =
runCleartool(directory, args, m_settings.timeOutMS(), SilentRun);
if (response.error)
return QString(); return QString();
const QStringList result = response.stdOut.split(QLatin1Char('\n'), QString::SkipEmptyParts);
if (result.size() != 2)
return QString();
const QByteArray workingDirPattern("Working directory view: ");
if (!result[0].startsWith(QLatin1String(workingDirPattern)))
return QString();
const QString workingDirectoryView = result[0].mid(workingDirPattern.size());
const QByteArray setViewDirPattern("Set view: ");
if (!result[1].startsWith(QLatin1String(setViewDirPattern)))
return QString();
const QString setView = result[1].mid(setViewDirPattern.size());
const QString none(QLatin1String("** NONE **"));
QString rootDir;
if (setView != none || workingDirectoryView != none)
rootDir = ccViewRoot(directory);
else
return QString();
// Check if the directory is inside one of the known VOBs.
static QStringList vobs;
if (vobs.empty())
vobs = getVobList();
foreach (const QString &relativeVobDir, vobs) {
const QString vobPath = QDir::cleanPath(rootDir + QDir::fromNativeSeparators(relativeVobDir));
const bool isManaged = Utils::FileName::fromString(directory).isChildOf(Utils::FileName::fromString(vobPath));
if (isManaged)
return vobPath;
}
return QString();
}
/// Find the root path of a clearcase view. Precondition: This is a clearcase managed dir
QString ClearCasePlugin::ccViewRoot(const QString &directory) const
{
QStringList args(QLatin1String("pwv"));
args << QLatin1String("-root");
const ClearCaseResponse response =
runCleartool(directory, args, m_settings.timeOutMS(), SilentRun);
QString root = response.stdOut.trimmed();
if (root.isEmpty()) {
if (Utils::HostOsInfo::isWindowsHost())
root = getDriveLetterOfPath(directory);
else
root = QLatin1String("/");
}
return QDir::fromNativeSeparators(root);
} }
/*! Find top level for view that contains \a directory /*! Find top level for view that contains \a directory
* *
* - Snapshot view has one of CLEARCASE_ROOT_FILES (view.dat or .view.dat) in its top dir * Handles both dynamic views and snapshot views.
* - Dynamic view can either be
* - M:/view_name,
* - or mapped to a drive letter, like Z:/
* (drive letters are just examples)
*/ */
QString ClearCasePlugin::findTopLevel(const QString &directory) const QString ClearCasePlugin::findTopLevel(const QString &directory) const
{ {
// Do not check again if we've already tested that the dir is managed,
// or if it is a child of a managed dir (top level).
if ((directory == m_topLevel) || if ((directory == m_topLevel) ||
Utils::FileName::fromString(directory).isChildOf(Utils::FileName::fromString(m_topLevel))) Utils::FileName::fromString(directory).isChildOf(Utils::FileName::fromString(m_topLevel)))
return m_topLevel; return m_topLevel;
// Snapshot view return ccManagesDirectory(directory);
QString topLevel =
ccFindRepositoryForDirectory(directory);
if (!topLevel.isEmpty() || !clearCaseControl()->isConfigured())
return topLevel;
// Dynamic view
if (ccGetView(directory).isDynamic) {
QDir dir(directory);
// Go up to one level before root
QDir outer = dir;
outer.cdUp();
while (outer.cdUp())
dir.cdUp();
topLevel = dir.path(); // M:/View_Name
dir.cdUp(); // Z:/ (dynamic view with assigned letter)
if (!ccGetView(dir.path()).name.isEmpty())
topLevel = dir.path();
}
return topLevel;
} }
static const VcsBase::VcsBaseSubmitEditorParameters submitParameters = { static const VcsBase::VcsBaseSubmitEditorParameters submitParameters = {
@@ -555,14 +637,15 @@ QString ClearCasePlugin::ccGetPredecessor(const QString &version) const
} }
//! Get a list of paths to active VOBs. //! Get a list of paths to active VOBs.
//! Paths are relative to topLevel //! Paths are relative to viewRoot
QStringList ClearCasePlugin::ccGetActiveVobs() const QStringList ClearCasePlugin::ccGetActiveVobs() const
{ {
QStringList res; QStringList res;
QStringList args(QLatin1String("lsvob")); QStringList args(QLatin1String("lsvob"));
const QString topLevel = currentState().topLevel(); const QString theViewRoot = viewRoot();
const ClearCaseResponse response = const ClearCaseResponse response =
runCleartool(topLevel, args, m_settings.timeOutMS(), SilentRun); runCleartool(theViewRoot, args, m_settings.timeOutMS(), SilentRun);
if (response.error) if (response.error)
return res; return res;
@@ -570,10 +653,11 @@ QStringList ClearCasePlugin::ccGetActiveVobs() const
// * /path/to/vob /path/to/vob/storage.vbs <and some text omitted here> // * /path/to/vob /path/to/vob/storage.vbs <and some text omitted here>
// format of output windows: // format of output windows:
// * \vob \\share\path\to\vob\storage.vbs <and some text omitted here> // * \vob \\share\path\to\vob\storage.vbs <and some text omitted here>
QString prefix = topLevel; QString prefix = theViewRoot;
if (!prefix.endsWith(QLatin1Char('/'))) if (!prefix.endsWith(QLatin1Char('/')))
prefix += QLatin1Char('/'); prefix += QLatin1Char('/');
const QDir theViewRootDir(theViewRoot);
foreach (const QString &line, response.stdOut.split(QLatin1Char('\n'), QString::SkipEmptyParts)) { foreach (const QString &line, response.stdOut.split(QLatin1Char('\n'), QString::SkipEmptyParts)) {
const bool isActive = line.at(0) == QLatin1Char('*'); const bool isActive = line.at(0) == QLatin1Char('*');
if (!isActive) if (!isActive)
@@ -581,7 +665,7 @@ QStringList ClearCasePlugin::ccGetActiveVobs() const
const QString dir = const QString dir =
QDir::fromNativeSeparators(line.mid(3, line.indexOf(QLatin1Char(' '), 3) - 3)); QDir::fromNativeSeparators(line.mid(3, line.indexOf(QLatin1Char(' '), 3) - 3));
const QString relativeDir = QDir(topLevel).relativeFilePath(dir); const QString relativeDir = theViewRootDir.relativeFilePath(dir);
// Snapshot views does not necessarily have all active VOBs loaded, so we'll have to // Snapshot views does not necessarily have all active VOBs loaded, so we'll have to
// check if the dirs exists as well. Else the command will work, but the output will // check if the dirs exists as well. Else the command will work, but the output will
@@ -628,11 +712,11 @@ void ClearCasePlugin::updateStatusActions()
FileStatus fileStatus = FileStatus::Unknown; FileStatus fileStatus = FileStatus::Unknown;
bool hasFile = currentState().hasFile(); bool hasFile = currentState().hasFile();
if (hasFile) { if (hasFile) {
QString fileName = currentState().relativeCurrentFile(); QString absoluteFileName = currentState().currentFile();
fileStatus = m_statusMap->value(fileName, FileStatus(FileStatus::Unknown)); fileStatus = m_statusMap->value(absoluteFileName, FileStatus(FileStatus::Unknown));
if (ClearCase::Constants::debug) if (ClearCase::Constants::debug)
qDebug() << Q_FUNC_INFO << fileName << ", status = " << fileStatus.status; qDebug() << Q_FUNC_INFO << absoluteFileName << ", status = " << fileStatus.status;
} }
m_checkOutAction->setEnabled(hasFile && (fileStatus.status & (FileStatus::CheckedIn | FileStatus::Hijacked))); m_checkOutAction->setEnabled(hasFile && (fileStatus.status & (FileStatus::CheckedIn | FileStatus::Hijacked)));
@@ -686,10 +770,12 @@ void ClearCasePlugin::addCurrentFile()
vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile()); vcsAdd(state.currentFileTopLevel(), state.relativeCurrentFile());
} }
// Set the FileStatus of file given in absolute path
void ClearCasePlugin::setStatus(const QString &file, FileStatus::Status status, bool update) void ClearCasePlugin::setStatus(const QString &file, FileStatus::Status status, bool update)
{ {
m_statusMap->insert(file, FileStatus(status, QFileInfo(currentState().topLevel(), file).permissions())); m_statusMap->insert(file, FileStatus(status, QFileInfo(file).permissions()));
if (update && (currentState().relativeCurrentFile() == file))
if (update && currentState().currentFile() == file)
QMetaObject::invokeMethod(this, "updateStatusActions"); QMetaObject::invokeMethod(this, "updateStatusActions");
} }
@@ -738,9 +824,11 @@ bool ClearCasePlugin::vcsUndoCheckOut(const QString &workingDir, const QString &
ShowStdOutInLogWindow | FullySynchronously); ShowStdOutInLogWindow | FullySynchronously);
if (!response.error) { if (!response.error) {
const QString absPath = workingDir + QLatin1Char('/') + fileName;
if (!m_settings.disableIndexer) if (!m_settings.disableIndexer)
setStatus(fileName, FileStatus::CheckedIn); setStatus(absPath, FileStatus::CheckedIn);
clearCaseControl()->emitFilesChanged(QStringList(fileName)); clearCaseControl()->emitFilesChanged(QStringList(absPath));
} }
return !response.error; return !response.error;
} }
@@ -767,8 +855,10 @@ bool ClearCasePlugin::vcsUndoHijack(const QString &workingDir, const QString &fi
const ClearCaseResponse response = const ClearCaseResponse response =
runCleartool(workingDir, args, m_settings.timeOutMS(), runCleartool(workingDir, args, m_settings.timeOutMS(),
ShowStdOutInLogWindow | FullySynchronously); ShowStdOutInLogWindow | FullySynchronously);
if (!response.error && !m_settings.disableIndexer) if (!response.error && !m_settings.disableIndexer) {
setStatus(fileName, FileStatus::CheckedIn); const QString absPath = workingDir + QLatin1Char('/') + fileName;
setStatus(absPath, FileStatus::CheckedIn);
}
return !response.error; return !response.error;
} }
@@ -819,8 +909,9 @@ void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringLis
QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VcsBase::VcsBaseEditorWidget::getCodec(source); QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VcsBase::VcsBaseEditorWidget::getCodec(source);
if ((m_settings.diffType == GraphicalDiff) && (files.count() == 1)) { if ((m_settings.diffType == GraphicalDiff) && (files.count() == 1)) {
QString file = files.first(); const QString file = files.first();
if (m_statusMap->value(file).status == FileStatus::Hijacked) const QString absFilePath = workingDir + QLatin1Char('/') + file;
if (m_statusMap->value(absFilePath).status == FileStatus::Hijacked)
diffGraphical(ccGetFileVersion(workingDir, file), file); diffGraphical(ccGetFileVersion(workingDir, file), file);
else else
diffGraphical(file); diffGraphical(file);
@@ -833,7 +924,8 @@ void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringLis
} }
QString result; QString result;
foreach (const QString &file, files) { foreach (const QString &file, files) {
if (m_statusMap->value(QDir::fromNativeSeparators(file)).status == FileStatus::Hijacked) const QString absFilePath = workingDir + QLatin1Char('/') + file;
if (m_statusMap->value(QDir::fromNativeSeparators(absFilePath)).status == FileStatus::Hijacked)
result += diffExternal(ccGetFileVersion(workingDir, file), file); result += diffExternal(ccGetFileVersion(workingDir, file), file);
else else
result += diffExternal(file); result += diffExternal(file);
@@ -1370,14 +1462,14 @@ bool ClearCasePlugin::vcsOpen(const QString &workingDir, const QString &fileName
CheckOutDialog coDialog(title, m_viewData.isUcm); CheckOutDialog coDialog(title, m_viewData.isUcm);
if (!m_settings.disableIndexer && if (!m_settings.disableIndexer &&
(fi.isWritable() || m_statusMap->value(relFile).status == FileStatus::Unknown)) (fi.isWritable() || m_statusMap->value(absPath).status == FileStatus::Unknown))
QtConcurrent::run(&sync, topLevel, QStringList(relFile)).waitForFinished(); QtConcurrent::run(&sync, QStringList(absPath)).waitForFinished();
if (m_statusMap->value(relFile).status == FileStatus::CheckedOut) { if (m_statusMap->value(absPath).status == FileStatus::CheckedOut) {
QMessageBox::information(0, tr("ClearCase Checkout"), tr("File is already checked out.")); QMessageBox::information(0, tr("ClearCase Checkout"), tr("File is already checked out."));
return true; return true;
} }
// Only snapshot views can have hijacked files // Only snapshot views can have hijacked files
bool isHijacked = (!m_viewData.isDynamic && (m_statusMap->value(relFile).status & FileStatus::Hijacked)); bool isHijacked = (!m_viewData.isDynamic && (m_statusMap->value(absPath).status & FileStatus::Hijacked));
if (!isHijacked) if (!isHijacked)
coDialog.hideHijack(); coDialog.hideHijack();
if (coDialog.exec() == QDialog::Accepted) { if (coDialog.exec() == QDialog::Accepted) {
@@ -1440,8 +1532,10 @@ bool ClearCasePlugin::vcsOpen(const QString &workingDir, const QString &fileName
QFile::rename(absPath + QLatin1String(".hijack"), absPath); QFile::rename(absPath + QLatin1String(".hijack"), absPath);
} }
if ((!response.error || response.stdOut.contains(QLatin1String("already checked out"))) && !m_settings.disableIndexer) if ((!response.error || response.stdOut.contains(QLatin1String("already checked out")))
setStatus(relFile, FileStatus::CheckedOut); && !m_settings.disableIndexer) {
setStatus(absPath, FileStatus::CheckedOut);
}
return !response.error; return !response.error;
} }
return true; return true;
@@ -1501,8 +1595,11 @@ bool ClearCasePlugin::vcsCheckIn(const QString &messageFile, const QStringList &
int offset = checkedIn.indexIn(response.stdOut); int offset = checkedIn.indexIn(response.stdOut);
while (offset != -1) { while (offset != -1) {
QString file = checkedIn.cap(1); QString file = checkedIn.cap(1);
QFileInfo fi(m_checkInView, file);
QString absPath = fi.absoluteFilePath();
if (!m_settings.disableIndexer) if (!m_settings.disableIndexer)
setStatus(QDir::fromNativeSeparators(file), FileStatus::CheckedIn); setStatus(QDir::fromNativeSeparators(absPath), FileStatus::CheckedIn);
clearCaseControl()->emitFilesChanged(files); clearCaseControl()->emitFilesChanged(files);
anySucceeded = true; anySucceeded = true;
offset = checkedIn.indexIn(response.stdOut, offset + 12); offset = checkedIn.indexIn(response.stdOut, offset + 12);
@@ -1639,7 +1736,9 @@ QString ClearCasePlugin::vcsGetRepositoryURL(const QString & /*directory*/)
return currentState().topLevel(); return currentState().topLevel();
} }
// ClearCase has "view.dat" file in the root directory it manages for snapshot views. ///
/// Check if the directory is managed under ClearCase control.
///
bool ClearCasePlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */) const bool ClearCasePlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */) const
{ {
QString topLevelFound = findTopLevel(directory); QString topLevelFound = findTopLevel(directory);
@@ -1770,6 +1869,7 @@ ViewData ClearCasePlugin::ccGetView(const QString &workingDir) const
res.isDynamic = !data.isEmpty() && (data.at(0) == QLatin1Char('*')); res.isDynamic = !data.isEmpty() && (data.at(0) == QLatin1Char('*'));
res.name = data.mid(2, data.indexOf(QLatin1Char(' '), 2) - 2); res.name = data.mid(2, data.indexOf(QLatin1Char(' '), 2) - 2);
res.isUcm = ccCheckUcm(res.name, workingDir); res.isUcm = ccCheckUcm(res.name, workingDir);
res.root = ccViewRoot(workingDir);
} }
return res; return res;
@@ -1829,7 +1929,7 @@ void ClearCasePlugin::updateIndex()
return; return;
m_checkInAllAction->setEnabled(false); m_checkInAllAction->setEnabled(false);
m_statusMap->clear(); m_statusMap->clear();
QFuture<void> result = QtConcurrent::run(&sync, currentState().topLevel(), QFuture<void> result = QtConcurrent::run(&sync,
project->files(ProjectExplorer::Project::ExcludeGeneratedFiles)); project->files(ProjectExplorer::Project::ExcludeGeneratedFiles));
if (!m_settings.disableIndexer) if (!m_settings.disableIndexer)
Core::ICore::progressManager()->addTask(result, tr("CC Indexing"), Core::ICore::progressManager()->addTask(result, tr("CC Indexing"),
@@ -1969,7 +2069,7 @@ void ClearCasePlugin::syncSlot()
QString topLevel = state.topLevel(); QString topLevel = state.topLevel();
if (topLevel != state.currentProjectTopLevel()) if (topLevel != state.currentProjectTopLevel())
return; return;
QtConcurrent::run(&sync, topLevel, QStringList()); QtConcurrent::run(&sync, QStringList());
} }
void ClearCasePlugin::closing() void ClearCasePlugin::closing()
@@ -1979,12 +2079,12 @@ void ClearCasePlugin::closing()
disconnect(Core::ICore::mainWindow(), SIGNAL(windowActivated()), this, SLOT(syncSlot())); disconnect(Core::ICore::mainWindow(), SIGNAL(windowActivated()), this, SLOT(syncSlot()));
} }
void ClearCasePlugin::sync(QFutureInterface<void> &future, QString topLevel, QStringList files) void ClearCasePlugin::sync(QFutureInterface<void> &future, QStringList files)
{ {
ClearCasePlugin *plugin = ClearCasePlugin::instance(); ClearCasePlugin *plugin = ClearCasePlugin::instance();
ClearCaseSync ccSync(plugin, plugin->m_statusMap); ClearCaseSync ccSync(plugin, plugin->m_statusMap);
connect(&ccSync, SIGNAL(updateStreamAndView()), plugin, SLOT(updateStreamAndView())); connect(&ccSync, SIGNAL(updateStreamAndView()), plugin, SLOT(updateStreamAndView()));
ccSync.run(future, topLevel, files); ccSync.run(future, files);
} }
#ifdef WITH_TESTS #ifdef WITH_TESTS

View File

@@ -107,6 +107,7 @@ public:
QString name; QString name;
bool isDynamic; bool isDynamic;
bool isUcm; bool isUcm;
QString root;
}; };
class ClearCasePlugin : public VcsBase::VcsBasePlugin class ClearCasePlugin : public VcsBase::VcsBasePlugin
@@ -156,6 +157,7 @@ public:
const QString &fileName, const QString &file2 = QString()); const QString &fileName, const QString &file2 = QString());
FileStatus vcsStatus(const QString &file) const; FileStatus vcsStatus(const QString &file) const;
QString currentView() const { return m_viewData.name; } QString currentView() const { return m_viewData.name; }
QString viewRoot() const { return m_viewData.root; }
void refreshActivities(); void refreshActivities();
inline bool isUcm() const { return m_viewData.isUcm; } inline bool isUcm() const { return m_viewData.isUcm; }
inline bool isDynamic() const { return m_viewData.isDynamic; } inline bool isDynamic() const { return m_viewData.isDynamic; }
@@ -207,6 +209,9 @@ protected:
private: private:
inline bool isCheckInEditorOpen() const; inline bool isCheckInEditorOpen() const;
QStringList getVobList() const;
QString ccManagesDirectory(const QString &directory) const;
QString ccViewRoot(const QString &directory) const;
QString findTopLevel(const QString &directory) const; QString findTopLevel(const QString &directory) const;
Core::IEditor *showOutputInEditor(const QString& title, const QString &output, Core::IEditor *showOutputInEditor(const QString& title, const QString &output,
int editorType, const QString &source, int editorType, const QString &source,
@@ -215,7 +220,7 @@ private:
ClearCaseResponse runCleartool(const QString &workingDir, ClearCaseResponse runCleartool(const QString &workingDir,
const QStringList &arguments, int timeOut, const QStringList &arguments, int timeOut,
unsigned flags, QTextCodec *outputCodec = 0) const; unsigned flags, QTextCodec *outputCodec = 0) const;
static void sync(QFutureInterface<void> &future, QString topLevel, QStringList files); static void sync(QFutureInterface<void> &future, QStringList files);
void history(const QString &workingDir, void history(const QString &workingDir,
const QStringList &file = QStringList(), const QStringList &file = QStringList(),
@@ -234,6 +239,7 @@ private:
static void rmdir(const QString &path); static void rmdir(const QString &path);
QString runExtDiff(const QString &workingDir, const QStringList &arguments, QString runExtDiff(const QString &workingDir, const QStringList &arguments,
int timeOut, QTextCodec *outputCodec = 0); int timeOut, QTextCodec *outputCodec = 0);
static QString getDriveLetterOfPath(const QString &directory);
ClearCaseSettings m_settings; ClearCaseSettings m_settings;

View File

@@ -44,7 +44,7 @@ ClearCaseSync::ClearCaseSync(ClearCasePlugin *plugin, QSharedPointer<StatusMap>
{ {
} }
void ClearCaseSync::run(QFutureInterface<void> &future, const QString &topLevel, QStringList &files) void ClearCaseSync::run(QFutureInterface<void> &future, QStringList &files)
{ {
ClearCaseSettings settings = m_plugin->settings(); ClearCaseSettings settings = m_plugin->settings();
const QString program = settings.ccBinaryPath; const QString program = settings.ccBinaryPath;
@@ -66,8 +66,9 @@ void ClearCaseSync::run(QFutureInterface<void> &future, const QString &topLevel,
if (settings.disableIndexer) if (settings.disableIndexer)
return; return;
const QDir topLevelDir(topLevel);
const bool isDynamic = m_plugin->isDynamic(); const bool isDynamic = m_plugin->isDynamic();
const QString viewRoot = m_plugin->viewRoot();
const QDir viewRootDir(viewRoot);
QStringList args(QLatin1String("ls")); QStringList args(QLatin1String("ls"));
if (hot) { if (hot) {
@@ -75,7 +76,7 @@ void ClearCaseSync::run(QFutureInterface<void> &future, const QString &topLevel,
// (might have become checked out) // (might have become checked out)
const StatusMap::Iterator send = m_statusMap->end(); const StatusMap::Iterator send = m_statusMap->end();
for (StatusMap::Iterator it = m_statusMap->begin(); it != send; ++it) { for (StatusMap::Iterator it = m_statusMap->begin(); it != send; ++it) {
const QFileInfo fi(topLevel, it.key()); const QFileInfo fi(viewRoot, it.key());
const bool permChanged = it.value().permissions != fi.permissions(); const bool permChanged = it.value().permissions != fi.permissions();
if (permChanged || it.value().status == FileStatus::Hijacked) { if (permChanged || it.value().status == FileStatus::Hijacked) {
files.append(it.key()); files.append(it.key());
@@ -90,11 +91,11 @@ void ClearCaseSync::run(QFutureInterface<void> &future, const QString &topLevel,
} else { } else {
foreach (const QString &file, files) { foreach (const QString &file, files) {
if (isDynamic) { // assume a read only file is checked in if (isDynamic) { // assume a read only file is checked in
const QFileInfo fi(topLevelDir, file); const QFileInfo fi(viewRootDir, file);
if (!fi.isWritable()) if (!fi.isWritable())
m_plugin->setStatus(topLevelDir.relativeFilePath(file), FileStatus::CheckedIn, false); m_plugin->setStatus(fi.absoluteFilePath(), FileStatus::CheckedIn, false);
} else { } else {
m_plugin->setStatus(topLevelDir.relativeFilePath(file), FileStatus::Unknown, false); m_plugin->setStatus(viewRootDir.absoluteFilePath(file), FileStatus::Unknown, false);
} }
} }
args << QLatin1String("-recurse"); args << QLatin1String("-recurse");
@@ -112,7 +113,7 @@ void ClearCaseSync::run(QFutureInterface<void> &future, const QString &topLevel,
// (we don't want it to become green) // (we don't want it to become green)
future.setProgressRange(0, total + 1); future.setProgressRange(0, total + 1);
QProcess process; QProcess process;
process.setWorkingDirectory(topLevel); process.setWorkingDirectory(viewRoot);
process.start(program, args); process.start(program, args);
if (!process.waitForStarted()) if (!process.waitForStarted())
@@ -130,21 +131,24 @@ void ClearCaseSync::run(QFutureInterface<void> &future, const QString &topLevel,
if (atatpos != -1) { // probably managed file if (atatpos != -1) { // probably managed file
// find first whitespace. anything before that is not interesting // find first whitespace. anything before that is not interesting
const int wspos = buffer.indexOf(QRegExp(QLatin1String("\\s"))); const int wspos = buffer.indexOf(QRegExp(QLatin1String("\\s")));
const QString relFile = topLevelDir.relativeFilePath(QDir::fromNativeSeparators(buffer.left(atatpos))); const QString absFile =
viewRootDir.absoluteFilePath(
QDir::fromNativeSeparators(buffer.left(atatpos)));
QString ccState; QString ccState;
const QRegExp reState(QLatin1String("^\\s*\\[[^\\]]*\\]")); // [hijacked]; [loaded but missing] const QRegExp reState(QLatin1String("^\\s*\\[[^\\]]*\\]")); // [hijacked]; [loaded but missing]
if (reState.indexIn(buffer, wspos + 1, QRegExp::CaretAtOffset) != -1) { if (reState.indexIn(buffer, wspos + 1, QRegExp::CaretAtOffset) != -1) {
ccState = reState.cap(); ccState = reState.cap();
if (ccState.indexOf(QLatin1String("hijacked")) != -1) if (ccState.indexOf(QLatin1String("hijacked")) != -1)
m_plugin->setStatus(relFile, FileStatus::Hijacked, true); m_plugin->setStatus(absFile, FileStatus::Hijacked, true);
else if (ccState.indexOf(QLatin1String("loaded but missing")) != -1) else if (ccState.indexOf(QLatin1String("loaded but missing")) != -1)
m_plugin->setStatus(relFile, FileStatus::Missing, false); m_plugin->setStatus(absFile, FileStatus::Missing, false);
} }
else if (buffer.lastIndexOf(QLatin1String("CHECKEDOUT"), wspos) != -1) else if (buffer.lastIndexOf(QLatin1String("CHECKEDOUT"), wspos) != -1)
m_plugin->setStatus(relFile, FileStatus::CheckedOut, true); m_plugin->setStatus(absFile, FileStatus::CheckedOut, true);
// don't care about checked-in files not listed in project // don't care about checked-in files not listed in project
else if (m_statusMap->contains(relFile)) else if (m_statusMap->contains(absFile))
m_plugin->setStatus(relFile, FileStatus::CheckedIn, true); m_plugin->setStatus(absFile, FileStatus::CheckedIn, true);
} }
buffer.clear(); buffer.clear();
future.setProgressValue(qMin(total, ++processed)); future.setProgressValue(qMin(total, ++processed));
@@ -154,9 +158,9 @@ void ClearCaseSync::run(QFutureInterface<void> &future, const QString &topLevel,
if (!future.isCanceled()) { if (!future.isCanceled()) {
foreach (const QString &file, files) { foreach (const QString &file, files) {
const QString relFile = topLevelDir.relativeFilePath(file); QString absFile = QFileInfo(file).absoluteFilePath();
if (m_statusMap->value(relFile).status == FileStatus::Unknown) if (!m_statusMap->contains(absFile))
m_plugin->setStatus(relFile, FileStatus::NotManaged, false); m_plugin->setStatus(absFile, FileStatus::NotManaged, false);
} }
future.setProgressValue(total + 1); future.setProgressValue(total + 1);
if (!hot) { if (!hot) {

View File

@@ -40,7 +40,7 @@ class ClearCaseSync : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit ClearCaseSync(ClearCasePlugin *plugin, QSharedPointer<StatusMap> statusMap); explicit ClearCaseSync(ClearCasePlugin *plugin, QSharedPointer<StatusMap> statusMap);
void run(QFutureInterface<void> &future, const QString &topLevel, QStringList &files); void run(QFutureInterface<void> &future, QStringList &files);
signals: signals:
void updateStreamAndView(); void updateStreamAndView();