diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 67b456755bb61013b631959f3b982b54d37d4a34..1dd2f0502e12fe72abc78583567cc72378fe598f 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -340,6 +340,37 @@ private: QString m_commit; }; +class RebaseManager : public QObject +{ + Q_OBJECT + +public: + RebaseManager(QObject *parent) : QObject(parent) + { + } + +public slots: + void readStdErr(const QString &error) + { + // rebase conflict is output to stdOut + QRegExp conflictedCommit(QLatin1String("Could not apply ([^\\n]*)")); + conflictedCommit.indexIn(error); + m_commit = conflictedCommit.cap(1); + } + + void finished(bool ok, int exitCode, const QVariant &workingDirectory) + { + Q_UNUSED(ok); + if (exitCode != 0 && !m_commit.isEmpty()) { + GitPlugin::instance()->gitClient()->handleMergeConflicts( + workingDirectory.toString(), m_commit, QLatin1String("rebase")); + } + } + +private: + QString m_commit; +}; + Core::IEditor *locateEditor(const char *property, const QString &entry) { foreach (Core::IEditor *ed, Core::ICore::editorManager()->openedEditors()) @@ -434,7 +465,7 @@ QString GitClient::findRepositoryForDirectory(const QString &dir) return QString(); } -QString GitClient::findGitDirForRepository(const QString &repositoryDir) +QString GitClient::findGitDirForRepository(const QString &repositoryDir) const { static QHash<QString, QString> repoDirCache; QString &res = repoDirCache[repositoryDir]; @@ -2335,6 +2366,19 @@ bool GitClient::synchronousMerge(const QString &workingDirectory, const QString return executeAndHandleConflicts(workingDirectory, arguments, command); } +bool GitClient::canRebase(const QString &workingDirectory) const +{ + const QString gitDir = findGitDirForRepository(workingDirectory); + if (QFileInfo(gitDir + QLatin1String("/rebase-apply")).exists() + || QFileInfo(gitDir + QLatin1String("/rebase-merge")).exists()) { + VcsBase::VcsBaseOutputWindow::instance()->appendError( + tr("Rebase, merge or am is in progress. Please finish " + "or abort it then try again")); + return false; + } + return true; +} + bool GitClient::synchronousRebase(const QString &workingDirectory, const QString &baseBranch, const QString &topicBranch) { @@ -2366,6 +2410,21 @@ bool GitClient::cherryPickCommit(const QString &workingDirectory, const QString return executeAndHandleConflicts(workingDirectory, arguments, command); } +void GitClient::interactiveRebase(const QString &workingDirectory, const QString &commit) +{ + QStringList arguments; + arguments << QLatin1String("rebase") << QLatin1String("-i") << commit; + outputWindow()->appendCommand(workingDirectory, settings()->stringValue(GitSettings::binaryPathKey), arguments); + VcsBase::Command *command = createCommand(workingDirectory, 0, true); + command->addJob(arguments, -1); + command->execute(); + command->setCookie(workingDirectory); + RebaseManager *rebaseManager = new RebaseManager(command); + connect(command, SIGNAL(errorText(QString)), rebaseManager, SLOT(readStdErr(QString))); + connect(command, SIGNAL(finished(bool,int,QVariant)), + rebaseManager, SLOT(finished(bool,int,QVariant))); +} + QString GitClient::msgNoChangedFiles() { return tr("There are no modified files."); diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 63344a8562a0ebc74d4991bbaa6a715ede0a8468..0109f44b1f4182029579d547a0b22ce29327c4a3 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -111,7 +111,7 @@ public: unsigned gitVersion(QString *errorMessage = 0) const; QString findRepositoryForDirectory(const QString &dir); - QString findGitDirForRepository(const QString &repositoryDir); + QString findGitDirForRepository(const QString &repositoryDir) const; void diff(const QString &workingDirectory, const QStringList &diffArgs, const QString &fileName); void diff(const QString &workingDirectory, const QStringList &diffArgs, @@ -199,11 +199,13 @@ public: bool synchronousPull(const QString &workingDirectory, bool rebase); bool synchronousPush(const QString &workingDirectory, const QString &remote = QString()); bool synchronousMerge(const QString &workingDirectory, const QString &branch); + bool canRebase(const QString &workingDirectory) const; bool synchronousRebase(const QString &workingDirectory, const QString &baseBranch, const QString &topicBranch = QString()); bool revertCommit(const QString &workingDirectory, const QString &commit); bool cherryPickCommit(const QString &workingDirectory, const QString &commit); + void interactiveRebase(const QString &workingDirectory, const QString &commit); void synchronousAbortCommand(const QString &workingDir, const QString &abortCommand); // git svn support (asynchronous). diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 59d9d4edd4ee10293ad43d40639f07ee658b17d0..00ce0ca5f241052e63c650edd779870df14a33ba 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -435,6 +435,10 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) tr("Reset..."), Core::Id("Git.Reset"), globalcontext, false, SLOT(resetRepository())); + createRepositoryAction(localRepositoryMenu, + tr("Interactive Rebase..."), Core::Id("Git.Rebase"), + globalcontext, true, SLOT(startRebase())); + createRepositoryAction(localRepositoryMenu, tr("Revert Single Commit..."), Core::Id("Git.Revert"), globalcontext, true, SLOT(startRevertCommit())); @@ -719,6 +723,24 @@ void GitPlugin::resetRepository() } } +void GitPlugin::startRebase() +{ + QString workingDirectory = currentState().currentDirectoryOrTopLevel(); + if (workingDirectory.isEmpty() || !m_gitClient->canRebase(workingDirectory)) + return; + GitClient::StashGuard stashGuard(workingDirectory, QLatin1String("Rebase-i")); + if (stashGuard.stashingFailed(true)) + return; + stashGuard.preventPop(); + LogChangeDialog dialog(false); + dialog.setWindowTitle(tr("Interactive Rebase")); + if (!dialog.runDialog(workingDirectory)) + return; + const QString change = dialog.commit(); + if (!change.isEmpty()) + m_gitClient->interactiveRebase(workingDirectory, change); +} + void GitPlugin::startRevertCommit() { const VcsBase::VcsBasePluginState state = currentState(); diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 3b373ecc7625b83be487602217c2ca4710f2d364..e0ffc8a45ab3c2499b4555ede68819e2b9479201 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -116,6 +116,7 @@ private slots: void undoFileChanges(bool revertStaging = true); void undoUnstagedFileChanges(); void resetRepository(); + void startRebase(); void startRevertCommit(); void startCherryPickCommit(); void stageFile();