Hatred's Log Place

DON'T PANIC!

Sep 10, 2013 - 6 minute read - programming

Qt Creator и perforce

UPD: Изменения, на основе этого патча, заинтегрированы в Qt Creator другим человеком. От меня была только ревью кода.

Во времена, когда проект Qt и все смежные тулы были под управлением VCS Perforce, появился плагин для Qt Creator’а для интеграции работы с этой системой, да вот только в своём развитии он конкретно и застрял в тех стародавних временах. Причина проста: проект переехал на Git, а перфорс нафиг никому не сдался. Как результат: плагин есть, и даже собирается, да вот только считать его хоть малость рабочим… не получается.

Как минимум одна особенность (читать бага), перечёркивает весь (и без того убогий) функционал: он не может определить директорию верхнего уровня для файлов.

При этом, это вроде как работает, когда забиваешь настройки, да вот только они там и поселяются навсегда, прибитые к одному серверу и одному пользователю. А кроме того, за директорию верхнего уровня принимается текущая рабочая директория (читать: та, из которой запущен Qt Creator).

Немного повозившись, родился патч:

From c111318c78acb2b7ddd41533de3577fa7dc64b1d Mon Sep 17 00:00:00 2001
From: Alexander Drozdov <adrozdoff@gmail.com>
Date: Tue, 10 Sep 2013 17:33:55 +1100
Subject: [PATCH] Perforce VCS plugin: fix top-level directory detection

---
 src/plugins/perforce/perforcechecker.cpp        | 21 ++++++++-
 src/plugins/perforce/perforcechecker.h          |  5 ++-
 src/plugins/perforce/perforceconstants.h        |  2 +-
 src/plugins/perforce/perforceplugin.cpp         | 57 ++++++++++++++++++++-----
 src/plugins/perforce/perforceplugin.h           | 16 ++++++-
 src/plugins/perforce/perforceversioncontrol.cpp |  2 +-
 6 files changed, 86 insertions(+), 17 deletions(-)

diff --git a/src/plugins/perforce/perforcechecker.cpp b/src/plugins/perforce/perforcechecker.cpp
index 5971c00..b9b0395 100644
--- a/src/plugins/perforce/perforcechecker.cpp
+++ b/src/plugins/perforce/perforcechecker.cpp
@@ -28,6 +28,7 @@
 ****************************************************************************/
 
 #include "perforcechecker.h"
+#include "perforceconstants.h"
 
 #include <utils/qtcassert.h>
 #include <utils/synchronousprocess.h>
@@ -78,7 +79,8 @@ void PerforceChecker::resetOverrideCursor()
 
 void PerforceChecker::start(const QString &binary,
                             const QStringList &basicArgs,
-                            int timeoutMS)
+                            int timeoutMS,
+                            const QString &workingDirectory)
 {
     if (isRunning()) {
         emitFailed(QLatin1String("Internal error: process still running"));
@@ -91,6 +93,15 @@ void PerforceChecker::start(const QString &binary,
     m_binary = binary;
     QStringList args = basicArgs;
     args << QLatin1String("client") << QLatin1String("-o");
+
+    if (Perforce::Constants::debug)
+        qDebug() << "PerforceChecker::start: [" << workingDirectory << "]" << m_binary << args;
+
+    if (!workingDirectory.isEmpty())
+    {
+        m_process.setWorkingDirectory(workingDirectory);
+    }
+
     m_process.start(m_binary, args);
     m_process.closeWriteChannel();
     // Timeout handling
@@ -105,6 +116,11 @@ void PerforceChecker::start(const QString &binary,
     }
 }
 
+bool PerforceChecker::waitForFinished(int msec)
+{
+    return m_process.waitForFinished(msec);
+}
+
 void PerforceChecker::slotTimeOut()
 {
     if (!isRunning())
@@ -169,6 +185,9 @@ static inline QString clientRootFromOutput(const QString &in)
 
 void PerforceChecker::parseOutput(const QString &response)
 {
+    if (Perforce::Constants::debug)
+        qDebug() << "PerforceChecker::parseOutput: " << response;
+
     if (!response.contains(QLatin1String("View:")) && !response.contains(QLatin1String("//depot/"))) {
         emitFailed(tr("The client does not seem to contain any mapped files."));
         return;
diff --git a/src/plugins/perforce/perforcechecker.h b/src/plugins/perforce/perforcechecker.h
index e466250..aada848 100644
--- a/src/plugins/perforce/perforcechecker.h
+++ b/src/plugins/perforce/perforcechecker.h
@@ -50,10 +50,13 @@ public:
 public slots:
     void start(const QString &binary,
                const QStringList &basicArgs = QStringList(),
-               int timeoutMS = -1);
+               int timeoutMS = -1,
+               const QString &workingDirectory = QString());
 
     bool isRunning() const;
 
+    bool waitForFinished(int msec = -1);
+
     bool useOverideCursor() const;
     void setUseOverideCursor(bool v);
 
diff --git a/src/plugins/perforce/perforceconstants.h b/src/plugins/perforce/perforceconstants.h
index 316fc24..1f8c2a1 100644
--- a/src/plugins/perforce/perforceconstants.h
+++ b/src/plugins/perforce/perforceconstants.h
@@ -55,7 +55,7 @@ const char SUBMIT_CURRENT[] = "Perforce.SubmitCurrentLog";
 const char DIFF_SELECTED[] = "Perforce.DiffSelectedFilesInLog";
 const char SUBMIT_MIMETYPE[] = "text/vnd.qtcreator.p4.submit";
 
-enum { debug = 0 };
+enum { debug = 1 };
 
 } // Constants
 } // Perforce
diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index 8fd5957..1f9480d 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -824,7 +824,11 @@ bool PerforcePlugin::managesDirectory(const QString &directory, QString *topLeve
     const bool rc = managesDirectoryFstat(directory);
     if (topLevel) {
         if (rc)
+        {
+            if (Perforce::Constants::debug)
+                qDebug() << "Top Level: " << m_settings.topLevelSymLinkTarget();
             *topLevel = m_settings.topLevelSymLinkTarget();
+        }
         else
             topLevel->clear();
     }
@@ -834,28 +838,48 @@ bool PerforcePlugin::managesDirectory(const QString &directory, QString *topLeve
 bool PerforcePlugin::managesDirectoryFstat(const QString &directory)
 {
     if (!m_settings.isValid())
+    {
+        if (Perforce::Constants::debug)
+            qDebug() << "Settings invalid";
         return false;
+    }
     // Cached?
     const ManagedDirectoryCache::const_iterator cit = m_managedDirectoryCache.constFind(directory);
     if (cit != m_managedDirectoryCache.constEnd())
-        return cit.value();
+    {
+        const DirectoryCacheEntry &entry = cit.value();
+        if (Perforce::Constants::debug)
+            qDebug() << "Directory: " << directory << " is cached and managed: " << entry.isManaged;
+
+        setNewTopLevel(entry.topLevel);
+
+        return entry.isManaged;
+    }
     // Determine value and insert into cache
     bool managed = false;
     do {
         // Quick check: Must be at or below top level and not "../../other_path"
         const QStringList relativeDirArgs = m_settings.relativeToTopLevelArguments(directory);
         if (!relativeDirArgs.empty() && relativeDirArgs.front().startsWith(QLatin1String("..")))
-            break;
+        {
+            if (Perforce::Constants::debug)
+                qDebug() << "Directory " << directory << " is a relative path to current top level dir [" << relativeDirArgs << "], try find new top level.";
+            getTopLevel(directory, true);
+        }
         // Is it actually managed by perforce?
         QStringList args;
         args << QLatin1String("fstat") << QLatin1String("-m1") << perforceRelativeFileArguments(relativeDirArgs);
         const PerforceResponse result = runP4Cmd(m_settings.topLevel(), args,
                                                  RunFullySynchronous);
+
+        if (Perforce::Constants::debug)
+            qDebug() << "Perforce result:\n" << result.stdOut << "\n---\n" << result.stdErr << "\n---\n" << result.message;
+
         managed = result.stdOut.contains(QLatin1String("depotFile"))
                   || result.stdErr.contains(QLatin1String("... - no such file(s)"));
     } while (false);
 
-    m_managedDirectoryCache.insert(directory, managed);
+    m_managedDirectoryCache.insert(directory, DirectoryCacheEntry(managed, m_settings.topLevel()));
     return managed;
 }
 
@@ -1489,12 +1513,7 @@ PerforceVersionControl *PerforcePlugin::perforceVersionControl() const
 
 void PerforcePlugin::slotTopLevelFound(const QString &t)
 {
-    m_settings.setTopLevel(t);
-    const QString msg = tr("Perforce repository: %1").
-                        arg(QDir::toNativeSeparators(t));
-    VcsBase::VcsBaseOutputWindow::instance()->appendSilently(msg);
-    if (Perforce::Constants::debug)
-        qDebug() << "P4: " << t;
+    setNewTopLevel(t);
 }
 
 void PerforcePlugin::slotTopLevelFailed(const QString &errorMessage)
@@ -1504,7 +1523,7 @@ void PerforcePlugin::slotTopLevelFailed(const QString &errorMessage)
         qDebug() << errorMessage;
 }
 
-void PerforcePlugin::getTopLevel()
+void PerforcePlugin::getTopLevel(const QString &workingDirectory, bool isSync)
 {
     // Run a new checker
     if (m_settings.p4BinaryPath().isEmpty())
@@ -1514,7 +1533,23 @@ void PerforcePlugin::getTopLevel()
     connect(checker, SIGNAL(failed(QString)), checker, SLOT(deleteLater()));
     connect(checker, SIGNAL(succeeded(QString)), this, SLOT(slotTopLevelFound(QString)));
     connect(checker, SIGNAL(succeeded(QString)),checker, SLOT(deleteLater()));
-    checker->start(m_settings.p4BinaryPath(), m_settings.commonP4Arguments(QString()), 30000);
+    checker->start(m_settings.p4BinaryPath(), m_settings.commonP4Arguments(QString()), 30000, workingDirectory);
+
+    if (isSync)
+        checker->waitForFinished();
+}
+
+void PerforcePlugin::setNewTopLevel(const QString &newTopLevel)
+{
+    if (m_settings.topLevel() != newTopLevel)
+    {
+        m_settings.setTopLevel(newTopLevel);
+        const QString msg = tr("Perforce repository: %1").
+                            arg(QDir::toNativeSeparators(newTopLevel));
+        VcsBase::VcsBaseOutputWindow::instance()->appendSilently(msg);
+        if (Perforce::Constants::debug)
+            qDebug() << "P4: " << newTopLevel;
+    }
 }
 
 #ifdef WITH_TESTS
diff --git a/src/plugins/perforce/perforceplugin.h b/src/plugins/perforce/perforceplugin.h
index 8eeb1e2..8e08500 100644
--- a/src/plugins/perforce/perforceplugin.h
+++ b/src/plugins/perforce/perforceplugin.h
@@ -146,7 +146,18 @@ protected:
 
 
 private:
-    typedef QHash<QString, bool> ManagedDirectoryCache;
+    struct DirectoryCacheEntry
+    {
+        DirectoryCacheEntry(bool isManaged, const QString &topLevel)
+            : isManaged(isManaged),
+              topLevel(topLevel)
+        {}
+
+        bool    isManaged;
+        QString topLevel;
+    };
+
+    typedef QHash<QString, DirectoryCacheEntry> ManagedDirectoryCache;
 
     Core::IEditor *showOutputInEditor(const QString& title, const QString output,
                                       int editorType, const QString &source,
@@ -193,7 +204,8 @@ private:
     bool isCommitEditorOpen() const;
     QSharedPointer<Utils::TempFileSaver> createTemporaryArgumentFile(const QStringList &extraArgs,
                                                                      QString *errorString) const;
-    void getTopLevel();
+    void getTopLevel(const QString &workingDirectory = QString(), bool isSync = false);
+    void setNewTopLevel(const QString &newTopLevel);
     QString pendingChangesData();
 
     void updateCheckout(const QString &workingDir = QString(),
diff --git a/src/plugins/perforce/perforceversioncontrol.cpp b/src/plugins/perforce/perforceversioncontrol.cpp
index 19303a6..ad4f35b 100644
--- a/src/plugins/perforce/perforceversioncontrol.cpp
+++ b/src/plugins/perforce/perforceversioncontrol.cpp
@@ -178,7 +178,7 @@ bool PerforceVersionControl::managesDirectory(const QString &directory, QString
         QDebug nsp = qDebug().nospace();
         nsp << "managesDirectory" << directory << rc;
         if (topLevel)
-            nsp << topLevel;
+            nsp << *topLevel;
     }
     return rc;
 }
-- 
1.8.3.msysgit.0

Tags: programming qtcreator perforce

Алтай, г.Белуха Забавный баг

comments powered by Disqus