From 9b76c068f97b200b814157f2aca2d880f7ad86f3 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Tue, 2 Feb 2010 12:27:05 +0100 Subject: [PATCH] VCS[git]: Implement add using '--intent-to-add' depending on version. Implement IVersionControl::vcsAdd() using --intent-to-add with a cached version check. On this occasion, implement vcsDelete() as well using 'git rm -f'. --- src/plugins/git/gitclient.cpp | 85 +++++++++++++++++++++++++-- src/plugins/git/gitclient.h | 15 ++++- src/plugins/git/gitutils.h | 6 ++ src/plugins/git/gitversioncontrol.cpp | 28 +++++++-- 4 files changed, 124 insertions(+), 10 deletions(-) diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index b98462562ab..f2020b3a5ff 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -131,7 +131,8 @@ GitClient::GitClient(GitPlugin* plugin) : m_msgWait(tr("Waiting for data...")), m_plugin(plugin), m_core(Core::ICore::instance()), - m_repositoryChangedSignalMapper(0) + m_repositoryChangedSignalMapper(0), + m_cachedGitVersion(0) { if (QSettings *s = m_core->settings()) { m_settings.fromSettings(s); @@ -463,14 +464,20 @@ void GitClient::addFile(const QString &workingDirectory, const QString &fileName executeGit(workingDirectory, arguments, 0, true); } -bool GitClient::synchronousAdd(const QString &workingDirectory, const QStringList &files) +// Warning: 'intendToAdd' works only from 1.6.1 onwards +bool GitClient::synchronousAdd(const QString &workingDirectory, + bool intendToAdd, + const QStringList &files) { if (Git::Constants::debug) qDebug() << Q_FUNC_INFO << workingDirectory << files; QByteArray outputText; QByteArray errorText; QStringList arguments; - arguments << QLatin1String("add") << files; + arguments << QLatin1String("add"); + if (intendToAdd) + arguments << QLatin1String("--intent-to-add"); + arguments.append(files); const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText); if (!rc) { const QString errorMessage = tr("Unable to add %n file(s) to %1: %2", 0, files.size()). @@ -480,6 +487,28 @@ bool GitClient::synchronousAdd(const QString &workingDirectory, const QStringLis return rc; } +bool GitClient::synchronousDelete(const QString &workingDirectory, + bool force, + const QStringList &files) +{ + if (Git::Constants::debug) + qDebug() << Q_FUNC_INFO << workingDirectory << files; + QByteArray outputText; + QByteArray errorText; + QStringList arguments; + arguments << QLatin1String("rm"); + if (force) + arguments << QLatin1String("--force"); + arguments.append(files); + const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText); + if (!rc) { + const QString errorMessage = tr("Unable to remove %n file(s) from %1: %2", 0, files.size()). + arg(workingDirectory, commandOutputFromLocal8Bit(errorText)); + VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage); + } + return rc; +} + bool GitClient::synchronousReset(const QString &workingDirectory, const QStringList &files, QString *errorMessage) @@ -1233,7 +1262,7 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory, // for deletion QStringList addFiles = checkedFiles.toSet().subtract(origDeletedFiles.toSet()).toList(); if (!addFiles.isEmpty()) - if (!synchronousAdd(repositoryDirectory, addFiles)) + if (!synchronousAdd(repositoryDirectory, false, addFiles)) return false; // Do the final commit @@ -1526,6 +1555,7 @@ void GitClient::setSettings(const GitSettings &s) if (QSettings *s = m_core->settings()) m_settings.toSettings(s); m_binaryPath = m_settings.gitBinaryPath(); + m_cachedGitVersion = 0u; } } @@ -1542,5 +1572,52 @@ void GitClient::connectRepositoryChanged(const QString & repository, GitCommand Qt::QueuedConnection); } +// determine version as '(major << 16) + (minor << 8) + patch' or 0. +unsigned GitClient::gitVersion(QString *errorMessage /* = 0 */) +{ + if (!m_cachedGitVersion) + m_cachedGitVersion = synchronousGitVersion(errorMessage); + return m_cachedGitVersion; } + +QString GitClient::gitVersionString(QString *errorMessage) +{ + if (const unsigned version = gitVersion(errorMessage)) { + QString rc; + QTextStream(&rc) << (version >> 16) << '.' + << (0xFF & (version >> 8)) << '.' + << (version & 0xFF); + return rc; + } + return QString(); +} + +// determine version as '(major << 16) + (minor << 8) + patch' or 0. +unsigned GitClient::synchronousGitVersion(QString *errorMessage /* = 0 */) +{ + // run git --version + QByteArray outputText; + QByteArray errorText; + const bool rc = synchronousGit(QString(), QStringList("--version"), &outputText, &errorText); + if (!rc) { + const QString msg = tr("Unable to determine git version: %1").arg(commandOutputFromLocal8Bit(errorText)); + if (errorMessage) { + *errorMessage = msg; + } else { + VCSBase::VCSBaseOutputWindow::instance()->appendError(msg); + } + return 0; + } + // cut 'git version 1.6.5.1.sha' + const QString output = commandOutputFromLocal8Bit(outputText); + const QRegExp versionPattern(QLatin1String("^[^\\d]+([\\d])\\.([\\d])\\.([\\d]).*$")); + QTC_ASSERT(versionPattern.isValid(), return 0); + QTC_ASSERT(versionPattern.exactMatch(output), return 0); + const unsigned major = versionPattern.cap(1).toUInt(); + const unsigned minor = versionPattern.cap(2).toUInt(); + const unsigned patch = versionPattern.cap(3).toUInt(); + return version(major, minor, patch); } + +} // namespace Internal +} // namespace Git diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 55f6800d404..6b6ca1a655c 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -95,7 +95,13 @@ public: void checkoutBranch(const QString &workingDirectory, const QString &branch); void hardReset(const QString &workingDirectory, const QString &commit = QString()); void addFile(const QString &workingDirectory, const QString &fileName); - bool synchronousAdd(const QString &workingDirectory, const QStringList &files); + bool synchronousAdd(const QString &workingDirectory, + // Warning: Works only from 1.6.1 onwards + bool intendToAdd, + const QStringList &files); + bool synchronousDelete(const QString &workingDirectory, + bool force, + const QStringList &files); bool synchronousReset(const QString &workingDirectory, const QStringList &files = QStringList(), QString *errorMessage = 0); @@ -139,6 +145,10 @@ public: QStringList *descriptions, QString *errorMessage); bool synchronousTopRevision(const QString &workingDirectory, QString *revision = 0, QString *branch = 0, QString *errorMessage = 0); + // determine version as '(major << 16) + (minor << 8) + patch' or 0 + // with some smart caching. + unsigned gitVersion(QString *errorMessage = 0); + QString gitVersionString(QString *errorMessage = 0); void pull(const QString &workingDirectory); void push(const QString &workingDirectory); @@ -223,6 +233,8 @@ private: QByteArray* outputText = 0, QByteArray* errorText = 0, bool logCommandToWindow = true); + // determine version as '(major << 16) + (minor << 8) + patch' or 0. + unsigned synchronousGitVersion(QString *errorMessage = 0); enum RevertResult { RevertOk, RevertUnchanged, RevertCanceled, RevertFailed }; RevertResult revertI(QStringList files, bool *isDirectory, QString *errorMessage); @@ -234,6 +246,7 @@ private: GitSettings m_settings; QString m_binaryPath; QSignalMapper *m_repositoryChangedSignalMapper; + unsigned m_cachedGitVersion; }; diff --git a/src/plugins/git/gitutils.h b/src/plugins/git/gitutils.h index 16d80ca656c..7dbefc91582 100644 --- a/src/plugins/git/gitutils.h +++ b/src/plugins/git/gitutils.h @@ -54,6 +54,12 @@ QDebug operator<<(QDebug d, const Stash &); // Make QInputDialog play nicely bool inputText(QWidget *parent, const QString &title, const QString &prompt, QString *s); +// Version information following Qt convention +inline unsigned version(unsigned major, unsigned minor, unsigned patch) +{ + return (major << 16) + (minor << 8) + patch; +} + } // namespace Internal } // namespace Git diff --git a/src/plugins/git/gitversioncontrol.cpp b/src/plugins/git/gitversioncontrol.cpp index c98f0688921..49955873ab3 100644 --- a/src/plugins/git/gitversioncontrol.cpp +++ b/src/plugins/git/gitversioncontrol.cpp @@ -32,6 +32,11 @@ #include "gitplugin.h" #include "gitutils.h" +#include <utils/qtcassert.h> + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> + static const char stashMessageKeywordC[] = "IVersionControl@"; static const char stashRevisionIdC[] = "revision"; @@ -54,12 +59,22 @@ QString GitVersionControl::displayName() const return QLatin1String("git"); } +// Add: Implement using "git add --intent-to-add" starting from 1.6.1 +static inline bool addOperationSupported() +{ + return gitClient()->gitVersion() >= version(1, 6, 1); +} + bool GitVersionControl::supportsOperation(Operation operation) const { bool rc = false; switch (operation) { case AddOperation: + rc = addOperationSupported(); + break; case DeleteOperation: + rc = true; + break; case OpenOperation: break; case CreateRepositoryOperation: @@ -75,15 +90,18 @@ bool GitVersionControl::vcsOpen(const QString & /*fileName*/) return false; } -bool GitVersionControl::vcsAdd(const QString & /*fileName*/) +bool GitVersionControl::vcsAdd(const QString & fileName) { - return false; + // Implement in terms of using "--intent-to-add" + QTC_ASSERT(addOperationSupported(), return false); + const QFileInfo fi(fileName); + return gitClient()->synchronousAdd(fi.absolutePath(), true, QStringList(fi.fileName())); } -bool GitVersionControl::vcsDelete(const QString & /*fileName*/) +bool GitVersionControl::vcsDelete(const QString & fileName) { - // TODO: implement using 'git rm'. - return false; + const QFileInfo fi(fileName); + return gitClient()->synchronousDelete(fi.absolutePath(), true, QStringList(fi.fileName())); } bool GitVersionControl::vcsCreateRepository(const QString &directory) -- GitLab