From 97a86c50dc00551818e9fcef37908ef7ebfb242b Mon Sep 17 00:00:00 2001
From: jkobus <jaroslaw.kobus@digia.com>
Date: Tue, 7 May 2013 14:02:08 +0200
Subject: [PATCH] Basic integration of diff editor inside git plugin

Change-Id: I7675fc1d994020f94f42f6bd7b4f75aa29e6edf6
Reviewed-by: David Schulz <david.schulz@digia.com>
Reviewed-by: Tobias Hunger <tobias.hunger@digia.com>
---
 src/plugins/diffeditor/diffeditor.pro         |  12 +-
 src/plugins/diffeditor/diffeditor.qbs         |  12 +-
 src/plugins/diffeditor/diffeditoreditable.cpp | 162 +++++++
 src/plugins/diffeditor/diffeditoreditable.h   |  82 ++++
 src/plugins/diffeditor/diffeditorfile.cpp     |  96 ++++
 src/plugins/diffeditor/diffeditorfile.h       |  78 ++++
 src/plugins/diffeditor/diffeditorplugin.cpp   | 202 +--------
 src/plugins/diffeditor/diffeditorplugin.h     |  74 +--
 src/plugins/diffeditor/diffeditorwidget.cpp   | 160 ++++---
 src/plugins/diffeditor/diffeditorwidget.h     |  66 +--
 src/plugins/git/git.qbs                       |   1 +
 src/plugins/git/git_dependencies.pri          |   3 +-
 src/plugins/git/gitclient.cpp                 | 423 +++++++++++++++---
 src/plugins/git/gitclient.h                   |  10 +
 src/plugins/git/giteditor.cpp                 |   2 +
 src/plugins/git/gitsettings.cpp               |   2 +
 src/plugins/git/gitsettings.h                 |   1 +
 src/plugins/git/settingspage.cpp              |   2 +
 src/plugins/git/settingspage.ui               |   7 +
 src/plugins/vcsbase/command.cpp               |  12 +-
 20 files changed, 965 insertions(+), 442 deletions(-)
 create mode 100644 src/plugins/diffeditor/diffeditoreditable.cpp
 create mode 100644 src/plugins/diffeditor/diffeditoreditable.h
 create mode 100644 src/plugins/diffeditor/diffeditorfile.cpp
 create mode 100644 src/plugins/diffeditor/diffeditorfile.h

diff --git a/src/plugins/diffeditor/diffeditor.pro b/src/plugins/diffeditor/diffeditor.pro
index 9e36bdbf774..2d6b13fc23b 100644
--- a/src/plugins/diffeditor/diffeditor.pro
+++ b/src/plugins/diffeditor/diffeditor.pro
@@ -1,13 +1,17 @@
 DEFINES += DIFFEDITOR_LIBRARY
 include(../../qtcreatorplugin.pri)
 
-HEADERS += diffeditorplugin.h \
-        diffeditorwidget.h \
+HEADERS += diffeditor_global.h \
         diffeditorconstants.h \
-        diffeditor_global.h \
+        diffeditoreditable.h \
+        diffeditorfile.h \
+        diffeditorplugin.h \
+        diffeditorwidget.h \
         differ.h
 
-SOURCES += diffeditorplugin.cpp \
+SOURCES += diffeditoreditable.cpp \
+        diffeditorfile.cpp \
+        diffeditorplugin.cpp \
         diffeditorwidget.cpp \
         differ.cpp
 
diff --git a/src/plugins/diffeditor/diffeditor.qbs b/src/plugins/diffeditor/diffeditor.qbs
index 568321d1498..6d472cbc75e 100644
--- a/src/plugins/diffeditor/diffeditor.qbs
+++ b/src/plugins/diffeditor/diffeditor.qbs
@@ -13,14 +13,18 @@ QtcPlugin {
     Depends { name: "cpp" }
 
     files: [
+        "diffeditor_global.h",
+        "diffeditorconstants.h",
+        "diffeditoreditable.cpp",
+        "diffeditoreditable.h",
+        "diffeditorfile.cpp",
+        "diffeditorfile.h",
         "diffeditorplugin.cpp",
         "diffeditorplugin.h",
-        "differ.cpp",
-        "differ.h",
         "diffeditorwidget.cpp",
         "diffeditorwidget.h",
-        "diffeditorconstants.h",
-        "diffeditor_global.h",
+        "differ.cpp",
+        "differ.h",
     ]
 }
 
diff --git a/src/plugins/diffeditor/diffeditoreditable.cpp b/src/plugins/diffeditor/diffeditoreditable.cpp
new file mode 100644
index 00000000000..9f2c9e1a8a3
--- /dev/null
+++ b/src/plugins/diffeditor/diffeditoreditable.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "diffeditoreditable.h"
+#include "diffeditorfile.h"
+#include "diffeditorwidget.h"
+#include "diffeditorconstants.h"
+
+#include <coreplugin/icore.h>
+#include <QCoreApplication>
+#include <QToolButton>
+#include <QSpinBox>
+#include <QStyle>
+#include <QLabel>
+
+namespace DiffEditor {
+
+///////////////////////////////// DiffEditorEditable //////////////////////////////////
+
+DiffEditorEditable::DiffEditorEditable(DiffEditorWidget *editorWidget)
+    : IEditor(0),
+      m_file(new Internal::DiffEditorFile(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE), this)),
+      m_editorWidget(editorWidget),
+      m_toolWidget(0)
+{
+    setWidget(editorWidget);
+}
+
+DiffEditorEditable::~DiffEditorEditable()
+{
+    delete m_toolWidget;
+    if (m_widget)
+        delete m_widget;
+}
+
+bool DiffEditorEditable::createNew(const QString &contents)
+{
+    Q_UNUSED(contents)
+    return true;
+}
+
+bool DiffEditorEditable::open(QString *errorString, const QString &fileName, const QString &realFileName)
+{
+    Q_UNUSED(errorString)
+    Q_UNUSED(fileName)
+    Q_UNUSED(realFileName)
+    return true;
+}
+
+Core::IDocument *DiffEditorEditable::document()
+{
+    return m_file;
+}
+
+QString DiffEditorEditable::displayName() const
+{
+    if (m_displayName.isEmpty())
+        m_displayName = QCoreApplication::translate("DiffEditor", Constants::DIFF_EDITOR_DISPLAY_NAME);
+    return m_displayName;
+}
+
+void DiffEditorEditable::setDisplayName(const QString &title)
+{
+    m_displayName = title;
+    emit changed();
+}
+
+bool DiffEditorEditable::duplicateSupported() const
+{
+    return false;
+}
+
+Core::IEditor *DiffEditorEditable::duplicate(QWidget *parent)
+{
+    Q_UNUSED(parent)
+    return 0;
+}
+
+Core::Id DiffEditorEditable::id() const
+{
+    return Constants::DIFF_EDITOR_ID;
+}
+
+static QToolBar *createToolBar(const QWidget *someWidget)
+{
+    // Create
+    QToolBar *toolBar = new QToolBar;
+    toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+    const int size = someWidget->style()->pixelMetric(QStyle::PM_SmallIconSize);
+    toolBar->setIconSize(QSize(size, size));
+    toolBar->addSeparator();
+
+    return toolBar;
+}
+
+QWidget *DiffEditorEditable::toolBar()
+{
+    if (m_toolWidget)
+        return m_toolWidget;
+
+    // Create
+    m_toolWidget = createToolBar(m_editorWidget);
+
+    QToolButton *whitespaceButton = new QToolButton(m_toolWidget);
+    whitespaceButton->setText(tr("Ignore Whitespaces"));
+    whitespaceButton->setCheckable(true);
+    whitespaceButton->setChecked(true);
+    connect(whitespaceButton, SIGNAL(clicked(bool)),
+            m_editorWidget, SLOT(setIgnoreWhitespaces(bool)));
+    m_toolWidget->addWidget(whitespaceButton);
+
+    QLabel *contextLabel = new QLabel(tr("Context Lines:"), m_toolWidget);
+    m_toolWidget->addWidget(contextLabel);
+
+    QSpinBox *contextSpinBox = new QSpinBox(m_toolWidget);
+    contextSpinBox->setRange(-1, 100);
+    contextSpinBox->setValue(3);
+    connect(contextSpinBox, SIGNAL(valueChanged(int)),
+            m_editorWidget, SLOT(setContextLinesNumber(int)));
+    m_toolWidget->addWidget(contextSpinBox);
+
+    return m_toolWidget;
+}
+
+QByteArray DiffEditorEditable::saveState() const
+{
+    return QByteArray();
+}
+
+bool DiffEditorEditable::restoreState(const QByteArray &state)
+{
+    Q_UNUSED(state)
+    return true;
+}
+
+} // namespace DiffEditor
diff --git a/src/plugins/diffeditor/diffeditoreditable.h b/src/plugins/diffeditor/diffeditoreditable.h
new file mode 100644
index 00000000000..8047fce312a
--- /dev/null
+++ b/src/plugins/diffeditor/diffeditoreditable.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef DIFFEDITOREDITABLE_H
+#define DIFFEDITOREDITABLE_H
+
+#include "diffeditor_global.h"
+
+#include <coreplugin/editormanager/ieditor.h>
+#include <coreplugin/idocument.h>
+
+#include <QToolBar>
+
+namespace DiffEditor {
+
+class DiffEditorWidget;
+
+namespace Internal {
+class DiffEditorFile;
+}
+
+class DIFFEDITOR_EXPORT DiffEditorEditable : public Core::IEditor
+{
+    Q_OBJECT
+public:
+    explicit DiffEditorEditable(DiffEditorWidget *editorWidget);
+    virtual ~DiffEditorEditable();
+
+public:
+    // Core::IEditor
+    bool createNew(const QString &contents);
+    bool open(QString *errorString, const QString &fileName, const QString &realFileName);
+    Core::IDocument *document();
+    QString displayName() const;
+    void setDisplayName(const QString &title);
+    bool duplicateSupported() const;
+    Core::IEditor *duplicate(QWidget *parent);
+    Core::Id id() const;
+    bool isTemporary() const { return true; }
+    DiffEditorWidget *editorWidget() const { return m_editorWidget; }
+
+    QWidget *toolBar();
+
+    QByteArray saveState() const;
+    bool restoreState(const QByteArray &state);
+
+private:
+    Internal::DiffEditorFile *m_file;
+    DiffEditorWidget *m_editorWidget;
+    QToolBar *m_toolWidget;
+    mutable QString m_displayName;
+};
+
+} // namespace DiffEditor
+
+#endif // DIFFEDITOREDITABLE_H
diff --git a/src/plugins/diffeditor/diffeditorfile.cpp b/src/plugins/diffeditor/diffeditorfile.cpp
new file mode 100644
index 00000000000..ede0392bd29
--- /dev/null
+++ b/src/plugins/diffeditor/diffeditorfile.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "diffeditorfile.h"
+
+namespace DiffEditor {
+namespace Internal {
+
+///////////////////////////////// DiffFile //////////////////////////////////
+
+DiffEditorFile::DiffEditorFile(const QString &mimeType, QObject *parent) :
+    Core::IDocument(parent),
+    m_mimeType(mimeType),
+    m_modified(false)
+{
+}
+
+void DiffEditorFile::rename(const QString &newName)
+{
+    Q_UNUSED(newName);
+    return;
+}
+
+void DiffEditorFile::setFileName(const QString &name)
+{
+    if (m_fileName == name)
+        return;
+    m_fileName = name;
+    emit changed();
+}
+
+void DiffEditorFile::setModified(bool modified)
+{
+    if (m_modified == modified)
+        return;
+    m_modified = modified;
+    emit changed();
+}
+
+bool DiffEditorFile::save(QString *errorString, const QString &fileName, bool autoSave)
+{
+    emit saveMe(errorString, fileName, autoSave);
+    if (!errorString->isEmpty())
+        return false;
+    emit changed();
+    return true;
+}
+
+QString DiffEditorFile::mimeType() const
+{
+    return m_mimeType;
+}
+
+Core::IDocument::ReloadBehavior DiffEditorFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
+{
+    Q_UNUSED(state)
+    Q_UNUSED(type)
+    return BehaviorSilent;
+}
+
+bool DiffEditorFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
+{
+    Q_UNUSED(errorString)
+    Q_UNUSED(flag)
+    Q_UNUSED(type)
+    return true;
+}
+
+} // namespace Internal
+} // namespace DiffEditor
diff --git a/src/plugins/diffeditor/diffeditorfile.h b/src/plugins/diffeditor/diffeditorfile.h
new file mode 100644
index 00000000000..4a45fb4aa5c
--- /dev/null
+++ b/src/plugins/diffeditor/diffeditorfile.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef DIFFEDITORFILE_H
+#define DIFFEDITORFILE_H
+
+#include "diffeditor_global.h"
+
+#include <coreplugin/idocument.h>
+
+namespace DiffEditor {
+
+class DiffEditorWidget;
+class DiffEditorFile;
+
+namespace Internal {
+
+class DiffEditorFile : public Core::IDocument
+{
+    Q_OBJECT
+public:
+    explicit DiffEditorFile(const QString &mimeType,
+                              QObject *parent = 0);
+
+    QString fileName() const { return m_fileName; }
+    QString defaultPath() const { return QString(); }
+    QString suggestedFileName() const { return QString(); }
+
+    bool isModified() const { return m_modified; }
+    QString mimeType() const;
+    bool isSaveAsAllowed() const { return false; }
+    bool save(QString *errorString, const QString &fileName, bool autoSave);
+    ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
+    bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
+    void rename(const QString &newName);
+
+    void setFileName(const QString &name);
+    void setModified(bool modified = true);
+
+signals:
+    void saveMe(QString *errorString, const QString &fileName, bool autoSave);
+
+private:
+    const QString m_mimeType;
+    bool m_modified;
+    QString m_fileName;
+};
+
+} // namespace Internal
+} // namespace DiffEditor
+
+#endif // DIFFEDITOREDITABLE_H
diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp
index 30fa36ca83f..a4a1429539a 100644
--- a/src/plugins/diffeditor/diffeditorplugin.cpp
+++ b/src/plugins/diffeditor/diffeditorplugin.cpp
@@ -28,17 +28,14 @@
 ****************************************************************************/
 
 #include "diffeditorplugin.h"
+#include "diffeditoreditable.h"
 #include "diffeditorwidget.h"
 #include "diffeditorconstants.h"
 
-#include <coreplugin/icore.h>
 #include <QCoreApplication>
-#include <QToolButton>
-#include <QSpinBox>
-#include <QStyle>
-#include <QLabel>
 #include <QFileDialog>
 #include <QTextCodec>
+#include <QtPlugin>
 
 #include <coreplugin/actionmanager/actioncontainer.h>
 #include <coreplugin/actionmanager/actionmanager.h>
@@ -46,195 +43,14 @@
 #include <coreplugin/editormanager/editormanager.h>
 
 namespace DiffEditor {
-namespace Internal {
-
-///////////////////////////////// DiffEditor //////////////////////////////////
-
-DiffEditorEditable::DiffEditorEditable(DiffEditorWidget *editorWidget)
-    :
-    IEditor(0),
-    m_file(new DiffFile(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE), this)),
-    m_editorWidget(editorWidget),
-    m_toolWidget(0)
-{
-    setWidget(editorWidget);
-}
-
-DiffEditorEditable::~DiffEditorEditable()
-{
-    delete m_toolWidget;
-    if (m_widget)
-        delete m_widget;
-}
-
-bool DiffEditorEditable::createNew(const QString &contents)
-{
-    Q_UNUSED(contents)
-//    setFileContents(contents);
-    return true;
-}
-
-bool DiffEditorEditable::open(QString *errorString, const QString &fileName, const QString &realFileName)
-{
-    Q_UNUSED(errorString)
-    Q_UNUSED(fileName)
-    Q_UNUSED(realFileName)
-    const QString text = QLatin1String("Open");
-    if (!createNew(text))
-        return false;
-
-    return true;
-}
-
-Core::IDocument *DiffEditorEditable::document()
-{
-    return m_file;
-}
-
-QString DiffEditorEditable::displayName() const
-{
-    if (m_displayName.isEmpty())
-        m_displayName = QCoreApplication::translate("DiffEditor", Constants::DIFF_EDITOR_DISPLAY_NAME);
-    return m_displayName;
-}
-
-void DiffEditorEditable::setDisplayName(const QString &title)
-{
-    m_displayName = title;
-    emit changed();
-}
-
-bool DiffEditorEditable::duplicateSupported() const
-{
-    return false;
-}
-
-Core::IEditor *DiffEditorEditable::duplicate(QWidget * /*parent*/)
-{
-    return 0;
-}
-
-Core::Id DiffEditorEditable::id() const
-{
-    return Constants::DIFF_EDITOR_ID;
-}
-
-static QToolBar *createToolBar(const QWidget *someWidget)
-{
-    // Create
-    QToolBar *toolBar = new QToolBar;
-    toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
-    const int size = someWidget->style()->pixelMetric(QStyle::PM_SmallIconSize);
-    toolBar->setIconSize(QSize(size, size));
-    toolBar->addSeparator();
-
-    return toolBar;
-}
-
-QWidget *DiffEditorEditable::toolBar()
-{
-    if (m_toolWidget)
-        return m_toolWidget;
-
-    // Create
-    m_toolWidget = createToolBar(m_editorWidget);
-
-    QToolButton *whitespaceButton = new QToolButton(m_toolWidget);
-    whitespaceButton->setText(tr("Ignore Whitespaces"));
-    whitespaceButton->setCheckable(true);
-    whitespaceButton->setChecked(true);
-    connect(whitespaceButton, SIGNAL(clicked(bool)),
-            m_editorWidget, SLOT(setIgnoreWhitespaces(bool)));
-    m_toolWidget->addWidget(whitespaceButton);
-
-    QLabel *contextLabel = new QLabel(tr("Context Lines:"), m_toolWidget);
-    m_toolWidget->addWidget(contextLabel);
-
-    QSpinBox *contextSpinBox = new QSpinBox(m_toolWidget);
-    contextSpinBox->setRange(-1, 100);
-    contextSpinBox->setValue(1);
-    connect(contextSpinBox, SIGNAL(valueChanged(int)),
-            m_editorWidget, SLOT(setContextLinesNumber(int)));
-    m_toolWidget->addWidget(contextSpinBox);
-
-    return m_toolWidget;
-}
-
-QByteArray DiffEditorEditable::saveState() const
-{
-    return QByteArray();
-}
-
-bool DiffEditorEditable::restoreState(const QByteArray &/*state*/)
-{
-    return true;
-}
-
-///////////////////////////////// DiffFile //////////////////////////////////
-
-DiffFile::DiffFile(const QString &mimeType, QObject *parent) :
-    Core::IDocument(parent),
-    m_mimeType(mimeType),
-    m_modified(false)
-{
-}
-
-void DiffFile::rename(const QString &newName)
-{
-    Q_UNUSED(newName);
-    return;
-}
-
-void DiffFile::setFileName(const QString &name)
-{
-    if (m_fileName == name)
-        return;
-    m_fileName = name;
-    emit changed();
-}
-
-void DiffFile::setModified(bool modified)
-{
-    if (m_modified == modified)
-        return;
-    m_modified = modified;
-    emit changed();
-}
-
-bool DiffFile::save(QString *errorString, const QString &fileName, bool autoSave)
-{
-    emit saveMe(errorString, fileName, autoSave);
-    if (!errorString->isEmpty())
-        return false;
-    emit changed();
-    return true;
-}
-
-QString DiffFile::mimeType() const
-{
-    return m_mimeType;
-}
-
-Core::IDocument::ReloadBehavior DiffFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
-{
-    Q_UNUSED(state)
-    Q_UNUSED(type)
-    return BehaviorSilent;
-}
-
-bool DiffFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
-{
-    Q_UNUSED(errorString)
-    Q_UNUSED(flag)
-    Q_UNUSED(type)
-    return true;
-}
 
 ///////////////////////////////// DiffEditorFactory //////////////////////////////////
 
-DiffEditorFactory::DiffEditorFactory(DiffEditorPlugin *owner) :
-    m_mimeTypes(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE)),
-    m_owner(owner)
+namespace Internal {
+
+DiffEditorFactory::DiffEditorFactory(DiffEditorPlugin *owner)
+    : m_mimeTypes(QLatin1String(Constants::DIFF_EDITOR_MIMETYPE)),
+      m_owner(owner)
 {
 }
 
@@ -329,9 +145,9 @@ void DiffEditorPlugin::diff()
         const QString text2 = getFileContents(fileName2, editorWidget->codec());
 
         DiffEditorWidget::DiffFilesContents dfc;
-        dfc.leftFileName = fileName1;
+        dfc.leftFileInfo = fileName1;
         dfc.leftText = text1;
-        dfc.rightFileName = fileName2;
+        dfc.rightFileInfo = fileName2;
         dfc.rightText = text2;
         QList<DiffEditorWidget::DiffFilesContents> list;
         list.append(dfc);
diff --git a/src/plugins/diffeditor/diffeditorplugin.h b/src/plugins/diffeditor/diffeditorplugin.h
index cc0de258fd7..39b6e4bf510 100644
--- a/src/plugins/diffeditor/diffeditorplugin.h
+++ b/src/plugins/diffeditor/diffeditorplugin.h
@@ -30,86 +30,22 @@
 #ifndef DIFFEDITORPLUGIN_H
 #define DIFFEDITORPLUGIN_H
 
+#include "diffeditor_global.h"
+
 #include <extensionsystem/iplugin.h>
 #include <coreplugin/editormanager/ieditorfactory.h>
-#include <coreplugin/editormanager/ieditor.h>
 #include <coreplugin/icontext.h>
 #include <coreplugin/idocument.h>
 
-#include <QtPlugin>
-#include <QPointer>
 #include <QStringList>
-#include <QAction>
-#include <QToolBar>
+
+namespace Core { class IEditor; }
 
 namespace DiffEditor {
+
 class DiffEditorWidget;
 
 namespace Internal {
-class DiffFile;
-
-class DiffEditorEditable : public Core::IEditor
-{
-    Q_OBJECT
-public:
-    explicit DiffEditorEditable(DiffEditorWidget *editorWidget);
-    virtual ~DiffEditorEditable();
-
-public:
-    // Core::IEditor
-    bool createNew(const QString &contents);
-    bool open(QString *errorString, const QString &fileName, const QString &realFileName);
-    Core::IDocument *document();
-    QString displayName() const;
-    void setDisplayName(const QString &title);
-    bool duplicateSupported() const;
-    Core::IEditor *duplicate(QWidget *parent);
-    Core::Id id() const;
-    bool isTemporary() const { return true; }
-    DiffEditorWidget *editorWidget() const { return m_editorWidget; }
-
-    QWidget *toolBar();
-
-    QByteArray saveState() const;
-    bool restoreState(const QByteArray &state);
-
-private:
-    DiffFile *m_file;
-    DiffEditorWidget *m_editorWidget;
-    QToolBar *m_toolWidget;
-    mutable QString m_displayName;
-};
-
-class DiffFile : public Core::IDocument
-{
-    Q_OBJECT
-public:
-    explicit DiffFile(const QString &mimeType,
-                              QObject *parent = 0);
-
-    QString fileName() const { return m_fileName; }
-    QString defaultPath() const { return QString(); }
-    QString suggestedFileName() const { return QString(); }
-
-    bool isModified() const { return m_modified; }
-    QString mimeType() const;
-    bool isSaveAsAllowed() const { return false; }
-    bool save(QString *errorString, const QString &fileName, bool autoSave);
-    ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
-    bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
-    void rename(const QString &newName);
-
-    void setFileName(const QString &name);
-    void setModified(bool modified = true);
-
-signals:
-    void saveMe(QString *errorString, const QString &fileName, bool autoSave);
-
-private:
-    const QString m_mimeType;
-    bool m_modified;
-    QString m_fileName;
-};
 
 class DiffEditorPlugin : public ExtensionSystem::IPlugin
 {
diff --git a/src/plugins/diffeditor/diffeditorwidget.cpp b/src/plugins/diffeditor/diffeditorwidget.cpp
index bf6630d6c9d..fd8a43e9589 100644
--- a/src/plugins/diffeditor/diffeditorwidget.cpp
+++ b/src/plugins/diffeditor/diffeditorwidget.cpp
@@ -35,9 +35,7 @@
 #include <QTextBlock>
 #include <QScrollBar>
 #include <QPainter>
-#include <QTime>
-
-#include <QDebug>
+#include <QDir>
 
 #include <texteditor/basetexteditor.h>
 #include <texteditor/snippets/snippeteditor.h>
@@ -54,6 +52,46 @@ using namespace TextEditor;
 
 namespace DiffEditor {
 
+struct TextLineData {
+    enum TextLineType {
+        TextLine,
+        Separator,
+        Invalid
+    };
+    TextLineData() : textLineType(Invalid) {}
+    TextLineData(const QString &txt) : textLineType(TextLine), text(txt) {}
+    TextLineData(TextLineType t) : textLineType(t) {}
+    TextLineType textLineType;
+    QString text;
+};
+
+struct RowData {
+    RowData() : equal(true) {}
+    RowData(const TextLineData &l)
+        : leftLine(l), rightLine(l), equal(true) {}
+    RowData(const TextLineData &l, const TextLineData &r, bool e = false)
+        : leftLine(l), rightLine(r), equal(e) {}
+    TextLineData leftLine;
+    TextLineData rightLine;
+    bool equal; // true if left and right lines are equal, taking whitespaces into account (or both invalid)
+};
+
+struct ChunkData {
+    ChunkData() : contextChunk(false) {}
+    QList<RowData> rows;
+    bool contextChunk;
+    QMap<int, int> changedLeftPositions; // counting from the beginning of the chunk
+    QMap<int, int> changedRightPositions; // counting from the beginning of the chunk
+};
+
+struct FileData {
+    FileData() {}
+    FileData(const ChunkData &chunkData) { chunks.append(chunkData); }
+    QList<ChunkData> chunks;
+    DiffEditorWidget::DiffFileInfo leftFileInfo;
+    DiffEditorWidget::DiffFileInfo rightFileInfo;
+};
+
 //////////////////////
 
 class DiffViewEditorEditable : public BaseTextEditor
@@ -86,12 +124,15 @@ public:
 
     QMap<int, int> skippedLines() const { return m_skippedLines; }
 
+    void setWorkingDirectory(const QString &workingDirectory) { m_workingDirectory = workingDirectory; }
     void setLineNumber(int blockNumber, int lineNumber);
-    void setFileName(int blockNumber, const QString &fileName) { m_fileNames[blockNumber] = fileName; setSeparator(blockNumber, true); }
+    void setFileInfo(int blockNumber, const DiffEditorWidget::DiffFileInfo &fileInfo) { m_fileInfo[blockNumber] = fileInfo; setSeparator(blockNumber, true); }
     void setSkippedLines(int blockNumber, int skippedLines) { m_skippedLines[blockNumber] = skippedLines; setSeparator(blockNumber, true); }
     void setSeparator(int blockNumber, bool separator) { m_separators[blockNumber] = separator; }
-    bool isFileLine(int blockNumber) const { return m_fileNames.contains(blockNumber); }
+    bool isFileLine(int blockNumber) const { return m_fileInfo.contains(blockNumber); }
     bool isChunkLine(int blockNumber) const { return m_skippedLines.contains(blockNumber); }
+    void clearAll();
+    void clearAll(const QString &message);
     void clearAllData();
     QTextBlock firstVisibleBlock() const { return SnippetEditorWidget::firstVisibleBlock(); }
 
@@ -116,10 +157,11 @@ private:
     void paintSeparator(QPainter &painter, const QString &text, const QTextBlock &block, int top);
     void jumpToOriginalFile(const QTextCursor &cursor);
 
+    QString m_workingDirectory;
     QMap<int, int> m_lineNumbers;
     int m_lineNumberDigits;
-    // block number, fileName
-    QMap<int, QString> m_fileNames;
+    // block number, fileInfo
+    QMap<int, DiffEditorWidget::DiffFileInfo> m_fileInfo;
     // block number, skipped lines
     QMap<int, int> m_skippedLines;
     // block number, separator. Separator used as lines alignment and inside skipped lines
@@ -200,11 +242,24 @@ void DiffViewEditorWidget::setLineNumber(int blockNumber, int lineNumber)
     m_lineNumberDigits = qMax(m_lineNumberDigits, lineNumberString.count());
 }
 
+void DiffViewEditorWidget::clearAll()
+{
+    clearAll(tr("No difference"));
+}
+
+void DiffViewEditorWidget::clearAll(const QString &message)
+{
+    setBlockSelection(false);
+    clear();
+    clearAllData();
+    setPlainText(message);
+}
+
 void DiffViewEditorWidget::clearAllData()
 {
     m_lineNumberDigits = 1;
     m_lineNumbers.clear();
-    m_fileNames.clear();
+    m_fileInfo.clear();
     m_skippedLines.clear();
     m_separators.clear();
 }
@@ -253,7 +308,7 @@ void DiffViewEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
 
 void DiffViewEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
 {
-    if (m_fileNames.isEmpty())
+    if (m_fileInfo.isEmpty())
         return;
 
     const int blockNumber = cursor.blockNumber();
@@ -262,10 +317,11 @@ void DiffViewEditorWidget::jumpToOriginalFile(const QTextCursor &cursor)
         return;
 
     const int lineNr = m_lineNumbers.value(blockNumber);
-    QMap<int, QString>::const_iterator it = m_fileNames.upperBound(blockNumber);
-    if (it != m_fileNames.constBegin())
+    QMap<int, DiffEditorWidget::DiffFileInfo>::const_iterator it = m_fileInfo.upperBound(blockNumber);
+    if (it != m_fileInfo.constBegin())
         --it;
-    const QString fileName = it.value();
+    const QDir dir(m_workingDirectory);
+    const QString fileName = dir.absoluteFilePath(it.value().fileName);
 
     Core::IEditor *ed = Core::EditorManager::openEditor(fileName, Core::Id(), Core::EditorManager::ModeSwitch);
     if (TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor *>(ed))
@@ -300,9 +356,12 @@ void DiffViewEditorWidget::paintEvent(QPaintEvent *e)
                     paintSeparator(painter, skippedRowsText, currentBlock, top);
                 }
 
-                const QString fileName = m_fileNames.value(blockNumber);
-                if (!fileName.isEmpty()) {
-                    paintSeparator(painter, fileName, currentBlock, top);
+                const DiffEditorWidget::DiffFileInfo fileInfo = m_fileInfo.value(blockNumber);
+                if (!fileInfo.fileName.isEmpty()) {
+                    const QString fileNameText = fileInfo.typeInfo.isEmpty()
+                            ? fileInfo.fileName
+                            : tr("[%1] %2").arg(fileInfo.typeInfo).arg(fileInfo.fileName);
+                    paintSeparator(painter, fileNameText, currentBlock, top);
                 }
             }
         }
@@ -416,7 +475,7 @@ void DiffViewEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
 
 DiffEditorWidget::DiffEditorWidget(QWidget *parent)
     : QWidget(parent),
-      m_contextLinesNumber(1),
+      m_contextLinesNumber(3),
       m_ignoreWhitespaces(true),
       m_foldingBlocker(false)
 {
@@ -457,6 +516,8 @@ DiffEditorWidget::DiffEditorWidget(QWidget *parent)
     m_splitter->addWidget(m_rightEditor);
     QVBoxLayout *l = new QVBoxLayout(this);
     l->addWidget(m_splitter);
+
+    clear();
 }
 
 DiffEditorWidget::~DiffEditorWidget()
@@ -464,15 +525,29 @@ DiffEditorWidget::~DiffEditorWidget()
 
 }
 
-void DiffEditorWidget::setDiff(const QList<DiffFilesContents> &diffFileList)
+void DiffEditorWidget::clear()
+{
+    m_leftEditor->clearAll();
+    m_rightEditor->clearAll();
+}
+
+void DiffEditorWidget::clear(const QString &message)
+{
+    m_leftEditor->clearAll(message);
+    m_rightEditor->clearAll(message);
+}
+
+void DiffEditorWidget::setDiff(const QList<DiffFilesContents> &diffFileList, const QString &workingDirectory)
 {
+    m_leftEditor->setWorkingDirectory(workingDirectory);
+    m_rightEditor->setWorkingDirectory(workingDirectory);
     Differ differ;
     QList<DiffList> diffList;
     for (int i = 0; i < diffFileList.count(); i++) {
         DiffFilesContents dfc = diffFileList.at(i);
         DiffList dl;
-        dl.leftFileName = dfc.leftFileName;
-        dl.rightFileName = dfc.rightFileName;
+        dl.leftFileInfo = dfc.leftFileInfo;
+        dl.rightFileInfo = dfc.rightFileInfo;
         dl.diffList = differ.cleanupSemantics(differ.diff(dfc.leftText, dfc.rightText));
         diffList.append(dl);
     }
@@ -490,8 +565,8 @@ void DiffEditorWidget::setDiff(const QList<DiffList> &diffList)
         ChunkData chunkData = calculateOriginalData(dl.diffList);
         m_originalChunkData.append(chunkData);
         FileData fileData = calculateContextData(chunkData);
-        fileData.leftFileName = dl.leftFileName;
-        fileData.rightFileName = dl.rightFileName;
+        fileData.leftFileInfo = dl.leftFileInfo;
+        fileData.rightFileInfo = dl.rightFileInfo;
         m_contextFileData.append(fileData);
     }
     showDiff();
@@ -506,8 +581,8 @@ void DiffEditorWidget::setContextLinesNumber(int lines)
     for (int i = 0; i < m_diffList.count(); i++) {
         const FileData oldFileData = m_contextFileData.at(i);
         FileData newFileData = calculateContextData(m_originalChunkData.at(i));
-        newFileData.leftFileName = oldFileData.leftFileName;
-        newFileData.rightFileName = oldFileData.rightFileName;
+        newFileData.leftFileInfo = oldFileData.leftFileInfo;
+        newFileData.rightFileInfo = oldFileData.rightFileInfo;
         m_contextFileData[i] = newFileData;
     }
 
@@ -855,8 +930,8 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c
                 RowData rowData = originalData.rows.at(i);
                 chunkData.rows.append(rowData);
 
-                leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
-                rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
+                leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for '\n'
+                rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for '\n'
                 i++;
             }
             while (leftChangedIt != originalData.changedLeftPositions.constEnd()) {
@@ -889,8 +964,8 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c
                 RowData rowData = originalData.rows.at(i);
                 chunkData.rows.append(rowData);
 
-                leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
-                rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
+                leftCharCounter += rowData.leftLine.text.count() + 1; // +1 for '\n'
+                rightCharCounter += rowData.rightLine.text.count() + 1; // +1 for '\n'
                 i++;
             }
             fileData.chunks.append(chunkData);
@@ -901,21 +976,12 @@ FileData DiffEditorWidget::calculateContextData(const ChunkData &originalData) c
 
 void DiffEditorWidget::showDiff()
 {
-//    QTime time;
-//    time.start();
-
     // TODO: remember the line number of the line in the middle
     const int verticalValue = m_leftEditor->verticalScrollBar()->value();
     const int leftHorizontalValue = m_leftEditor->horizontalScrollBar()->value();
     const int rightHorizontalValue = m_rightEditor->horizontalScrollBar()->value();
 
-    m_leftEditor->setBlockSelection(false);
-    m_rightEditor->setBlockSelection(false);
-    m_leftEditor->clear();
-    m_rightEditor->clear();
-    m_leftEditor->clearAllData();
-    m_rightEditor->clearAllData();
-//    int ela1 = time.elapsed();
+    clear();
 
     QString leftText, rightText;
     int blockNumber = 0;
@@ -925,8 +991,8 @@ void DiffEditorWidget::showDiff()
 
         int leftLineNumber = 0;
         int rightLineNumber = 0;
-        m_leftEditor->setFileName(blockNumber, contextFileData.leftFileName);
-        m_rightEditor->setFileName(blockNumber, contextFileData.rightFileName);
+        m_leftEditor->setFileInfo(blockNumber, contextFileData.leftFileInfo);
+        m_rightEditor->setFileInfo(blockNumber, contextFileData.rightFileInfo);
         leftText += separator;
         rightText += separator;
         blockNumber++;
@@ -967,13 +1033,12 @@ void DiffEditorWidget::showDiff()
             }
         }
     }
-//    int ela2 = time.elapsed();
+
+    if (leftText.isEmpty() && rightText.isEmpty())
+        return;
 
     m_leftEditor->setPlainText(leftText);
     m_rightEditor->setPlainText(rightText);
-//    int ela3 = time.elapsed();
-
-//    int ela4 = time.elapsed();
 
     colorDiff(m_contextFileData);
 
@@ -1029,14 +1094,10 @@ void DiffEditorWidget::showDiff()
     }
     m_foldingBlocker = false;
 
-//    int ela5 = time.elapsed();
-
     m_leftEditor->verticalScrollBar()->setValue(verticalValue);
     m_rightEditor->verticalScrollBar()->setValue(verticalValue);
     m_leftEditor->horizontalScrollBar()->setValue(leftHorizontalValue);
     m_rightEditor->horizontalScrollBar()->setValue(rightHorizontalValue);
-//    int ela6 = time.elapsed();
-//    qDebug() << ela1 << ela2 << ela3 << ela4 << ela5 << ela6;
     m_leftEditor->updateFoldingHighlight(QPoint(-1, -1));
     m_rightEditor->updateFoldingHighlight(QPoint(-1, -1));
 }
@@ -1099,7 +1160,6 @@ void DiffEditorWidget::colorDiff(const QList<FileData> &fileDataList)
 
     int leftPos = 0;
     int rightPos = 0;
-    // startPos, endPos
     QMap<int, int> leftLinePos;
     QMap<int, int> rightLinePos;
     QMap<int, int> leftCharPos;
@@ -1154,8 +1214,8 @@ void DiffEditorWidget::colorDiff(const QList<FileData> &fileDataList)
             for (int k = 0; k < chunkData.rows.count(); k++) {
                 RowData rowData = chunkData.rows.at(k);
 
-                leftPos += rowData.leftLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
-                rightPos += rowData.rightLine.text.count() + 1; // +1 for separator or for '\n', each line has one of it
+                leftPos += rowData.leftLine.text.count() + 1; // +1 for '\n'
+                rightPos += rowData.rightLine.text.count() + 1; // +1 for '\n'
 
                 if (!rowData.equal) {
                     if (rowData.leftLine.textLineType == TextLineData::TextLine) {
diff --git a/src/plugins/diffeditor/diffeditorwidget.h b/src/plugins/diffeditor/diffeditorwidget.h
index 17b51074857..b4e7fe83b4b 100644
--- a/src/plugins/diffeditor/diffeditorwidget.h
+++ b/src/plugins/diffeditor/diffeditorwidget.h
@@ -52,59 +52,26 @@ QT_END_NAMESPACE
 namespace DiffEditor {
 
 class DiffViewEditorWidget;
-
-struct TextLineData {
-    enum TextLineType {
-        TextLine,
-        Separator,
-        Invalid
-    };
-    TextLineData() : textLineType(Invalid) {}
-    TextLineData(const QString &txt) : textLineType(TextLine), text(txt) {}
-    TextLineData(TextLineType t) : textLineType(t) {}
-    TextLineType textLineType;
-    QString text;
-};
-
-struct RowData {
-    RowData() : equal(true) {}
-    RowData(const TextLineData &l)
-        : leftLine(l), rightLine(l), equal(true) {}
-    RowData(const TextLineData &l, const TextLineData &r, bool e = false)
-        : leftLine(l), rightLine(r), equal(e) {}
-    TextLineData leftLine;
-    TextLineData rightLine;
-    bool equal; // true if left and right lines are equal, taking whitespaces into account (or both invalid)
-};
-
-struct ChunkData {
-    ChunkData() : contextChunk(false) {}
-    QList<RowData> rows;
-    bool contextChunk;
-    QMap<int, int> changedLeftPositions; // counting from the beginning of the chunk
-    QMap<int, int> changedRightPositions; // counting from the beginning of the chunk
-};
-
-struct FileData {
-    FileData() {}
-    FileData(const ChunkData &chunkData) { chunks.append(chunkData); }
-    QList<ChunkData> chunks;
-    QString leftFileName;
-    QString rightFileName;
-};
-
-struct DiffData {
-    QList<FileData> files;
-};
+struct TextLineData;
+struct ChunkData;
+struct FileData;
 
 class DIFFEDITOR_EXPORT DiffEditorWidget : public QWidget
 {
     Q_OBJECT
 public:
+    struct DiffFileInfo {
+        DiffFileInfo() {}
+        DiffFileInfo(const QString &file) : fileName(file) {}
+        DiffFileInfo(const QString &file, const QString &type) : fileName(file), typeInfo(type) {}
+        QString fileName;
+        QString typeInfo;
+    };
+
     struct DiffFilesContents {
-        QString leftFileName;
+        DiffFileInfo leftFileInfo;
         QString leftText;
-        QString rightFileName;
+        DiffFileInfo rightFileInfo;
         QString rightText;
     };
 
@@ -112,7 +79,8 @@ public:
     ~DiffEditorWidget();
 
     void clear();
-    void setDiff(const QList<DiffFilesContents> &diffFileList);
+    void clear(const QString &message);
+    void setDiff(const QList<DiffFilesContents> &diffFileList, const QString &workingDirectory = QString());
     QTextCodec *codec() const;
 
 public slots:
@@ -131,8 +99,8 @@ private slots:
 
 private:
     struct DiffList {
-        QString leftFileName;
-        QString rightFileName;
+        DiffFileInfo leftFileInfo;
+        DiffFileInfo rightFileInfo;
         QList<Diff> diffList;
     };
 
diff --git a/src/plugins/git/git.qbs b/src/plugins/git/git.qbs
index 95fa416ea2e..80acf97b6ed 100644
--- a/src/plugins/git/git.qbs
+++ b/src/plugins/git/git.qbs
@@ -10,6 +10,7 @@ QtcPlugin {
     Depends { name: "TextEditor" }
     Depends { name: "Find" }
     Depends { name: "VcsBase" }
+    Depends { name: "DiffEditor" }
     Depends { name: "Locator" }
 
     files: [
diff --git a/src/plugins/git/git_dependencies.pri b/src/plugins/git/git_dependencies.pri
index fccc00210e0..e21fdc7299d 100644
--- a/src/plugins/git/git_dependencies.pri
+++ b/src/plugins/git/git_dependencies.pri
@@ -5,4 +5,5 @@ QTC_PLUGIN_DEPENDS += \
     projectexplorer \
     texteditor \
     coreplugin \
-    vcsbase
+    vcsbase \
+    diffeditor
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 7ed91ff072f..134ab12030a 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -56,6 +56,10 @@
 #include <vcsbase/vcsbaseoutputwindow.h>
 #include <vcsbase/vcsbaseplugin.h>
 
+#include <diffeditor/diffeditorwidget.h>
+#include <diffeditor/diffeditoreditable.h>
+#include <diffeditor/diffeditorconstants.h>
+
 #include <QCoreApplication>
 #include <QDir>
 #include <QFileInfo>
@@ -75,6 +79,224 @@ static const char graphLogFormatC[] = "%h %d %an %s %ci";
 namespace Git {
 namespace Internal {
 
+class GitDiffHandler : public QObject
+{
+    Q_OBJECT
+
+public:
+    GitDiffHandler(const QString &gitPath,
+                   const QString &workingDirectory,
+                   const QProcessEnvironment &environment,
+                   DiffEditor::DiffEditorWidget *editor,
+                   int timeout);
+
+    // index -> working tree
+    void diffFile(const QString &fileName);
+    // stagedFileNames - files in index, diff will compare the state in HEAD to the one in the index
+    // unstagedFileNames - diff will compare the state in the index to the one in the working tree
+    void diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames);
+    // index -> working tree
+    void diffProjects(const QStringList &projectPaths);
+    // index -> working tree
+    void diffRepository();
+
+private slots:
+    void slotFileListReceived(const QByteArray &data);
+    void slotFileContentsReceived(const QByteArray &data);
+
+private:
+    void collectFilesList(const QStringList &additionalArguments);
+    void collectFilesContents();
+    void feedEditor();
+    QString workingTreeContents(const QString &fileName) const;
+
+    const QString m_gitPath;
+    const QString m_workingDirectory;
+    const QProcessEnvironment m_processEnvironment;
+    QWeakPointer<DiffEditor::DiffEditorWidget> m_editor;
+    const int m_timeout;
+    const QString m_waitMessage;
+
+    enum RevisionType {
+        Head        = 0x01,
+        Index       = 0x02,
+        WorkingTree = 0x04
+    };
+
+    QStringList m_requestedHeadFileNames;
+    QStringList m_requestedIndexFileNames;
+
+    QStringList m_headFileNames;
+    QStringList m_indexFileNames;
+
+    QStringList m_headContents;
+    QStringList m_indexContents;
+};
+
+GitDiffHandler::GitDiffHandler(const QString &gitPath,
+               const QString &workingDirectory,
+               const QProcessEnvironment &environment,
+               DiffEditor::DiffEditorWidget *editor,
+               int timeout)
+    : m_gitPath(gitPath),
+      m_workingDirectory(workingDirectory),
+      m_processEnvironment(environment),
+      m_editor(editor),
+      m_timeout(timeout),
+      m_waitMessage(tr("Waiting for data..."))
+{
+}
+
+void GitDiffHandler::diffFile(const QString &fileName)
+{
+    m_requestedIndexFileNames << fileName;
+    collectFilesList(QStringList() << QLatin1String("--") << m_requestedIndexFileNames);
+}
+
+void GitDiffHandler::diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames)
+{
+    m_requestedHeadFileNames = stagedFileNames;
+    m_requestedHeadFileNames.removeDuplicates();
+    m_requestedIndexFileNames = unstagedFileNames;
+    m_requestedIndexFileNames.removeDuplicates();
+
+    m_headFileNames = m_requestedHeadFileNames;
+    m_indexFileNames = m_requestedIndexFileNames;
+    for (int i = 0; i < m_headFileNames.count(); i++) {
+        const QString headFileName = m_headFileNames.at(i);
+        if (!m_indexFileNames.contains(headFileName))
+            m_indexFileNames.append(headFileName);
+    }
+    collectFilesContents();
+}
+
+void GitDiffHandler::diffProjects(const QStringList &projectPaths)
+{
+    collectFilesList(QStringList() << QLatin1String("--") << projectPaths);
+}
+
+void GitDiffHandler::diffRepository()
+{
+    collectFilesList(QStringList());
+}
+
+void GitDiffHandler::collectFilesList(const QStringList &additionalArguments)
+{
+    m_editor.data()->clear(m_waitMessage);
+    VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
+    connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileListReceived(QByteArray)));
+    QStringList arguments;
+    arguments << QLatin1String("diff") << QLatin1String("--name-only") << additionalArguments;
+    command->addJob(arguments, m_timeout);
+    command->execute();
+}
+
+void GitDiffHandler::slotFileListReceived(const QByteArray &data)
+{
+    if (m_editor.isNull())
+        return;
+
+    const QString fileList = m_editor.data()->codec()->toUnicode(data);
+    m_requestedIndexFileNames = fileList.split(QLatin1Char('\n'), QString::SkipEmptyParts);
+    m_requestedIndexFileNames.removeDuplicates();
+    m_indexFileNames = m_requestedIndexFileNames;
+
+    collectFilesContents();
+}
+
+void GitDiffHandler::collectFilesContents()
+{
+    const int headFilesReceived = m_headContents.count();
+    const int indexFilesReceived = m_indexContents.count();
+
+    if (headFilesReceived < m_headFileNames.count()) {
+        VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
+        connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileContentsReceived(QByteArray)));
+
+        QStringList arguments;
+        arguments << QLatin1String("show") << QLatin1String("HEAD:./") + m_headFileNames.at(headFilesReceived);
+        command->addJob(arguments, m_timeout);
+        command->execute();
+    } else if (indexFilesReceived < m_indexFileNames.count()) {
+        VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
+        connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileContentsReceived(QByteArray)));
+
+        QStringList arguments;
+        arguments << QLatin1String("show") << QLatin1String(":./") + m_indexFileNames.at(indexFilesReceived);
+        command->addJob(arguments, m_timeout);
+        command->execute();
+    } else {
+        feedEditor();
+    }
+}
+
+void GitDiffHandler::slotFileContentsReceived(const QByteArray &data)
+{
+    if (m_editor.isNull())
+        return;
+
+    const int headFilesReceived = m_headContents.count();
+    const int indexFilesReceived = m_indexContents.count();
+    const QString contents = m_editor.data()->codec()->toUnicode(data);
+    if (headFilesReceived < m_headFileNames.count())
+        m_headContents.append(contents);
+    else if (indexFilesReceived < m_indexFileNames.count())
+        m_indexContents.append(contents);
+
+    collectFilesContents();
+}
+
+void GitDiffHandler::feedEditor()
+{
+    QList<DiffEditor::DiffEditorWidget::DiffFilesContents> list;
+
+    for (int i = 0; i < m_requestedHeadFileNames.count(); i++) {
+        const QString fileName = m_requestedHeadFileNames.at(i);
+        const QString original = m_headContents.at(i);
+        const int idx = m_indexFileNames.indexOf(fileName);
+        if (idx >= 0) {
+            const QString modified = m_indexContents.at(idx);
+            if (original != modified) {
+                DiffEditor::DiffEditorWidget::DiffFilesContents dfc;
+                dfc.leftFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Head"));
+                dfc.leftText = original;
+                dfc.rightFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Index"));
+                dfc.rightText = modified;
+                list.append(dfc);
+            }
+        }
+    }
+    for (int i = 0; i < m_requestedIndexFileNames.count(); i++) {
+        const QString fileName = m_requestedIndexFileNames.at(i);
+        const QString original = m_indexContents.at(i);
+        const QString modified = workingTreeContents(fileName);
+        if (original != modified) {
+            DiffEditor::DiffEditorWidget::DiffFilesContents dfc;
+            dfc.leftFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Index"));
+            dfc.leftText = original;
+            dfc.rightFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Working tree"));
+            dfc.rightText = modified;
+            list.append(dfc);
+        }
+    }
+    m_editor.data()->setDiff(list, m_workingDirectory);
+    deleteLater();
+}
+
+QString GitDiffHandler::workingTreeContents(const QString &fileName) const
+{
+    QDir workingDir(m_workingDirectory);
+    QString absoluteFileName = workingDir.absoluteFilePath(fileName);
+
+    QFile file(absoluteFileName);
+    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        return m_editor.data()->codec()->toUnicode(file.readAll());
+    }
+    return QString();
+}
+
+///////////////////////////////////////////////////////////
+
 class BaseGitDiffArgumentsWidget : public VcsBase::VcsBaseEditorParameterWidget
 {
     Q_OBJECT
@@ -506,6 +728,22 @@ VcsBase::VcsBaseEditorWidget *GitClient::findExistingVCSEditor(const char *regis
     return rc;
 }
 
+DiffEditor::DiffEditorWidget *GitClient::findExistingDiffEditor(const char *registerDynamicProperty,
+                                                      const QString &dynamicPropertyValue) const
+{
+    DiffEditor::DiffEditorWidget *editorWidget = 0;
+    Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
+    if (!outputEditor)
+        return 0;
+
+    // Exists already
+    Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
+    outputEditor->createNew(m_msgWait);
+    editorWidget = diffEditorWidget(outputEditor);
+
+    return editorWidget;
+}
+
 /* 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
@@ -549,94 +787,153 @@ VcsBase::VcsBaseEditorWidget *GitClient::createVcsEditor(const Core::Id &id,
     return rc;
 }
 
+DiffEditor::DiffEditorWidget *GitClient::diffEditorWidget(const Core::IEditor *editor) const
+{
+    if (const DiffEditor::DiffEditorEditable *de = qobject_cast<const DiffEditor::DiffEditorEditable *>(editor))
+        return de->editorWidget();
+    return 0;
+}
+
 void GitClient::diff(const QString &workingDirectory,
                      const QStringList &diffArgs,
                      const QStringList &unstagedFileNames,
                      const QStringList &stagedFileNames)
 {
-    const QString binary = settings()->stringValue(GitSettings::binaryPathKey);
-    const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
-    const QString title = tr("Git Diff");
-
-    VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", workingDirectory);
-    if (!editor) {
-        GitCommitDiffArgumentsWidget *argWidget =
-                new GitCommitDiffArgumentsWidget(this, workingDirectory, diffArgs,
-                                                 unstagedFileNames, stagedFileNames);
+    if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
+        const Core::Id editorId = DiffEditor::Constants::DIFF_EDITOR_ID;
+        QString title = tr("Git Diff");
 
-        editor = createVcsEditor(editorId, title,
-                                 workingDirectory, CodecSource, "originalFileName", workingDirectory, argWidget);
-        connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand()));
-    }
-
-    GitCommitDiffArgumentsWidget *argWidget = qobject_cast<GitCommitDiffArgumentsWidget *>(editor->configurationWidget());
-    QStringList userDiffArgs = argWidget->arguments();
-    editor->setDiffBaseDirectory(workingDirectory);
+        DiffEditor::DiffEditorWidget *editorWidget = findExistingDiffEditor("originalFileName", workingDirectory);
 
-    // Create a batch of 2 commands to be run after each other in case
-    // we have a mixture of staged/unstaged files as is the case
-    // when using the submit dialog.
-    VcsBase::Command *command = createCommand(workingDirectory, editor);
-    // Directory diff?
+        if (!editorWidget) {
+            Core::IEditor *outputEditor = Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait);
+            outputEditor->document()->setProperty("originalFileName", workingDirectory);
+            Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch); // should probably go outside this block
 
-    QStringList cmdArgs;
-    cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption);
+            editorWidget = diffEditorWidget(outputEditor);
+        }
 
-    int timeout = settings()->intValue(GitSettings::timeoutKey);
+        int timeout = settings()->intValue(GitSettings::timeoutKey);
+        GitDiffHandler *handler = new GitDiffHandler(gitBinaryPath(), workingDirectory, processEnvironment(), editorWidget, timeout);
 
-    if (unstagedFileNames.empty() && stagedFileNames.empty()) {
-       QStringList arguments(cmdArgs);
-       arguments << userDiffArgs;
-       outputWindow()->appendCommand(workingDirectory, binary, arguments);
-       command->addJob(arguments, timeout);
+        if (unstagedFileNames.empty() && stagedFileNames.empty()) {
+            // local repository diff
+            handler->diffRepository();
+        } else {
+            if (!stagedFileNames.empty()) {
+                // diff of selected files only with --cached option, used in commit editor
+                handler->diffFiles(stagedFileNames, unstagedFileNames);
+            } else if (!unstagedFileNames.empty()) {
+                // current project diff
+                handler->diffProjects(unstagedFileNames);
+            }
+        }
     } else {
-        // Files diff.
-        if (!unstagedFileNames.empty()) {
-           QStringList arguments(cmdArgs);
-           arguments << userDiffArgs;
-           arguments << QLatin1String("--") << unstagedFileNames;
-           outputWindow()->appendCommand(workingDirectory, binary, arguments);
-           command->addJob(arguments, timeout);
+        const QString binary = settings()->stringValue(GitSettings::binaryPathKey);
+        const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
+        const QString title = tr("Git Diff");
+
+        VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", workingDirectory);
+        if (!editor) {
+            GitCommitDiffArgumentsWidget *argWidget =
+                    new GitCommitDiffArgumentsWidget(this, workingDirectory, diffArgs,
+                                                     unstagedFileNames, stagedFileNames);
+
+            editor = createVcsEditor(editorId, title,
+                                     workingDirectory, CodecSource, "originalFileName", workingDirectory, argWidget);
+            connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand()));
         }
-        if (!stagedFileNames.empty()) {
-           QStringList arguments(cmdArgs);
-           arguments << userDiffArgs;
-           arguments << QLatin1String("--cached") << diffArgs << QLatin1String("--") << stagedFileNames;
-           outputWindow()->appendCommand(workingDirectory, binary, arguments);
-           command->addJob(arguments, timeout);
+
+        GitCommitDiffArgumentsWidget *argWidget = qobject_cast<GitCommitDiffArgumentsWidget *>(editor->configurationWidget());
+        QStringList userDiffArgs = argWidget->arguments();
+        editor->setDiffBaseDirectory(workingDirectory);
+
+        // Create a batch of 2 commands to be run after each other in case
+        // we have a mixture of staged/unstaged files as is the case
+        // when using the submit dialog.
+        VcsBase::Command *command = createCommand(workingDirectory, editor);
+        // Directory diff?
+
+        QStringList cmdArgs;
+        cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption);
+
+        int timeout = settings()->intValue(GitSettings::timeoutKey);
+
+        if (unstagedFileNames.empty() && stagedFileNames.empty()) {
+            QStringList arguments(cmdArgs);
+            arguments << userDiffArgs;
+            outputWindow()->appendCommand(workingDirectory, binary, arguments);
+            command->addJob(arguments, timeout);
+        } else {
+            // Files diff.
+            if (!unstagedFileNames.empty()) {
+                QStringList arguments(cmdArgs);
+                arguments << userDiffArgs;
+                arguments << QLatin1String("--") << unstagedFileNames;
+                outputWindow()->appendCommand(workingDirectory, binary, arguments);
+                command->addJob(arguments, timeout);
+            }
+            if (!stagedFileNames.empty()) {
+                QStringList arguments(cmdArgs);
+                arguments << userDiffArgs;
+                arguments << QLatin1String("--cached") << diffArgs << QLatin1String("--") << stagedFileNames;
+                outputWindow()->appendCommand(workingDirectory, binary, arguments);
+                command->addJob(arguments, timeout);
+            }
         }
+        command->execute();
     }
-    command->execute();
 }
 
 void GitClient::diff(const QString &workingDirectory,
                      const QStringList &diffArgs,
                      const QString &fileName)
 {
-    const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
-    const QString title = tr("Git Diff \"%1\"").arg(fileName);
-    const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
+    if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
+        const Core::Id editorId = DiffEditor::Constants::DIFF_EDITOR_ID;
+        QString title = tr("Git Diff \"%1\"").arg(fileName);
+        const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
 
-    VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", sourceFile);
-    if (!editor) {
-        GitFileDiffArgumentsWidget *argWidget =
-                new GitFileDiffArgumentsWidget(this, workingDirectory, diffArgs, fileName);
+        DiffEditor::DiffEditorWidget *editorWidget = findExistingDiffEditor("originalFileName", sourceFile);
+        if (!editorWidget) {
+            Core::IEditor *outputEditor = Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait);
+            outputEditor->document()->setProperty("originalFileName", sourceFile);
+            Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
 
-        editor = createVcsEditor(editorId, title, sourceFile, CodecSource, "originalFileName", sourceFile, argWidget);
-        connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand()));
-    }
-    editor->setDiffBaseDirectory(workingDirectory);
+            editorWidget = diffEditorWidget(outputEditor);
+        }
 
-    GitFileDiffArgumentsWidget *argWidget = qobject_cast<GitFileDiffArgumentsWidget *>(editor->configurationWidget());
-    QStringList userDiffArgs = argWidget->arguments();
+        if (!fileName.isEmpty()) {
+            int timeout = settings()->intValue(GitSettings::timeoutKey);
+            GitDiffHandler *handler = new GitDiffHandler(gitBinaryPath(), workingDirectory, processEnvironment(), editorWidget, timeout);
+            handler->diffFile(fileName);
+        }
+    } else {
+        const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
+        const QString title = tr("Git Diff \"%1\"").arg(fileName);
+        const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
 
-    QStringList cmdArgs;
-    cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption)
-              << userDiffArgs;
+        VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", sourceFile);
+        if (!editor) {
+            GitFileDiffArgumentsWidget *argWidget =
+                    new GitFileDiffArgumentsWidget(this, workingDirectory, diffArgs, fileName);
 
-    if (!fileName.isEmpty())
-        cmdArgs << QLatin1String("--") << fileName;
-    executeGit(workingDirectory, cmdArgs, editor);
+            editor = createVcsEditor(editorId, title, sourceFile, CodecSource, "originalFileName", sourceFile, argWidget);
+            connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand()));
+        }
+        editor->setDiffBaseDirectory(workingDirectory);
+
+        GitFileDiffArgumentsWidget *argWidget = qobject_cast<GitFileDiffArgumentsWidget *>(editor->configurationWidget());
+        QStringList userDiffArgs = argWidget->arguments();
+
+        QStringList cmdArgs;
+        cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption)
+                << userDiffArgs;
+
+        if (!fileName.isEmpty())
+            cmdArgs << QLatin1String("--") << fileName;
+        executeGit(workingDirectory, cmdArgs, editor);
+    }
 }
 
 void GitClient::diffBranch(const QString &workingDirectory,
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index b02ed7cffb3..003cf6b92cd 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -60,11 +60,16 @@ namespace Utils {
     struct SynchronousProcessResponse;
 }
 
+namespace DiffEditor {
+    class DiffEditorWidget;
+}
+
 namespace Git {
 namespace Internal {
 
 class GitPlugin;
 class GitOutputWindow;
+class GitDiffEditorWidget;
 class CommitData;
 struct GitSubmitEditorPanelData;
 class Stash;
@@ -128,6 +133,8 @@ public:
     QString findRepositoryForDirectory(const QString &dir);
     QString findGitDirForRepository(const QString &repositoryDir) const;
 
+    DiffEditor::DiffEditorWidget *diffEditorWidget(const Core::IEditor *editor) const;
+
     void diff(const QString &workingDirectory, const QStringList &diffArgs, const QString &fileName);
     void diff(const QString &workingDirectory, const QStringList &diffArgs,
               const QStringList &unstagedFileNames, const QStringList &stagedFileNames= QStringList());
@@ -319,6 +326,9 @@ private:
     QTextCodec *getSourceCodec(const QString &file) const;
     VcsBase::VcsBaseEditorWidget *findExistingVCSEditor(const char *registerDynamicProperty,
                                                   const QString &dynamicPropertyValue) const;
+    DiffEditor::DiffEditorWidget *findExistingDiffEditor(const char *registerDynamicProperty,
+                                               const QString &dynamicPropertyValue) const;
+
     enum CodecType { CodecSource, CodecLogOutput, CodecNone };
     VcsBase::VcsBaseEditorWidget *createVcsEditor(const Core::Id &kind,
                                             QString title,
diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp
index 1b4d3e3fd64..fb6bea95f9a 100644
--- a/src/plugins/git/giteditor.cpp
+++ b/src/plugins/git/giteditor.cpp
@@ -179,6 +179,8 @@ void GitEditor::setPlainTextDataFiltered(const QByteArray &a)
         break;
     }
     case VcsBase::DiffOutput: {
+        if (array.isEmpty())
+            array = QByteArray("No difference to HEAD");
         const QFileInfo fi(source());
         const QString workingDirectory = fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath();
         QByteArray precedes, follows;
diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp
index 2c1e27fc8ac..7bacca13070 100644
--- a/src/plugins/git/gitsettings.cpp
+++ b/src/plugins/git/gitsettings.cpp
@@ -35,6 +35,7 @@
 namespace Git {
 namespace Internal {
 
+const QLatin1String GitSettings::useDiffEditorKey("UseDiffEditor");
 const QLatin1String GitSettings::pullRebaseKey("PullRebase");
 const QLatin1String GitSettings::showTagsKey("ShowTags");
 const QLatin1String GitSettings::omitAnnotationDateKey("OmitAnnotationDate");
@@ -54,6 +55,7 @@ GitSettings::GitSettings()
 
     declareKey(binaryPathKey, QLatin1String("git"));
     declareKey(timeoutKey, Utils::HostOsInfo::isWindowsHost() ? 60 : 30);
+    declareKey(useDiffEditorKey, true);
     declareKey(pullRebaseKey, false);
     declareKey(showTagsKey, false);
     declareKey(omitAnnotationDateKey, false);
diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h
index eaf01280175..dcf5fd940e4 100644
--- a/src/plugins/git/gitsettings.h
+++ b/src/plugins/git/gitsettings.h
@@ -48,6 +48,7 @@ class GitSettings : public VcsBase::VcsBaseClientSettings
 public:
     GitSettings();
 
+    static const QLatin1String useDiffEditorKey;
     static const QLatin1String pullRebaseKey;
     static const QLatin1String showTagsKey;
     static const QLatin1String omitAnnotationDateKey;
diff --git a/src/plugins/git/settingspage.cpp b/src/plugins/git/settingspage.cpp
index a76d07bc2cc..08b4875e44c 100644
--- a/src/plugins/git/settingspage.cpp
+++ b/src/plugins/git/settingspage.cpp
@@ -70,6 +70,7 @@ GitSettings SettingsPageWidget::settings() const
     rc.setValue(GitSettings::pathKey, m_ui.pathLineEdit->text());
     rc.setValue(GitSettings::logCountKey, m_ui.logCountSpinBox->value());
     rc.setValue(GitSettings::timeoutKey, m_ui.timeoutSpinBox->value());
+    rc.setValue(GitSettings::useDiffEditorKey, m_ui.useDiffEditorCheckBox->isChecked());
     rc.setValue(GitSettings::pullRebaseKey, m_ui.pullRebaseCheckBox->isChecked());
     rc.setValue(GitSettings::showTagsKey, m_ui.showTagsCheckBox->isChecked());
     rc.setValue(GitSettings::promptOnSubmitKey, m_ui.promptToSubmitCheckBox->isChecked());
@@ -84,6 +85,7 @@ void SettingsPageWidget::setSettings(const GitSettings &s)
     m_ui.pathLineEdit->setText(s.stringValue(GitSettings::pathKey));
     m_ui.logCountSpinBox->setValue(s.intValue(GitSettings::logCountKey));
     m_ui.timeoutSpinBox->setValue(s.intValue(GitSettings::timeoutKey));
+    m_ui.useDiffEditorCheckBox->setChecked(s.boolValue(GitSettings::useDiffEditorKey));
     m_ui.pullRebaseCheckBox->setChecked(s.boolValue(GitSettings::pullRebaseKey));
     m_ui.showTagsCheckBox->setChecked(s.boolValue(GitSettings::showTagsKey));
     m_ui.promptToSubmitCheckBox->setChecked(s.boolValue(GitSettings::promptOnSubmitKey));
diff --git a/src/plugins/git/settingspage.ui b/src/plugins/git/settingspage.ui
index d67a59369ea..881ae3570a4 100644
--- a/src/plugins/git/settingspage.ui
+++ b/src/plugins/git/settingspage.ui
@@ -137,6 +137,13 @@
         </property>
        </widget>
       </item>
+      <item row="2" column="3">
+       <widget class="QCheckBox" name="useDiffEditorCheckBox">
+        <property name="text">
+         <string>Use Diff Editor</string>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
diff --git a/src/plugins/vcsbase/command.cpp b/src/plugins/vcsbase/command.cpp
index 556f86515f6..d6edff387ad 100644
--- a/src/plugins/vcsbase/command.cpp
+++ b/src/plugins/vcsbase/command.cpp
@@ -271,19 +271,13 @@ void Command::run()
         }
     }
 
-    // Special hack: Always produce output for diff
-    if (ok && stdOut.isEmpty() && d->m_jobs.front().arguments.at(0) == QLatin1String("diff")) {
-        stdOut += "No difference to HEAD";
-    } else {
-        // @TODO: Remove, see below
-        if (ok && d->m_jobs.front().arguments.at(0) == QLatin1String("status"))
-            removeColorCodes(&stdOut);
-    }
+    if (ok && d->m_jobs.front().arguments.at(0) == QLatin1String("status"))
+        removeColorCodes(&stdOut);
 
     d->m_lastExecSuccess = ok;
     d->m_lastExecExitCode = exitCode;
 
-    if (ok && !stdOut.isEmpty())
+    if (ok)
         emit outputData(stdOut);
 
     if (!error.isEmpty())
-- 
GitLab