From f84c2285680308cc7a7b48c86763c73dbd794371 Mon Sep 17 00:00:00 2001
From: Tobias Hunger <tobias.hunger@nokia.com>
Date: Thu, 18 Nov 2010 16:58:30 +0100
Subject: [PATCH] Git: Add options to diff editor

---
 src/plugins/git/gitclient.cpp             | 428 ++++++++++++++++++----
 src/plugins/git/gitclient.h               |  41 ++-
 src/plugins/git/giteditor.h               |   2 +-
 src/plugins/git/gitplugin.cpp             |   2 +-
 src/plugins/git/gitsettings.cpp           |  16 +-
 src/plugins/git/gitsettings.h             |   3 +-
 src/plugins/git/gitversioncontrol.cpp     |   2 +-
 src/plugins/git/settingspage.cpp          |  10 +-
 src/plugins/git/settingspage.ui           |  23 +-
 src/plugins/texteditor/basetexteditor.cpp |   2 +-
 src/plugins/texteditor/basetexteditor.h   |   2 +-
 src/plugins/vcsbase/vcsbaseeditor.cpp     |  33 +-
 src/plugins/vcsbase/vcsbaseeditor.h       |   4 +
 13 files changed, 449 insertions(+), 119 deletions(-)

diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 3bf8ccdfac7..4ec8c407613 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -65,11 +65,233 @@
 
 #include <QtGui/QMainWindow> // for msg box parent
 #include <QtGui/QMessageBox>
-#include <QtGui/QPushButton>
+#include <QtGui/QToolButton>
 
 static const char *const kGitDirectoryC = ".git";
 static const char *const kBranchIndicatorC = "# On branch";
 
+namespace Git {
+namespace Internal {
+
+BaseGitArgumentsWidget::BaseGitArgumentsWidget(GitSettings *settings,
+                                               Git::Internal::GitClient *client,
+                                               const QString &directory,
+                                               const QStringList &args) :
+    QWidget(0),
+    m_client(client),
+    m_workingDirectory(directory),
+    m_diffArgs(args),
+    m_settings(settings)
+{
+    Q_ASSERT(settings);
+    Q_ASSERT(client);
+}
+
+} // namespace Internal
+} // namespace Git
+
+namespace {
+
+class BaseGitDiffArgumentsWidget : public Git::Internal::BaseGitArgumentsWidget
+{
+public:
+    BaseGitDiffArgumentsWidget(Git::Internal::GitSettings *settings,
+                               Git::Internal::GitClient *client,
+                               const QString &directory,
+                               const QStringList &args) :
+        BaseGitArgumentsWidget(settings, client, directory, args),
+        m_patience(0),
+        m_ignoreSpaces(0)
+    {
+        QHBoxLayout * layout = new QHBoxLayout(this);
+        layout->setContentsMargins(3, 0, 3, 0);
+
+        m_patience = new QToolButton;
+        m_patience->setText(tr("Patience"));
+        layout->addWidget(m_patience);
+        m_patience->setCheckable(true);
+        m_patience->setChecked(m_settings->diffPatience);
+        connect(m_patience, SIGNAL(toggled(bool)), this, SLOT(testForArgumentsChanged()));
+
+        m_ignoreSpaces = new QToolButton;
+        m_ignoreSpaces->setText(tr("Ignore Whitespace"));
+        layout->addWidget(m_ignoreSpaces);
+        m_ignoreSpaces->setCheckable(true);
+        m_ignoreSpaces->setChecked(m_settings->ignoreSpaceChangesInDiff);
+        connect(m_ignoreSpaces, SIGNAL(toggled(bool)), this, SLOT(testForArgumentsChanged()));
+    }
+
+    QStringList arguments() const
+    {
+        QStringList args = m_diffArgs;
+
+        args.removeAll(QLatin1String("--patience"));
+        args.removeAll(QLatin1String("--ignore-space-change"));
+
+        if (m_patience->isChecked())
+            args.prepend(QLatin1String("--patience"));
+        if (m_ignoreSpaces->isChecked())
+            args.prepend(QLatin1String("--ignore-space-change"));
+
+        return args;
+    }
+
+    void testForArgumentsChanged() {
+        m_settings->diffPatience = m_patience->isChecked();
+        m_settings->ignoreSpaceChangesInDiff = m_ignoreSpaces->isChecked();
+
+        QStringList newArguments = arguments();
+
+        if (newArguments == m_diffArgs)
+            return;
+
+        m_diffArgs = newArguments;
+        redoCommand();
+    }
+
+private:
+    QToolButton *m_patience;
+    QToolButton *m_ignoreSpaces;
+};
+
+class GitCommitDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
+{
+public:
+    GitCommitDiffArgumentsWidget(Git::Internal::GitSettings *settings,
+                                 Git::Internal::GitClient *client, const QString &directory,
+                                 const QStringList &args, const QStringList &unstaged,
+                                 const QStringList &staged) :
+        BaseGitDiffArgumentsWidget(settings, client, directory, args),
+        m_unstagedFileNames(unstaged),
+        m_stagedFileNames(staged)
+    { }
+
+    void redoCommand()
+    {
+        m_client->diff(m_workingDirectory, m_diffArgs, m_unstagedFileNames, m_stagedFileNames);
+    }
+
+private:
+    const QStringList m_unstagedFileNames;
+    const QStringList m_stagedFileNames;
+};
+
+class GitFileDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
+{
+public:
+    GitFileDiffArgumentsWidget(Git::Internal::GitSettings *settings,
+                               Git::Internal::GitClient *client, const QString &directory,
+                               const QStringList &args, const QString &file) :
+        BaseGitDiffArgumentsWidget(settings, client, directory, args),
+        m_fileName(file)
+    { }
+
+    void redoCommand()
+    {
+        m_client->diff(m_workingDirectory, m_diffArgs, m_fileName);
+    }
+
+private:
+    const QString m_fileName;
+};
+
+class GitBranchDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
+{
+public:
+    GitBranchDiffArgumentsWidget(Git::Internal::GitSettings *settings,
+                                 Git::Internal::GitClient *client, const QString &directory,
+                                 const QStringList &args, const QString &branch) :
+        BaseGitDiffArgumentsWidget(settings, client, directory, args),
+        m_branchName(branch)
+    { }
+
+    void redoCommand()
+    {
+        m_client->diffBranch(m_workingDirectory, m_diffArgs, m_branchName);
+    }
+
+private:
+    const QString m_branchName;
+};
+
+class GitBlameArgumentsWidget : public Git::Internal::BaseGitArgumentsWidget
+{
+public:
+    GitBlameArgumentsWidget(Git::Internal::GitSettings *settings,
+                            Git::Internal::GitClient *client, const QString &directory,
+                            const QStringList &args, const QString &revision,
+                            const QString &fileName) :
+        Git::Internal::BaseGitArgumentsWidget(settings, client, directory, args),
+        m_omitDate(0),
+        m_ignoreSpaces(0),
+        m_editor(0),
+        m_revision(revision),
+        m_fileName(fileName)
+    {
+        QHBoxLayout * layout = new QHBoxLayout(this);
+        layout->setContentsMargins(3, 0, 3, 0);
+
+        m_omitDate = new QToolButton;
+        m_omitDate->setText(tr("Omit Date"));
+        layout->addWidget(m_omitDate);
+        m_omitDate->setCheckable(true);
+        m_omitDate->setChecked(m_settings->omitAnnotationDate);
+        m_omitDate->setMinimumHeight(16);
+        m_omitDate->setMaximumHeight(16);
+        connect(m_omitDate, SIGNAL(toggled(bool)), this, SLOT(testForArgumentsChanged()));
+
+        m_ignoreSpaces = new QToolButton;
+        m_ignoreSpaces->setText(tr("Ignore Whitespace"));
+        layout->addWidget(m_ignoreSpaces);
+        m_ignoreSpaces->setCheckable(true);
+        m_ignoreSpaces->setChecked(m_settings->ignoreSpaceChangesInBlame);
+        m_ignoreSpaces->setMinimumHeight(16);
+        m_ignoreSpaces->setMaximumHeight(16);
+        connect(m_ignoreSpaces, SIGNAL(toggled(bool)), this, SLOT(testForArgumentsChanged()));
+    }
+
+    void setEditor(VCSBase::VCSBaseEditor *editor)
+    {
+        Q_ASSERT(editor);
+        m_editor = editor;
+    }
+
+    QStringList arguments() const
+    {
+        QStringList args = m_diffArgs;
+
+        args.removeAll(QLatin1String("-w"));
+
+        if (m_ignoreSpaces->isChecked())
+            args.prepend(QLatin1String("-w"));
+
+        return args;
+    }
+
+    void testForArgumentsChanged() {
+        m_settings->omitAnnotationDate = m_omitDate->isChecked();
+        m_settings->ignoreSpaceChangesInBlame = m_ignoreSpaces->isChecked();
+
+        m_diffArgs = arguments();
+        redoCommand(); // always redo for omit date
+    }
+
+    void redoCommand()
+    {
+        m_client->blame(m_workingDirectory, m_diffArgs, m_fileName,
+                        m_revision, m_editor->lineNumberOfCurrentEditor());
+    }
+
+private:
+    QToolButton *m_omitDate;
+    QToolButton *m_ignoreSpaces;
+    VCSBase::VCSBaseEditor *m_editor;
+    QString m_revision;
+    QString m_fileName;
+};
+
+} // namespace
+
 inline Core::IEditor* locateEditor(const Core::ICore *core, const char *property, const QString &entry)
 {
     foreach (Core::IEditor *ed, core->editorManager()->openedEditors())
@@ -147,41 +369,56 @@ QString GitClient::findRepositoryForDirectory(const QString &dir)
     return VCSBase::VCSBasePlugin::findRepositoryForDirectory(dir, checkFile);
 }
 
+VCSBase::VCSBaseEditor *GitClient::findExistingVCSEditor(const char *registerDynamicProperty,
+                                                         const QString &dynamicPropertyValue) const
+{
+    VCSBase::VCSBaseEditor *rc = 0;
+    Core::IEditor *outputEditor = locateEditor(m_core, registerDynamicProperty, dynamicPropertyValue);
+    if (!outputEditor)
+        return 0;
+
+     // Exists already
+    outputEditor->createNew(m_msgWait);
+    rc = VCSBase::VCSBaseEditor::getVcsBaseEditor(outputEditor);
+
+    return rc;
+}
+
+
 /* Create an editor associated to VCS output of a source file/directory
  * (using the file's codec). Makes use of a dynamic property to find an
  * existing instance and to reuse it (in case, say, 'git diff foo' is
  * already open). */
-VCSBase::VCSBaseEditor
-    *GitClient::createVCSEditor(const QString &id,
-                                QString title,
-                                // Source file or directory
-                                const QString &source,
-                                bool setSourceCodec,
-                                // Dynamic property and value to identify that editor
-                                const char *registerDynamicProperty,
-                                const QString &dynamicPropertyValue) const
+VCSBase::VCSBaseEditor *GitClient::createVCSEditor(const QString &id,
+                                                   QString title,
+                                                   // Source file or directory
+                                                   const QString &source,
+                                                   bool setSourceCodec,
+                                                   // Dynamic property and value to identify that editor
+                                                   const char *registerDynamicProperty,
+                                                   const QString &dynamicPropertyValue,
+                                                   QWidget *configWidget) const
 {
     VCSBase::VCSBaseEditor *rc = 0;
-    Core::IEditor* outputEditor = locateEditor(m_core, registerDynamicProperty, dynamicPropertyValue);
-    if (outputEditor) {
-         // Exists already
-        outputEditor->createNew(m_msgWait);
-        rc = VCSBase::VCSBaseEditor::getVcsBaseEditor(outputEditor);
-        QTC_ASSERT(rc, return 0);
-    } else {
-        // Create new, set wait message, set up with source and codec
-        outputEditor = m_core->editorManager()->openEditorWithContents(id, &title, m_msgWait);
-        outputEditor->file()->setProperty(registerDynamicProperty, dynamicPropertyValue);
-        rc = VCSBase::VCSBaseEditor::getVcsBaseEditor(outputEditor);
-        connect(rc, SIGNAL(annotateRevisionRequested(QString,QString,int)),
-                this, SLOT(slotBlameRevisionRequested(QString,QString,int)));
-        QTC_ASSERT(rc, return 0);
-        rc->setSource(source);
-        if (setSourceCodec)
-            rc->setCodec(VCSBase::VCSBaseEditor::getCodec(source));
-    }
+    Q_ASSERT(!findExistingVCSEditor(registerDynamicProperty, dynamicPropertyValue));
+
+    // Create new, set wait message, set up with source and codec
+    Core::IEditor *outputEditor = m_core->editorManager()->openEditorWithContents(id, &title, m_msgWait);
+    outputEditor->file()->setProperty(registerDynamicProperty, dynamicPropertyValue);
+    rc = VCSBase::VCSBaseEditor::getVcsBaseEditor(outputEditor);
+    connect(rc, SIGNAL(annotateRevisionRequested(QString,QString,int)),
+            this, SLOT(slotBlameRevisionRequested(QString,QString,int)));
+    QTC_ASSERT(rc, return 0);
+    rc->setSource(source);
+    if (setSourceCodec)
+        rc->setCodec(VCSBase::VCSBaseEditor::getCodec(source));
+
     m_core->editorManager()->activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
     rc->setForceReadOnly(true);
+
+    if (configWidget)
+        rc->setConfigurationWidget(configWidget);
+
     return rc;
 }
 
@@ -198,7 +435,17 @@ void GitClient::diff(const QString &workingDirectory,
     const QString editorId = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_ID);
     const QString title = tr("Git Diff");
 
-    VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, workingDirectory, true, "originalFileName", workingDirectory);
+    QStringList userDiffArgs = diffArgs;
+    VCSBase::VCSBaseEditor *editor = findExistingVCSEditor("originalFileName", workingDirectory);
+    if (!editor) {
+        GitCommitDiffArgumentsWidget *argWidget =
+                new GitCommitDiffArgumentsWidget(&m_settings, this, workingDirectory, diffArgs,
+                                                 unstagedFileNames, stagedFileNames);
+        userDiffArgs = argWidget->arguments();
+
+        editor = createVCSEditor(editorId, title,
+                                 workingDirectory, true, "originalFileName", workingDirectory, argWidget);
+    }
     editor->setDiffBaseDirectory(workingDirectory);
 
     // Create a batch of 2 commands to be run after each other in case
@@ -206,27 +453,27 @@ void GitClient::diff(const QString &workingDirectory,
     // when using the submit dialog.
     GitCommand *command = createCommand(workingDirectory, editor);
     // Directory diff?
-    QStringList commonDiffArgs;
-    commonDiffArgs << QLatin1String("diff") << QLatin1String(noColorOption);
-    if (m_settings.diffPatience)
-        commonDiffArgs << QLatin1String("--patience");
-    if (m_settings.ignoreSpaceChanges)
-        commonDiffArgs << QLatin1String("--ignore-space-change");
+
+    QStringList cmdArgs;
+    cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption);
+
     if (unstagedFileNames.empty() && stagedFileNames.empty()) {
-       QStringList arguments(commonDiffArgs);
-       arguments << diffArgs;
+       QStringList arguments(cmdArgs);
+       arguments << userDiffArgs;
        outputWindow()->appendCommand(workingDirectory, binary, arguments);
        command->addJob(arguments, m_settings.timeoutSeconds);
     } else {
         // Files diff.
         if (!unstagedFileNames.empty()) {
-           QStringList arguments(commonDiffArgs);
+           QStringList arguments(cmdArgs);
+           arguments << userDiffArgs;
            arguments << QLatin1String("--") << unstagedFileNames;
            outputWindow()->appendCommand(workingDirectory, binary, arguments);
            command->addJob(arguments, m_settings.timeoutSeconds);
         }
         if (!stagedFileNames.empty()) {
-           QStringList arguments(commonDiffArgs);
+           QStringList arguments(cmdArgs);
+           arguments << userDiffArgs;
            arguments << QLatin1String("--cached") << diffArgs << QLatin1String("--") << stagedFileNames;
            outputWindow()->appendCommand(workingDirectory, binary, arguments);
            command->addJob(arguments, m_settings.timeoutSeconds);
@@ -239,18 +486,32 @@ void GitClient::diff(const QString &workingDirectory,
                      const QStringList &diffArgs,
                      const QString &fileName)
 {
-    if (Git::Constants::debug)
-        qDebug() << "diff" << workingDirectory << fileName;
-    QStringList arguments;
-    arguments << QLatin1String("diff") << QLatin1String(noColorOption)
-              << diffArgs;
-    if (!fileName.isEmpty())
-        arguments << QLatin1String("--") << fileName;
+    // if (Git::Constants::debug)
+        qDebug() << "diff" << workingDirectory << fileName << diffArgs;
+
+
     const QString editorId = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_ID);
     const QString title = tr("Git Diff %1").arg(fileName);
     const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, fileName);
-    VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, sourceFile, true, "originalFileName", sourceFile);
-    executeGit(workingDirectory, arguments, editor);
+
+    QStringList userDiffArgs = diffArgs;
+    VCSBase::VCSBaseEditor *editor = findExistingVCSEditor("originalFileName", sourceFile);
+    if (!editor) {
+        GitFileDiffArgumentsWidget *argWidget =
+                new GitFileDiffArgumentsWidget(&m_settings, this, workingDirectory,
+                                               diffArgs, fileName);
+        userDiffArgs = argWidget->arguments();
+
+        editor = createVCSEditor(editorId, title, sourceFile, true, "originalFileName", sourceFile, argWidget);
+    }
+
+    QStringList cmdArgs;
+    cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption)
+              << userDiffArgs;
+
+    if (!fileName.isEmpty())
+        cmdArgs << QLatin1String("--") << fileName;
+    executeGit(workingDirectory, cmdArgs, editor);
 }
 
 void GitClient::diffBranch(const QString &workingDirectory,
@@ -259,16 +520,27 @@ void GitClient::diffBranch(const QString &workingDirectory,
 {
     if (Git::Constants::debug)
         qDebug() << "diffBranch" << workingDirectory << branchName;
-    QStringList arguments;
-    arguments << QLatin1String("diff") << QLatin1String(noColorOption)
-              << diffArgs  << branchName;
 
     const QString editorId = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_ID);
     const QString title = tr("Git Diff Branch %1").arg(branchName);
     const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, QStringList());
-    VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, sourceFile, true,
-                                                     "BranchName", branchName);
-    executeGit(workingDirectory, arguments, editor);
+
+    QStringList userDiffArgs = diffArgs;
+    VCSBase::VCSBaseEditor *editor = findExistingVCSEditor("BranchName", branchName);
+    if (!editor) {
+        GitBranchDiffArgumentsWidget *argWidget =
+                new GitBranchDiffArgumentsWidget(&m_settings, this, workingDirectory,
+                                                 diffArgs, branchName);
+        userDiffArgs = argWidget->arguments();
+
+        editor = createVCSEditor(editorId, title, sourceFile, true, "BranchName", branchName, argWidget);
+    }
+
+    QStringList cmdArgs;
+    cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption)
+              << diffArgs  << branchName;
+
+    executeGit(workingDirectory, cmdArgs, editor);
 }
 
 void GitClient::status(const QString &workingDirectory)
@@ -308,7 +580,9 @@ void GitClient::graphLog(const QString &workingDirectory, const QString & branch
     }
     const QString editorId = QLatin1String(Git::Constants::GIT_LOG_EDITOR_ID);
     const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, QStringList());
-    VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, sourceFile, false, "logFileName", sourceFile);
+    VCSBase::VCSBaseEditor *editor = findExistingVCSEditor("logFileName", sourceFile);
+    if (!editor)
+        editor = createVCSEditor(editorId, title, sourceFile, false, "logFileName", sourceFile, 0);
     executeGit(workingDirectory, arguments, editor);
 }
 
@@ -331,7 +605,9 @@ void GitClient::log(const QString &workingDirectory, const QStringList &fileName
     const QString title = tr("Git Log %1").arg(msgArg);
     const QString editorId = QLatin1String(Git::Constants::GIT_LOG_EDITOR_ID);
     const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, fileNames);
-    VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, sourceFile, false, "logFileName", sourceFile);
+    VCSBase::VCSBaseEditor *editor = findExistingVCSEditor("logFileName", sourceFile);
+    if (!editor)
+        editor = createVCSEditor(editorId, title, sourceFile, false, "logFileName", sourceFile, 0);
     editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu);
     executeGit(workingDirectory, arguments, editor);
 }
@@ -365,7 +641,9 @@ void GitClient::show(const QString &source, const QString &id)
 
     const QString title =  tr("Git Show %1").arg(id);
     const QString editorId = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_ID);
-    VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, source, true, "show", id);
+    VCSBase::VCSBaseEditor *editor = findExistingVCSEditor("show", id);
+    if (!editor)
+        editor = createVCSEditor(editorId, title, source, true, "show", id, 0);
 
     const QFileInfo sourceFi(source);
     const QString workDir = sourceFi.isDir() ? sourceFi.absoluteFilePath() : sourceFi.absolutePath();
@@ -380,29 +658,41 @@ void GitClient::slotBlameRevisionRequested(const QString &source, QString change
     if (blankPos != -1)
         change.truncate(blankPos);
     const QFileInfo fi(source);
-    blame(fi.absolutePath(), fi.fileName(), change, lineNumber);
+    blame(fi.absolutePath(), QStringList(), fi.fileName(), change, lineNumber);
 }
 
 void GitClient::blame(const QString &workingDirectory,
+                      const QStringList &args,
                       const QString &fileName,
                       const QString &revision /* = QString() */,
                       int lineNumber /* = -1 */)
 {
     if (Git::Constants::debug)
-        qDebug() << "blame" << workingDirectory << fileName << lineNumber;
-    QStringList arguments(QLatin1String("blame"));
-    arguments << QLatin1String("--root");
-    if (m_plugin->settings().ignoreSpaceChanges)
-        arguments << QLatin1String("-w");
-    arguments << QLatin1String("--") << fileName;
-    if (!revision.isEmpty())
-        arguments << revision;
+        qDebug() << "blame" << workingDirectory << fileName << lineNumber << args;
+
     const QString editorId = QLatin1String(Git::Constants::GIT_BLAME_EDITOR_ID);
     const QString id = VCSBase::VCSBaseEditor::getTitleId(workingDirectory, QStringList(fileName), revision);
     const QString title = tr("Git Blame %1").arg(id);
     const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, fileName);
 
-    VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, sourceFile, true, "blameFileName", id);
+    QStringList userBlameArgs = args;
+    VCSBase::VCSBaseEditor *editor = findExistingVCSEditor("blameFileName", id);
+    if (!editor) {
+        GitBlameArgumentsWidget *argWidget =
+                new GitBlameArgumentsWidget(&m_settings, this, workingDirectory, userBlameArgs,
+                                            revision, fileName);
+        editor = createVCSEditor(editorId, title, sourceFile, true, "blameFileName", id, argWidget);
+        argWidget->setEditor(editor);
+
+        userBlameArgs = argWidget->arguments();
+    }
+
+    QStringList arguments(QLatin1String("blame"));
+    arguments << QLatin1String("--root");
+    arguments.append(userBlameArgs);
+    arguments << QLatin1String("--") << fileName;
+    if (!revision.isEmpty())
+        arguments << revision;
     executeGit(workingDirectory, arguments, editor, false, GitCommand::NoReport, lineNumber);
 }
 
@@ -1776,7 +2066,9 @@ void GitClient::subversionLog(const QString &workingDirectory)
     const QString title = tr("Git SVN Log");
     const QString editorId = QLatin1String(Git::Constants::C_GIT_COMMAND_LOG_EDITOR);
     const QString sourceFile = VCSBase::VCSBaseEditor::getSource(workingDirectory, QStringList());
-    VCSBase::VCSBaseEditor *editor = createVCSEditor(editorId, title, sourceFile, false, "svnLog", sourceFile);
+    VCSBase::VCSBaseEditor *editor = findExistingVCSEditor("svnLog", sourceFile);
+    if (!editor)
+        editor = createVCSEditor(editorId, title, sourceFile, false, "svnLog", sourceFile, 0);
     executeGit(workingDirectory, arguments, editor);
 }
 
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index 2c8226d32f8..db8ba15f1f1 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -1,4 +1,4 @@
-/**************************************************************************
+ /**************************************************************************
 **
 ** This file is part of Qt Creator
 **
@@ -37,8 +37,10 @@
 
 #include <QtCore/QString>
 #include <QtCore/QStringList>
+#include <QtGui/QWidget>
 
 QT_BEGIN_NAMESPACE
+class QCheckBox;
 class QErrorMessage;
 class QSignalMapper;
 class QDebug;
@@ -91,7 +93,7 @@ public:
     void graphLog(const QString &workingDirectory, const QString &branch);
     void log(const QString &workingDirectory, const QStringList &fileNames,
              bool enableAnnotationContextMenu = false);
-    void blame(const QString &workingDirectory, const QString &fileName,
+    void blame(const QString &workingDirectory, const QStringList &args, const QString &fileName,
                const QString &revision = QString(), int lineNumber = -1);
     void checkout(const QString &workingDirectory, const QString &file);
     void checkoutBranch(const QString &workingDirectory, const QString &branch);
@@ -229,12 +231,15 @@ private slots:
     void slotBlameRevisionRequested(const QString &source, QString change, int lineNumber);
 
 private:
+    VCSBase::VCSBaseEditor *findExistingVCSEditor(const char *registerDynamicProperty,
+                                                  const QString &dynamicPropertyValue) const;
     VCSBase::VCSBaseEditor *createVCSEditor(const QString &kind,
-                                                 QString title,
-                                                 const QString &source,
-                                                 bool setSourceCodec,
-                                                 const char *registerDynamicProperty,
-                                                 const QString &dynamicPropertyValue) const;
+                                            QString title,
+                                            const QString &source,
+                                            bool setSourceCodec,
+                                            const char *registerDynamicProperty,
+                                            const QString &dynamicPropertyValue,
+                                            QWidget *configWidget) const;
 
     GitCommand *createCommand(const QString &workingDirectory,
                              VCSBase::VCSBaseEditor* editor = 0,
@@ -288,6 +293,28 @@ private:
     bool          m_hasCachedGitVersion;
 };
 
+class BaseGitArgumentsWidget : public QWidget {
+    Q_OBJECT
+
+public:
+    BaseGitArgumentsWidget(GitSettings *settings,
+                           GitClient *client, const QString &directory,
+                           const QStringList &args);
+
+    virtual QStringList arguments() const = 0;
+    virtual void redoCommand() = 0;
+
+protected slots:
+    virtual void testForArgumentsChanged() = 0;
+
+protected:
+    GitClient *m_client;
+
+    const QString m_workingDirectory;
+    QStringList m_diffArgs;
+    GitSettings *m_settings;
+};
+
 } // namespace Internal
 } // namespace Git
 
diff --git a/src/plugins/git/giteditor.h b/src/plugins/git/giteditor.h
index c82a501e545..7028a9c2806 100644
--- a/src/plugins/git/giteditor.h
+++ b/src/plugins/git/giteditor.h
@@ -47,7 +47,7 @@ class GitEditor : public VCSBase::VCSBaseEditor
 
 public:
     explicit GitEditor(const VCSBase::VCSBaseEditorParameters *type,
-                            QWidget *parent);
+                       QWidget *parent);
 
 public slots:
     void setPlainTextDataFiltered(const QByteArray &a);
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index 835592132be..25113a7a6de 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -596,7 +596,7 @@ void GitPlugin::blameFile()
     const VCSBase::VCSBasePluginState state = currentState();
     QTC_ASSERT(state.hasFile(), return)
     const int lineNumber = VCSBase::VCSBaseEditor::lineNumberOfCurrentEditor(state.currentFile());
-    m_gitClient->blame(state.currentFileTopLevel(), state.relativeCurrentFile(), QString(), lineNumber);
+    m_gitClient->blame(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile(), QString(), lineNumber);
 }
 
 void GitPlugin::logProject()
diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp
index 3cded7b2d87..56f77548e22 100644
--- a/src/plugins/git/gitsettings.cpp
+++ b/src/plugins/git/gitsettings.cpp
@@ -44,7 +44,8 @@ static const char timeoutKeyC[] = "TimeOut";
 static const char pullRebaseKeyC[] = "PullRebase";
 static const char promptToSubmitKeyC[] = "PromptForSubmit";
 static const char omitAnnotationDateKeyC[] = "OmitAnnotationDate";
-static const char ignoreSpaceChangesC[] = "SpaceIgnorantBlame";
+static const char ignoreSpaceChangesBlameKeyC[] = "SpaceIgnorantBlame";
+static const char ignoreSpaceChangesDiffKeyC[] = "SpaceIgnorantDiff";
 static const char diffPatienceKeyC[] = "DiffPatience";
 static const char winSetHomeEnvironmentKeyC[] = "WinSetHomeEnvironment";
 static const char gitkOptionsKeyC[] = "GitKOptions";
@@ -69,7 +70,8 @@ GitSettings::GitSettings() :
     pullRebase(bool(defaultPullRebase)),
     promptToSubmit(true),
     omitAnnotationDate(false),
-    ignoreSpaceChanges(true),
+    ignoreSpaceChangesInDiff(false),
+    ignoreSpaceChangesInBlame(true),
     diffPatience(true),
     winSetHomeEnvironment(false)
 {
@@ -85,7 +87,8 @@ void GitSettings::fromSettings(QSettings *settings)
     pullRebase = settings->value(QLatin1String(pullRebaseKeyC), bool(defaultPullRebase)).toBool();
     promptToSubmit = settings->value(QLatin1String(promptToSubmitKeyC), true).toBool();
     omitAnnotationDate = settings->value(QLatin1String(omitAnnotationDateKeyC), false).toBool();
-    ignoreSpaceChanges = settings->value(QLatin1String(ignoreSpaceChangesC), true).toBool();
+    ignoreSpaceChangesInDiff = settings->value(QLatin1String(ignoreSpaceChangesDiffKeyC), true).toBool();
+    ignoreSpaceChangesInBlame = settings->value(QLatin1String(ignoreSpaceChangesBlameKeyC), true).toBool();
     diffPatience = settings->value(QLatin1String(diffPatienceKeyC), true).toBool();
     winSetHomeEnvironment = settings->value(QLatin1String(winSetHomeEnvironmentKeyC), false).toBool();
     gitkOptions = settings->value(QLatin1String(gitkOptionsKeyC)).toString();
@@ -102,7 +105,8 @@ void GitSettings::toSettings(QSettings *settings) const
     settings->setValue(QLatin1String(pullRebaseKeyC), pullRebase);
     settings->setValue(QLatin1String(promptToSubmitKeyC), promptToSubmit);
     settings->setValue(QLatin1String(omitAnnotationDateKeyC), omitAnnotationDate);
-    settings->setValue(QLatin1String(ignoreSpaceChangesC), ignoreSpaceChanges);
+    settings->setValue(QLatin1String(ignoreSpaceChangesDiffKeyC), ignoreSpaceChangesInDiff);
+    settings->setValue(QLatin1String(ignoreSpaceChangesBlameKeyC), ignoreSpaceChangesInBlame);
     settings->setValue(QLatin1String(diffPatienceKeyC), diffPatience);
     settings->setValue(QLatin1String(winSetHomeEnvironmentKeyC), winSetHomeEnvironment);
     settings->setValue(QLatin1String(gitkOptionsKeyC), gitkOptions);
@@ -114,7 +118,9 @@ bool GitSettings::equals(const GitSettings &s) const
     return adoptPath == s.adoptPath && path == s.path && logCount == s.logCount
            && timeoutSeconds == s.timeoutSeconds && promptToSubmit == s.promptToSubmit
            && pullRebase == s.pullRebase
-           && omitAnnotationDate == s.omitAnnotationDate && ignoreSpaceChanges == s.ignoreSpaceChanges
+           && omitAnnotationDate == s.omitAnnotationDate
+           && ignoreSpaceChangesInBlame == s.ignoreSpaceChangesInBlame
+           && ignoreSpaceChangesInDiff == s.ignoreSpaceChangesInDiff
            && diffPatience == s.diffPatience && winSetHomeEnvironment == s.winSetHomeEnvironment
            && gitkOptions == s.gitkOptions;
 }
diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h
index d7235230621..675162ed11d 100644
--- a/src/plugins/git/gitsettings.h
+++ b/src/plugins/git/gitsettings.h
@@ -59,7 +59,8 @@ struct GitSettings
     bool pullRebase;
     bool promptToSubmit;
     bool omitAnnotationDate;
-    bool ignoreSpaceChanges;
+    bool ignoreSpaceChangesInDiff;
+    bool ignoreSpaceChangesInBlame;
     bool diffPatience;
     bool winSetHomeEnvironment;
     QString gitkOptions;
diff --git a/src/plugins/git/gitversioncontrol.cpp b/src/plugins/git/gitversioncontrol.cpp
index ad6cc63c2da..68926d42978 100644
--- a/src/plugins/git/gitversioncontrol.cpp
+++ b/src/plugins/git/gitversioncontrol.cpp
@@ -232,7 +232,7 @@ bool GitVersionControl::managesDirectory(const QString &directory, QString *topL
 bool GitVersionControl::vcsAnnotate(const QString &file, int line)
 {
     const QFileInfo fi(file);
-    gitClient()->blame(fi.absolutePath(), fi.fileName(), QString(), line);
+    gitClient()->blame(fi.absolutePath(), QStringList(), fi.fileName(), QString(), line);
     return true;
 }
 
diff --git a/src/plugins/git/settingspage.cpp b/src/plugins/git/settingspage.cpp
index 93c27315053..423cd920c70 100644
--- a/src/plugins/git/settingspage.cpp
+++ b/src/plugins/git/settingspage.cpp
@@ -73,9 +73,6 @@ GitSettings SettingsPageWidget::settings() const
     rc.timeoutSeconds = m_ui.timeoutSpinBox->value();
     rc.pullRebase = m_ui.pullRebaseCheckBox->isChecked();
     rc.promptToSubmit = m_ui.promptToSubmitCheckBox->isChecked();
-    rc.omitAnnotationDate = m_ui.omitAnnotationDataCheckBox->isChecked();
-    rc.ignoreSpaceChanges = m_ui.spaceIgnorantBlameCheckBox->isChecked();
-    rc.diffPatience = m_ui.diffPatienceCheckBox->isChecked();
     rc.winSetHomeEnvironment = m_ui.winHomeCheckBox->isChecked();
     rc.gitkOptions = m_ui.gitkOptionsLineEdit->text().trimmed();
     return rc;
@@ -89,9 +86,6 @@ void SettingsPageWidget::setSettings(const GitSettings &s)
     m_ui.timeoutSpinBox->setValue(s.timeoutSeconds);
     m_ui.pullRebaseCheckBox->setChecked(s.pullRebase);
     m_ui.promptToSubmitCheckBox->setChecked(s.promptToSubmit);
-    m_ui.omitAnnotationDataCheckBox->setChecked(s.omitAnnotationDate);
-    m_ui.spaceIgnorantBlameCheckBox->setChecked(s.ignoreSpaceChanges);
-    m_ui.diffPatienceCheckBox->setChecked(s.diffPatience);
     m_ui.winHomeCheckBox->setChecked(s.winSetHomeEnvironment);
     m_ui.gitkOptionsLineEdit->setText(s.gitkOptions);
 }
@@ -108,9 +102,7 @@ QString SettingsPageWidget::searchKeywords() const
         << ' ' << m_ui.timeoutLabel->text()
         << ' ' << m_ui.promptToSubmitCheckBox->text()
         << ' ' << m_ui.promptToSubmitCheckBox->text()
-        << ' ' << m_ui.omitAnnotationDataCheckBox->text()
-        << ' ' << m_ui.environmentGroupBox->title()
-        << ' ' << m_ui.spaceIgnorantBlameCheckBox->text();
+        << ' ' << m_ui.environmentGroupBox->title();
     rc.remove(QLatin1Char('&'));
     return rc;
 }
diff --git a/src/plugins/git/settingspage.ui b/src/plugins/git/settingspage.ui
index 386de3f01b1..6a7eefe8810 100644
--- a/src/plugins/git/settingspage.ui
+++ b/src/plugins/git/settingspage.ui
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>361</width>
+    <width>390</width>
     <height>444</height>
    </rect>
   </property>
@@ -128,27 +128,6 @@
         </property>
        </widget>
       </item>
-      <item row="5" column="0" colspan="2">
-       <widget class="QCheckBox" name="omitAnnotationDataCheckBox">
-        <property name="text">
-         <string>Omit date from blame output</string>
-        </property>
-       </widget>
-      </item>
-      <item row="6" column="0" colspan="2">
-       <widget class="QCheckBox" name="spaceIgnorantBlameCheckBox">
-        <property name="text">
-         <string>Ignore whitespace changes in blame and diff</string>
-        </property>
-       </widget>
-      </item>
-      <item row="4" column="0">
-       <widget class="QCheckBox" name="diffPatienceCheckBox">
-        <property name="text">
-         <string>Use &quot;patience diff&quot; algorithm</string>
-        </property>
-       </widget>
-      </item>
       <item row="3" column="0" colspan="2">
        <widget class="QCheckBox" name="pullRebaseCheckBox">
         <property name="text">
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index d9e2284d64f..e729431e76f 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -5927,7 +5927,7 @@ BaseTextEditorEditable::BaseTextEditorEditable(BaseTextEditor *editor)
     QWidget *w = new QWidget;
     l->setMargin(0);
     l->setContentsMargins(5, 0, 5, 0);
-    l->addStretch(0);
+    // l->addStretch(0);
     l->addWidget(m_cursorPositionLabel);
     w->setLayout(l);
 
diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h
index 9b19c2f0448..6f4dfc725c5 100644
--- a/src/plugins/texteditor/basetexteditor.h
+++ b/src/plugins/texteditor/basetexteditor.h
@@ -573,7 +573,7 @@ public:
 
     inline QByteArray saveState() const { return e->saveState(); }
     inline bool restoreState(const QByteArray &state) { return e->restoreState(state); }
-    QWidget *toolBar();
+    virtual QWidget *toolBar();
 
     // ITextEditor
     int find(const QString &string) const;
diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp
index f65e221f634..c2939cd8576 100644
--- a/src/plugins/vcsbase/vcsbaseeditor.cpp
+++ b/src/plugins/vcsbase/vcsbaseeditor.cpp
@@ -131,7 +131,6 @@ VCSBaseDiffEditorEditable::VCSBaseDiffEditorEditable(VCSBaseEditor *e, const VCS
     m_diffFileBrowseComboBox(new QComboBox(m_toolBar))
 {
     m_diffFileBrowseComboBox->setMinimumContentsLength(20);
-    m_diffFileBrowseComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
     // Make the combo box prefer to expand
     QSizePolicy policy = m_diffFileBrowseComboBox->sizePolicy();
     policy.setHorizontalPolicy(QSizePolicy::Expanding);
@@ -163,6 +162,8 @@ struct VCSBaseEditorPrivate
     QString m_annotatePreviousRevisionTextFormat;
     QString m_copyRevisionTextFormat;
     bool m_fileLogAnnotateEnabled;
+    QToolBar *m_toolBar;
+    QWidget *m_configurationWidget;
 };
 
 VCSBaseEditorPrivate::VCSBaseEditorPrivate(const VCSBaseEditorParameters *type)  :
@@ -170,7 +171,9 @@ VCSBaseEditorPrivate::VCSBaseEditorPrivate(const VCSBaseEditorParameters *type)
     m_cursorLine(-1),
     m_annotateRevisionTextFormat(VCSBaseEditor::tr("Annotate \"%1\"")),
     m_copyRevisionTextFormat(VCSBaseEditor::tr("Copy \"%1\"")),
-    m_fileLogAnnotateEnabled(false)
+    m_fileLogAnnotateEnabled(false),
+    m_toolBar(0),
+    m_configurationWidget(0)
 {
 }
 
@@ -325,6 +328,8 @@ TextEditor::BaseTextEditorEditable *VCSBaseEditor::createEditableInterface()
     } else {
         editable = new VCSBaseEditorEditable(this, d->m_parameters);
     }
+    d->m_toolBar = qobject_cast<QToolBar *>(editable->toolBar());
+
     // Pass on signals.
     connect(this, SIGNAL(describeRequested(QString,QString)),
             editable, SIGNAL(describeRequested(QString,QString)));
@@ -829,6 +834,30 @@ QString VCSBaseEditor::getTitleId(const QString &workingDirectory,
     return rc;
 }
 
+bool VCSBaseEditor::setConfigurationWidget(QWidget *w)
+{
+    if (!d->m_toolBar || d->m_configurationWidget)
+        return false;
+
+    d->m_configurationWidget = w;
+    if (contentType() == AnnotateOutput) {
+        QList<QAction *> actions = d->m_toolBar->actions();
+        Q_ASSERT(actions.count() >= 1);
+        QWidget *spacer = new QWidget(d->m_toolBar);
+        spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+        QAction *configAction = d->m_toolBar->insertWidget(actions.at(0), w);
+        d->m_toolBar->insertWidget(configAction, spacer);
+    } else {
+        d->m_toolBar->addWidget(w);
+    }
+    return true;
+}
+
+QWidget *VCSBaseEditor::configurationWidget() const
+{
+    return d->m_configurationWidget;
+}
+
 // Find the complete file from a diff relative specification.
 QString VCSBaseEditor::findDiffFile(const QString &f, Core::IVersionControl *control /* = 0 */) const
 {
diff --git a/src/plugins/vcsbase/vcsbaseeditor.h b/src/plugins/vcsbase/vcsbaseeditor.h
index f03a83554b3..737c61fab75 100644
--- a/src/plugins/vcsbase/vcsbaseeditor.h
+++ b/src/plugins/vcsbase/vcsbaseeditor.h
@@ -178,6 +178,10 @@ public:
     static QString getTitleId(const QString &workingDirectory,
                               const QStringList &fileNames,
                               const QString &revision = QString());
+
+    bool setConfigurationWidget(QWidget *w);
+    QWidget *configurationWidget() const;
+
 signals:
     // These signals also exist in the opaque editable (IEditor) that is
     // handled by the editor manager for convenience. They are emitted
-- 
GitLab