From fdb6ef90fc7a695e2b8d37abdfcf48fe0d8afb03 Mon Sep 17 00:00:00 2001
From: Orgad Shaneh <orgad.shaneh@audiocodes.com>
Date: Wed, 2 Oct 2013 00:18:39 +0300
Subject: [PATCH] VCS: Do not try to add files that are already tracked

Task-number: QTCREATORBUG-2455
Change-Id: I4b1fa9676ad4a8d7293e035e4d6103251bef8aee
Reviewed-by: Nikita Baryshnikov <nib952051@gmail.com>
Reviewed-by: Cristian Tibirna <tibirna@kde.org>
Reviewed-by: Tobias Hunger <tobias.hunger@digia.com>
---
 src/plugins/bazaar/bazaarclient.cpp             | 10 ++++++++++
 src/plugins/bazaar/bazaarclient.h               |  1 +
 src/plugins/bazaar/bazaarcontrol.cpp            |  5 +++++
 src/plugins/bazaar/bazaarcontrol.h              |  1 +
 src/plugins/clearcase/clearcasecontrol.cpp      |  5 +++++
 src/plugins/clearcase/clearcasecontrol.h        |  1 +
 src/plugins/clearcase/clearcaseplugin.cpp       |  7 +++++++
 src/plugins/clearcase/clearcaseplugin.h         |  1 +
 src/plugins/coreplugin/iversioncontrol.h        |  8 ++++++++
 src/plugins/coreplugin/vcsmanager.cpp           | 13 +++++++++++--
 src/plugins/cvs/cvscontrol.cpp                  |  5 +++++
 src/plugins/cvs/cvscontrol.h                    |  1 +
 src/plugins/cvs/cvsplugin.cpp                   | 13 ++++++++++++-
 src/plugins/cvs/cvsplugin.h                     |  4 +++-
 src/plugins/git/gitclient.cpp                   |  9 +++++++++
 src/plugins/git/gitclient.h                     |  1 +
 src/plugins/git/gitversioncontrol.cpp           |  5 +++++
 src/plugins/git/gitversioncontrol.h             |  1 +
 src/plugins/mercurial/mercurialclient.cpp       |  9 +++++++++
 src/plugins/mercurial/mercurialclient.h         |  1 +
 src/plugins/mercurial/mercurialcontrol.cpp      |  5 +++++
 src/plugins/mercurial/mercurialcontrol.h        |  1 +
 src/plugins/perforce/perforceplugin.cpp         |  8 ++++++++
 src/plugins/perforce/perforceplugin.h           |  1 +
 src/plugins/perforce/perforceversioncontrol.cpp |  5 +++++
 src/plugins/perforce/perforceversioncontrol.h   |  1 +
 src/plugins/subversion/subversioncontrol.cpp    |  5 +++++
 src/plugins/subversion/subversioncontrol.h      |  1 +
 src/plugins/subversion/subversionplugin.cpp     | 13 +++++++++++--
 src/plugins/subversion/subversionplugin.h       |  5 +++--
 src/plugins/vcsbase/vcsbaseclient.cpp           |  2 +-
 src/plugins/vcsbase/vcsbaseclient.h             |  2 +-
 32 files changed, 140 insertions(+), 10 deletions(-)

diff --git a/src/plugins/bazaar/bazaarclient.cpp b/src/plugins/bazaar/bazaarclient.cpp
index 431ff8c6a50..5df1804457b 100644
--- a/src/plugins/bazaar/bazaarclient.cpp
+++ b/src/plugins/bazaar/bazaarclient.cpp
@@ -128,6 +128,16 @@ QString BazaarClient::findTopLevelForFile(const QFileInfo &file) const
                                                                    repositoryCheckFile);
 }
 
+bool BazaarClient::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    QStringList args(QLatin1String("status"));
+    args << fileName;
+    QByteArray stdOut;
+    if (!vcsFullySynchronousExec(workingDirectory, args, &stdOut))
+        return false;
+    return !stdOut.startsWith("unknown");
+}
+
 void BazaarClient::view(const QString &source, const QString &id, const QStringList &extraOptions)
 {
     QStringList args(QLatin1String("log"));
diff --git a/src/plugins/bazaar/bazaarclient.h b/src/plugins/bazaar/bazaarclient.h
index d0b1f4a216f..3dc756ef586 100644
--- a/src/plugins/bazaar/bazaarclient.h
+++ b/src/plugins/bazaar/bazaarclient.h
@@ -57,6 +57,7 @@ public:
     void view(const QString &source, const QString &id,
               const QStringList &extraOptions = QStringList());
     QString findTopLevelForFile(const QFileInfo &file) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
 protected:
     Core::Id vcsEditorKind(VcsCommand cmd) const;
diff --git a/src/plugins/bazaar/bazaarcontrol.cpp b/src/plugins/bazaar/bazaarcontrol.cpp
index bf480761a0a..5d27b46f63f 100644
--- a/src/plugins/bazaar/bazaarcontrol.cpp
+++ b/src/plugins/bazaar/bazaarcontrol.cpp
@@ -61,6 +61,11 @@ bool BazaarControl::managesDirectory(const QString &directory, QString *topLevel
     return !topLevelFound.isEmpty();
 }
 
+bool BazaarControl::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    return m_bazaarClient->managesFile(workingDirectory, fileName);
+}
+
 bool BazaarControl::isConfigured() const
 {
     const QString binary = m_bazaarClient->settings()->binaryPath();
diff --git a/src/plugins/bazaar/bazaarcontrol.h b/src/plugins/bazaar/bazaarcontrol.h
index 673b70a5240..94e57aebdda 100644
--- a/src/plugins/bazaar/bazaarcontrol.h
+++ b/src/plugins/bazaar/bazaarcontrol.h
@@ -53,6 +53,7 @@ public:
     Core::Id id() const;
 
     bool managesDirectory(const QString &filename, QString *topLevel = 0) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
     bool isConfigured() const;
     bool supportsOperation(Operation operation) const;
     bool vcsOpen(const QString &fileName);
diff --git a/src/plugins/clearcase/clearcasecontrol.cpp b/src/plugins/clearcase/clearcasecontrol.cpp
index 79f81242a17..c2f7dd44e02 100644
--- a/src/plugins/clearcase/clearcasecontrol.cpp
+++ b/src/plugins/clearcase/clearcasecontrol.cpp
@@ -134,6 +134,11 @@ bool ClearCaseControl::managesDirectory(const QString &directory, QString *topLe
     return m_plugin->managesDirectory(directory, topLevel);
 }
 
+bool ClearCaseControl::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    return m_plugin->managesFile(workingDirectory, fileName);
+}
+
 bool ClearCaseControl::vcsAnnotate(const QString &file, int line)
 {
     const QFileInfo fi(file);
diff --git a/src/plugins/clearcase/clearcasecontrol.h b/src/plugins/clearcase/clearcasecontrol.h
index f4a0fb5743c..fa980595e26 100644
--- a/src/plugins/clearcase/clearcasecontrol.h
+++ b/src/plugins/clearcase/clearcasecontrol.h
@@ -48,6 +48,7 @@ public:
     Core::Id id() const;
 
     bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
     bool isConfigured() const;
 
diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp
index 3e2c34a505f..4775f2e8764 100644
--- a/src/plugins/clearcase/clearcaseplugin.cpp
+++ b/src/plugins/clearcase/clearcaseplugin.cpp
@@ -1850,6 +1850,13 @@ bool ClearCasePlugin::ccCheckUcm(const QString &viewname, const QString &working
     return QRegExp(QLatin1String("(^|\\n)ucm\\n")).indexIn(catcsData) != -1;
 }
 
+bool ClearCasePlugin::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    QStringList args;
+    args << QLatin1String("ls") << fileName;
+    return runCleartoolSync(workingDirectory, args).contains(QLatin1String("@@"));
+}
+
 ViewData ClearCasePlugin::ccGetView(const QString &workingDir) const
 {
     static QHash<QString, ViewData> viewCache;
diff --git a/src/plugins/clearcase/clearcaseplugin.h b/src/plugins/clearcase/clearcaseplugin.h
index 6d62e2e5e88..929844197af 100644
--- a/src/plugins/clearcase/clearcaseplugin.h
+++ b/src/plugins/clearcase/clearcaseplugin.h
@@ -166,6 +166,7 @@ public:
     void setStatus(const QString &file, FileStatus::Status status, bool update = true);
 
     bool ccCheckUcm(const QString &viewname, const QString &workingDir) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
 public slots:
     void vcsAnnotate(const QString &workingDir, const QString &file,
diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h
index 4ba0277a712..329f687f215 100644
--- a/src/plugins/coreplugin/iversioncontrol.h
+++ b/src/plugins/coreplugin/iversioncontrol.h
@@ -80,6 +80,14 @@ public:
 
     virtual bool managesDirectory(const QString &filename, QString *topLevel = 0) const = 0;
 
+    /*!
+     * Returns whether \a fileName is managed by this version control.
+     *
+     * \a workingDirectory is assumed to be part of a valid repository (not necessarily its
+     * top level). \a fileName is expected to be relative to workingDirectory.
+     */
+    virtual bool managesFile(const QString &workingDirectory, const QString &fileName) const = 0;
+
     /*!
      * Returns true is the VCS is configured to run.
      */
diff --git a/src/plugins/coreplugin/vcsmanager.cpp b/src/plugins/coreplugin/vcsmanager.cpp
index c2419d8beb9..1345aca4eaa 100644
--- a/src/plugins/coreplugin/vcsmanager.cpp
+++ b/src/plugins/coreplugin/vcsmanager.cpp
@@ -412,11 +412,20 @@ void VcsManager::promptToAdd(const QString &directory, const QStringList &fileNa
     if (!vc || !vc->supportsOperation(Core::IVersionControl::AddOperation))
         return;
 
+    QStringList unmanagedFiles;
+    QDir dir(directory);
+    foreach (const QString &fileName, fileNames) {
+        if (!vc->managesFile(directory, dir.relativeFilePath(fileName)))
+            unmanagedFiles << fileName;
+    }
+    if (unmanagedFiles.isEmpty())
+        return;
+
     Internal::AddToVcsDialog dlg(Core::ICore::mainWindow(), VcsManager::msgAddToVcsTitle(),
-                                 fileNames, vc->displayName());
+                                 unmanagedFiles, vc->displayName());
     if (dlg.exec() == QDialog::Accepted) {
         QStringList notAddedToVc;
-        foreach (const QString &file, fileNames) {
+        foreach (const QString &file, unmanagedFiles) {
             if (!vc->vcsAdd(file))
                 notAddedToVc << file;
         }
diff --git a/src/plugins/cvs/cvscontrol.cpp b/src/plugins/cvs/cvscontrol.cpp
index 87fdc2cf242..af579f3e99c 100644
--- a/src/plugins/cvs/cvscontrol.cpp
+++ b/src/plugins/cvs/cvscontrol.cpp
@@ -163,6 +163,11 @@ bool CvsControl::managesDirectory(const QString &directory, QString *topLevel) c
     return m_plugin->managesDirectory(directory, topLevel);
 }
 
+bool CvsControl::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    return m_plugin->managesFile(workingDirectory, fileName);
+}
+
 void CvsControl::emitRepositoryChanged(const QString &s)
 {
     emit repositoryChanged(s);
diff --git a/src/plugins/cvs/cvscontrol.h b/src/plugins/cvs/cvscontrol.h
index 586296fa4df..4a765557a30 100644
--- a/src/plugins/cvs/cvscontrol.h
+++ b/src/plugins/cvs/cvscontrol.h
@@ -48,6 +48,7 @@ public:
     Core::Id id() const;
 
     bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
     bool isConfigured() const;
     bool supportsOperation(Operation operation) const;
diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp
index adbde2d7df2..9d4a8055f74 100644
--- a/src/plugins/cvs/cvsplugin.cpp
+++ b/src/plugins/cvs/cvsplugin.cpp
@@ -1226,7 +1226,7 @@ CvsResponse CvsPlugin::runCvs(const QString &workingDirectory,
                               const QStringList &arguments,
                               int timeOut,
                               unsigned flags,
-                              QTextCodec *outputCodec)
+                              QTextCodec *outputCodec) const
 {
     const QString executable = m_settings.cvsBinaryPath;
     CvsResponse response;
@@ -1365,6 +1365,17 @@ bool CvsPlugin::managesDirectory(const QString &directory, QString *topLevel /*
     return manages;
 }
 
+bool CvsPlugin::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    QStringList args;
+    args << QLatin1String("status") << fileName;
+    const CvsResponse response =
+            runCvs(workingDirectory, args, m_settings.timeOutMS(), SshPasswordPrompt);
+    if (response.result != CvsResponse::Ok)
+        return false;
+    return !response.stdOut.contains(QLatin1String("Status: Unknown"));
+}
+
 bool CvsPlugin::checkCVSDirectory(const QDir &directory) const
 {
     const QString cvsDir = directory.absoluteFilePath(QLatin1String("CVS"));
diff --git a/src/plugins/cvs/cvsplugin.h b/src/plugins/cvs/cvsplugin.h
index ba5aad89b8f..6d8f057b83b 100644
--- a/src/plugins/cvs/cvsplugin.h
+++ b/src/plugins/cvs/cvsplugin.h
@@ -90,6 +90,7 @@ public:
     bool vcsAdd(const QString &workingDir, const QString &fileName);
     bool vcsDelete(const QString &workingDir, const QString &fileName);
     bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
     // cvs 'edit' is used to implement 'open' (cvsnt).
     bool edit(const QString &topLevel, const QStringList &files);
 
@@ -143,7 +144,8 @@ private:
     CvsResponse runCvs(const QString &workingDirectory,
                        const QStringList &arguments,
                        int timeOut,
-                       unsigned flags, QTextCodec *outputCodec = 0);
+                       unsigned flags,
+                       QTextCodec *outputCodec = 0) const;
 
     void annotate(const QString &workingDir, const QString &file,
                   const QString &revision = QString(), int lineNumber= -1);
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 413f663e90d..eab02e8c054 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -972,6 +972,15 @@ QString GitClient::findGitDirForRepository(const QString &repositoryDir) const
     return res;
 }
 
+bool GitClient::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    QByteArray output;
+    QStringList arguments;
+    arguments << QLatin1String("ls-files") << QLatin1String("--error-unmatch") << fileName;
+    return fullySynchronousGit(workingDirectory, arguments, &output, 0,
+                               VcsBasePlugin::SuppressCommandLogging);
+}
+
 VcsBase::VcsBaseEditorWidget *GitClient::findExistingVCSEditor(const char *registerDynamicProperty,
                                                                const QString &dynamicPropertyValue) const
 {
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index 7517f2629a5..f21a1486728 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -137,6 +137,7 @@ public:
 
     QString findRepositoryForDirectory(const QString &dir);
     QString findGitDirForRepository(const QString &repositoryDir) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
     void diff(const QString &workingDirectory, const QString &fileName);
     void diff(const QString &workingDirectory,
diff --git a/src/plugins/git/gitversioncontrol.cpp b/src/plugins/git/gitversioncontrol.cpp
index b98e9ea3501..ae5154e81ef 100644
--- a/src/plugins/git/gitversioncontrol.cpp
+++ b/src/plugins/git/gitversioncontrol.cpp
@@ -229,6 +229,11 @@ bool GitVersionControl::managesDirectory(const QString &directory, QString *topL
     return !topLevelFound.isEmpty();
 }
 
+bool GitVersionControl::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    return m_client->managesFile(workingDirectory, fileName);
+}
+
 bool GitVersionControl::vcsAnnotate(const QString &file, int line)
 {
     const QFileInfo fi(file);
diff --git a/src/plugins/git/gitversioncontrol.h b/src/plugins/git/gitversioncontrol.h
index 6baa1520771..71c53be43cb 100644
--- a/src/plugins/git/gitversioncontrol.h
+++ b/src/plugins/git/gitversioncontrol.h
@@ -48,6 +48,7 @@ public:
     Core::Id id() const;
 
     bool managesDirectory(const QString &directory, QString *topLevel) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
     bool isConfigured() const;
     bool supportsOperation(Operation operation) const;
diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp
index e4966d26271..f1969682f51 100644
--- a/src/plugins/mercurial/mercurialclient.cpp
+++ b/src/plugins/mercurial/mercurialclient.cpp
@@ -257,6 +257,15 @@ QString MercurialClient::vcsGetRepositoryURL(const QString &directory)
     return QString();
 }
 
+bool MercurialClient::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    QStringList args;
+    args << QLatin1String("status") << QLatin1String("--unknown") << fileName;
+    QByteArray output;
+    vcsFullySynchronousExec(workingDirectory, args, &output);
+    return output.isEmpty();
+}
+
 void MercurialClient::incoming(const QString &repositoryRoot, const QString &repository)
 {
     QStringList args;
diff --git a/src/plugins/mercurial/mercurialclient.h b/src/plugins/mercurial/mercurialclient.h
index 7f12b8470f3..845d2db4786 100644
--- a/src/plugins/mercurial/mercurialclient.h
+++ b/src/plugins/mercurial/mercurialclient.h
@@ -63,6 +63,7 @@ public:
     void incoming(const QString &repositoryRoot, const QString &repository = QString());
     void outgoing(const QString &repositoryRoot);
     QString vcsGetRepositoryURL(const QString &directory);
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
     void annotate(const QString &workingDir, const QString &file,
                   const QString revision = QString(), int lineNumber = -1,
diff --git a/src/plugins/mercurial/mercurialcontrol.cpp b/src/plugins/mercurial/mercurialcontrol.cpp
index 8eaa73dce19..c8fa812e2dd 100644
--- a/src/plugins/mercurial/mercurialcontrol.cpp
+++ b/src/plugins/mercurial/mercurialcontrol.cpp
@@ -64,6 +64,11 @@ bool MercurialControl::managesDirectory(const QString &directory, QString *topLe
     return !topLevelFound.isEmpty();
 }
 
+bool MercurialControl::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    return mercurialClient->managesFile(workingDirectory, fileName);
+}
+
 bool MercurialControl::isConfigured() const
 {
     const QString binary = mercurialClient->settings()->binaryPath();
diff --git a/src/plugins/mercurial/mercurialcontrol.h b/src/plugins/mercurial/mercurialcontrol.h
index 3fac104c044..4a5c2e872c4 100644
--- a/src/plugins/mercurial/mercurialcontrol.h
+++ b/src/plugins/mercurial/mercurialcontrol.h
@@ -53,6 +53,7 @@ public:
     QString displayName() const;
     Core::Id id() const;
     bool managesDirectory(const QString &filename, QString *topLevel = 0) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
     bool isConfigured() const;
     bool supportsOperation(Operation operation) const;
     bool vcsOpen(const QString &fileName);
diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp
index ec8e13e5eea..69dcc62fbcb 100644
--- a/src/plugins/perforce/perforceplugin.cpp
+++ b/src/plugins/perforce/perforceplugin.cpp
@@ -828,6 +828,14 @@ bool PerforcePlugin::managesDirectory(const QString &directory, QString *topLeve
     return rc;
 }
 
+bool PerforcePlugin::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    QStringList args;
+    args << QLatin1String("fstat") << QLatin1String("-m1") << fileName;
+    const PerforceResponse result = runP4Cmd(workingDirectory, args, RunFullySynchronous);
+    return result.stdOut.contains(QLatin1String("depotFile"));
+}
+
 bool PerforcePlugin::managesDirectoryFstat(const QString &directory)
 {
     if (!m_settings.isValid())
diff --git a/src/plugins/perforce/perforceplugin.h b/src/plugins/perforce/perforceplugin.h
index 71e0a6e39d7..b03fab15c9d 100644
--- a/src/plugins/perforce/perforceplugin.h
+++ b/src/plugins/perforce/perforceplugin.h
@@ -86,6 +86,7 @@ public:
     void extensionsInitialized();
 
     bool managesDirectory(const QString &directory, QString *topLevel = 0);
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
     bool vcsOpen(const QString &workingDir, const QString &fileName);
     bool vcsAdd(const QString &workingDir, const QString &fileName);
     bool vcsDelete(const QString &workingDir, const QString &filename);
diff --git a/src/plugins/perforce/perforceversioncontrol.cpp b/src/plugins/perforce/perforceversioncontrol.cpp
index a6477335916..37c78efce9d 100644
--- a/src/plugins/perforce/perforceversioncontrol.cpp
+++ b/src/plugins/perforce/perforceversioncontrol.cpp
@@ -184,6 +184,11 @@ bool PerforceVersionControl::managesDirectory(const QString &directory, QString
     return rc;
 }
 
+bool PerforceVersionControl::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    return m_plugin->managesFile(workingDirectory, fileName);
+}
+
 void PerforceVersionControl::emitRepositoryChanged(const QString &s)
 {
     emit repositoryChanged(s);
diff --git a/src/plugins/perforce/perforceversioncontrol.h b/src/plugins/perforce/perforceversioncontrol.h
index 888ce898501..844791429ed 100644
--- a/src/plugins/perforce/perforceversioncontrol.h
+++ b/src/plugins/perforce/perforceversioncontrol.h
@@ -47,6 +47,7 @@ public:
     Core::Id id() const;
 
     bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
     bool isConfigured() const;
     bool supportsOperation(Operation operation) const;
diff --git a/src/plugins/subversion/subversioncontrol.cpp b/src/plugins/subversion/subversioncontrol.cpp
index 3b11ef8acac..200105bb3ba 100644
--- a/src/plugins/subversion/subversioncontrol.cpp
+++ b/src/plugins/subversion/subversioncontrol.cpp
@@ -146,6 +146,11 @@ bool SubversionControl::managesDirectory(const QString &directory, QString *topL
     return m_plugin->managesDirectory(directory, topLevel);
 }
 
+bool SubversionControl::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    return m_plugin->managesFile(workingDirectory, fileName);
+}
+
 bool SubversionControl::vcsAnnotate(const QString &file, int line)
 {
     const QFileInfo fi(file);
diff --git a/src/plugins/subversion/subversioncontrol.h b/src/plugins/subversion/subversioncontrol.h
index d7b3a5ca444..ce7c54dfb28 100644
--- a/src/plugins/subversion/subversioncontrol.h
+++ b/src/plugins/subversion/subversioncontrol.h
@@ -47,6 +47,7 @@ public:
     Core::Id id() const;
 
     bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
 
     bool isConfigured() const;
     bool supportsOperation(Operation operation) const;
diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp
index 946e8e5026c..de57be400e1 100644
--- a/src/plugins/subversion/subversionplugin.cpp
+++ b/src/plugins/subversion/subversionplugin.cpp
@@ -1083,7 +1083,7 @@ SubversionResponse
                                  const QStringList &arguments,
                                  int timeOut,
                                  unsigned flags,
-                                 QTextCodec *outputCodec)
+                                 QTextCodec *outputCodec) const
 {
     const bool hasAuth = m_settings.hasAuthentication();
     return runSvn(workingDir,
@@ -1142,7 +1142,7 @@ SubversionPlugin::Version SubversionPlugin::svnVersion()
 SubversionResponse SubversionPlugin::runSvn(const QString &workingDir,
                           const QString &userName, const QString &password,
                           const QStringList &arguments, int timeOut,
-                          unsigned flags, QTextCodec *outputCodec)
+                          unsigned flags, QTextCodec *outputCodec) const
 {
     const QString executable = m_settings.binaryPath();
     SubversionResponse response;
@@ -1354,6 +1354,15 @@ bool SubversionPlugin::managesDirectory(const QString &directory, QString *topLe
     return true;
 }
 
+bool SubversionPlugin::managesFile(const QString &workingDirectory, const QString &fileName) const
+{
+    QStringList args;
+    args << QLatin1String("status") << fileName;
+    SubversionResponse response =
+            runSvn(workingDirectory, args, m_settings.timeOutMs(), 0);
+    return response.stdOut.isEmpty() || response.stdOut.at(0) != QLatin1Char('?');
+}
+
 // Check whether SVN management subdirs exist.
 bool SubversionPlugin::checkSVNSubDir(const QDir &directory, const QString &fileName) const
 {
diff --git a/src/plugins/subversion/subversionplugin.h b/src/plugins/subversion/subversionplugin.h
index 81a89b668ea..82940f1ae6d 100644
--- a/src/plugins/subversion/subversionplugin.h
+++ b/src/plugins/subversion/subversionplugin.h
@@ -96,6 +96,7 @@ public:
     bool vcsDelete(const QString &workingDir, const QString &fileName);
     bool vcsMove(const QString &workingDir, const QString &from, const QString &to);
     bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
+    bool managesFile(const QString &workingDirectory, const QString &fileName) const;
     bool vcsCheckout(const QString &directory, const QByteArray &url);
     QString vcsGetRepositoryURL(const QString &directory);
 
@@ -161,12 +162,12 @@ private:
     // Run using the settings' authentication options.
     SubversionResponse runSvn(const QString &workingDir,
                               const QStringList &arguments, int timeOut,
-                              unsigned flags, QTextCodec *outputCodec = 0);
+                              unsigned flags, QTextCodec *outputCodec = 0) const;
     // Run using custom authentication options.
     SubversionResponse runSvn(const QString &workingDir,
                               const QString &userName, const QString &password,
                               const QStringList &arguments, int timeOut,
-                              unsigned flags, QTextCodec *outputCodec = 0);
+                              unsigned flags, QTextCodec *outputCodec = 0) const;
 
     void filelog(const QString &workingDir,
                  const QString &file = QString(),
diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp
index ca8a7dd3ffd..442313d87e6 100644
--- a/src/plugins/vcsbase/vcsbaseclient.cpp
+++ b/src/plugins/vcsbase/vcsbaseclient.cpp
@@ -276,7 +276,7 @@ bool VcsBaseClient::synchronousPush(const QString &workingDir,
 
 bool VcsBaseClient::vcsFullySynchronousExec(const QString &workingDir,
                                             const QStringList &args,
-                                            QByteArray *output)
+                                            QByteArray *output) const
 {
     QProcess vcsProcess;
     if (!workingDir.isEmpty())
diff --git a/src/plugins/vcsbase/vcsbaseclient.h b/src/plugins/vcsbase/vcsbaseclient.h
index 2d3ff336a03..9734e4829cc 100644
--- a/src/plugins/vcsbase/vcsbaseclient.h
+++ b/src/plugins/vcsbase/vcsbaseclient.h
@@ -166,7 +166,7 @@ protected:
     // Fully synchronous VCS execution (QProcess-based)
     bool vcsFullySynchronousExec(const QString &workingDir,
                                  const QStringList &args,
-                                 QByteArray *output);
+                                 QByteArray *output) const;
     // Synchronous VCS execution using Utils::SynchronousProcess, with
     // log windows updating (using VcsBasePlugin::runVcs with flags)
     Utils::SynchronousProcessResponse vcsSynchronousExec(const QString &workingDir,
-- 
GitLab