diff --git a/src/plugins/coreplugin/iversioncontrol.cpp b/src/plugins/coreplugin/iversioncontrol.cpp index a798a782912367770f41e6836e9a740946151a6c..5839c1375d6b399fd9882d67bcad070b6470dd0c 100644 --- a/src/plugins/coreplugin/iversioncontrol.cpp +++ b/src/plugins/coreplugin/iversioncontrol.cpp @@ -28,7 +28,29 @@ ****************************************************************************/ #include "iversioncontrol.h" +#include "vcsmanager.h" + +#include <utils/qtcassert.h> + +#include <QFileInfo> +/*! + * \class Core::IVersionControl::TopicCache + * \brief The TopicCache class stores a {directory -> topic} cache + * + * In order to support topic, an IVersionControl subclass needs to create + * an instance of TopicCache subclass with appropriate overrides for its + * pure virtual functions, and pass this instance to IVersionControl's constructor. + */ + +/*! + * \fn Core::IVersionControl::TopicCache::trackFile(const QString &repository) + * Returns path to file that invalidates the cache when modified, for \a repository. + * e.g. for git this file is .git/HEAD + * + * \fn Core::IVersionControl::TopicCache::refreshTopic(const QString &repository) + * Returns current topic for \a repository. + */ namespace Core { QString IVersionControl::vcsOpenText() const @@ -41,9 +63,14 @@ QString IVersionControl::vcsMakeWritableText() const return QString(); } -QString IVersionControl::vcsTopic(const QString &) +QString IVersionControl::vcsTopic(const QString &topLevel) { - return QString(); + return m_topicCache ? m_topicCache->topic(topLevel) : QString(); +} + +IVersionControl::~IVersionControl() +{ + delete m_topicCache; } IVersionControl::OpenSupportMode IVersionControl::openSupportMode(const QString &fileName) const @@ -52,6 +79,30 @@ IVersionControl::OpenSupportMode IVersionControl::openSupportMode(const QString return NoOpen; } +IVersionControl::TopicCache::~TopicCache() +{ +} + +/*! + * Returns topic for repository under \a topLevel. + * + * If the cache for \a topLevel is valid, it will be used. Otherwise it will be refreshed. + */ +QString IVersionControl::TopicCache::topic(const QString &topLevel) +{ + QTC_ASSERT(!topLevel.isEmpty(), return QString()); + TopicData &data = m_cache[topLevel]; + QString file = trackFile(topLevel); + + if (file.isEmpty()) + return QString(); + const QDateTime lastModified = QFileInfo(file).lastModified(); + if (lastModified == data.timeStamp) + return data.topic; + data.timeStamp = lastModified; + return data.topic = refreshTopic(topLevel); +} + } // namespace Core #if defined(WITH_TESTS) diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h index 57a93a74fc0be1e75e58668856c26465d73f6c51..cbde6138b2afbbf6581a9f532cae6f75aa435934 100644 --- a/src/plugins/coreplugin/iversioncontrol.h +++ b/src/plugins/coreplugin/iversioncontrol.h @@ -33,9 +33,10 @@ #include "core_global.h" #include "id.h" +#include <QDateTime> +#include <QFlags> #include <QObject> #include <QString> -#include <QFlags> namespace Core { @@ -64,8 +65,29 @@ public: OpenMandatory /*!< Files must always be opened by the VCS */ }; - IVersionControl() {} - virtual ~IVersionControl() {} + class TopicCache + { + public: + virtual ~TopicCache(); + QString topic(const QString &topLevel); + + protected: + virtual QString trackFile(const QString &repository) = 0; + virtual QString refreshTopic(const QString &repository) = 0; + + private: + struct TopicData + { + QDateTime timeStamp; + QString topic; + }; + + QHash<QString, TopicData> m_cache; + + }; + + explicit IVersionControl(TopicCache *topicCache = 0) : m_topicCache(topicCache) {} + virtual ~IVersionControl(); virtual QString displayName() const = 0; virtual Id id() const = 0; @@ -158,7 +180,7 @@ public: /*! * Topic (e.g. name of the current branch) */ - virtual QString vcsTopic(const QString &directory); + virtual QString vcsTopic(const QString &topLevel); /*! * Display annotation for a file and scroll to line @@ -179,6 +201,9 @@ signals: void repositoryChanged(const QString &repository); void filesChanged(const QStringList &files); void configurationChanged(); + +private: + TopicCache *m_topicCache; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Core::IVersionControl::SettingsFlags) diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index fbe39458f6b18b4074c8c5ca928d5163475cc819..22c094c1711138861f36ba55bfa1b7872a2d551a 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -2024,28 +2024,13 @@ bool GitClient::synchronousHeadRefs(const QString &workingDirectory, QStringList return true; } -struct TopicData -{ - QDateTime timeStamp; - QString topic; -}; - // Retrieve topic (branch, tag or HEAD hash) QString GitClient::synchronousTopic(const QString &workingDirectory) { - static QHash<QString, TopicData> topicCache; - QString gitDir = findGitDirForRepository(workingDirectory); - if (gitDir.isEmpty()) - return QString(); - TopicData &data = topicCache[gitDir]; - QDateTime lastModified = QFileInfo(gitDir + QLatin1String("/HEAD")).lastModified(); - if (lastModified == data.timeStamp) - return data.topic; - data.timeStamp = lastModified; // First try to find branch QString branch = synchronousCurrentLocalBranch(workingDirectory); if (!branch.isEmpty()) - return data.topic = branch; + return branch; // Detached HEAD, try a tag or remote branch QStringList references; @@ -2059,10 +2044,8 @@ QString GitClient::synchronousTopic(const QString &workingDirectory) foreach (const QString &ref, references) { int derefInd = ref.indexOf(dereference); - if (ref.startsWith(tagStart)) { - return data.topic = ref.mid(tagStart.size(), - (derefInd == -1) ? -1 : derefInd - tagStart.size()); - } + if (ref.startsWith(tagStart)) + return ref.mid(tagStart.size(), (derefInd == -1) ? -1 : derefInd - tagStart.size()); if (ref.startsWith(remoteStart)) { remoteBranch = ref.mid(remoteStart.size(), (derefInd == -1) ? -1 : derefInd - remoteStart.size()); @@ -2070,7 +2053,7 @@ QString GitClient::synchronousTopic(const QString &workingDirectory) } // No tag - return data.topic = remoteBranch.isEmpty() ? tr("Detached HEAD") : remoteBranch; + return remoteBranch.isEmpty() ? tr("Detached HEAD") : remoteBranch; } bool GitClient::synchronousRevParseCmd(const QString &workingDirectory, const QString &ref, diff --git a/src/plugins/git/gitversioncontrol.cpp b/src/plugins/git/gitversioncontrol.cpp index dbb302d3d8bf3c3e4c7457e8409ec4ca9947adf3..c45e1dada28ea30211a7b98e17ac9a61d999861d 100644 --- a/src/plugins/git/gitversioncontrol.cpp +++ b/src/plugins/git/gitversioncontrol.cpp @@ -38,7 +38,32 @@ namespace Git { namespace Internal { +class GitTopicCache : public Core::IVersionControl::TopicCache +{ +public: + GitTopicCache(GitClient *client) : + m_client(client) + { + } + +protected: + QString trackFile(const QString &repository) + { + const QString gitDir = m_client->findGitDirForRepository(repository); + return gitDir.isEmpty() ? QString() : (gitDir + QLatin1String("/HEAD")); + } + + QString refreshTopic(const QString &repository) + { + return m_client->synchronousTopic(repository); + } + +private: + GitClient *m_client; +}; + GitVersionControl::GitVersionControl(GitClient *client) : + Core::IVersionControl(new GitTopicCache(client)), m_client(client) { } @@ -120,7 +145,7 @@ QString GitVersionControl::vcsGetRepositoryURL(const QString &directory) QString GitVersionControl::vcsTopic(const QString &directory) { - QString topic = m_client->synchronousTopic(directory); + QString topic = Core::IVersionControl::vcsTopic(directory); const QString commandInProgress = m_client->commandInProgressDescription(directory); if (!commandInProgress.isEmpty()) topic += QLatin1String(" (") + commandInProgress + QLatin1Char(')');