diff --git a/src/plugins/git/git.pro b/src/plugins/git/git.pro
index fb887a2e55e559d31c13d34b6de66c6d6dda9f0a..873ad6bacd8274daac73b7f4c68a1f59470488a7 100644
--- a/src/plugins/git/git.pro
+++ b/src/plugins/git/git.pro
@@ -28,7 +28,8 @@ HEADERS += gitplugin.h \
     remotemodel.h \
     remotedialog.h \
     branchadddialog.h \
-    resetdialog.h
+    resetdialog.h \
+    mergetool.h
 
 SOURCES += gitplugin.cpp \
     gitclient.cpp \
@@ -50,7 +51,8 @@ SOURCES += gitplugin.cpp \
     remotemodel.cpp \
     remotedialog.cpp \
     branchadddialog.cpp \
-    resetdialog.cpp
+    resetdialog.cpp \
+    mergetool.cpp
 
 FORMS += changeselectiondialog.ui \
     settingspage.ui \
diff --git a/src/plugins/git/git.qbs b/src/plugins/git/git.qbs
index e11a830c964ced94df126d5dbb30a0d50cbd151c..ec5a0af2927b8e37f7956f55a7bc3ebba5e492c7 100644
--- a/src/plugins/git/git.qbs
+++ b/src/plugins/git/git.qbs
@@ -54,6 +54,8 @@ QtcPlugin {
         "gitutils.h",
         "gitversioncontrol.cpp",
         "gitversioncontrol.h",
+        "mergetool.cpp",
+        "mergetool.h",
         "remoteadditiondialog.ui",
         "remotedialog.cpp",
         "remotedialog.h",
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 4ccdb0132664cbefb0827ce1d9131f5595ff4d5a..61747141ed12215001bf0322a9046985da0b506b 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -35,6 +35,7 @@
 #include "gitplugin.h"
 #include "gitsubmiteditor.h"
 #include "gitversioncontrol.h"
+#include "mergetool.h"
 
 #include <vcsbase/submitfilemodel.h>
 
@@ -572,6 +573,13 @@ void GitClient::diffBranch(const QString &workingDirectory,
     executeGit(workingDirectory, cmdArgs, editor);
 }
 
+void GitClient::merge(const QString &workingDirectory, const QStringList &unmergedFileNames)
+{
+    MergeTool *mergeTool = new MergeTool(this);
+    if (!mergeTool->start(workingDirectory, unmergedFileNames))
+        delete mergeTool;
+}
+
 void GitClient::status(const QString &workingDirectory)
 {
     // @TODO: Use "--no-color" once it is supported
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index 33ed046617959f25b943f8da703ae10e6dae0151..3a684e606e2b7f4bf30ab4c5e4d5b2f1d2e5a354 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -92,6 +92,7 @@ public:
     void diffBranch(const QString &workingDirectory,
                     const QStringList &diffArgs,
                     const QString &branchName);
+    void merge(const QString &workingDirectory, const QStringList &unmergedFileNames = QStringList());
 
     void status(const QString &workingDirectory);
     void graphLog(const QString &workingDirectory) { graphLog(workingDirectory, QString()); }
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index ac3cd283c5cf919781902fad648802b115c2c71c..6a0d7d4638dfabf120ba57c54462e15dc1b7d777 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -43,6 +43,7 @@
 #include "stashdialog.h"
 #include "settingspage.h"
 #include "resetdialog.h"
+#include "mergetool.h"
 
 #include "gerrit/gerritplugin.h"
 
@@ -519,6 +520,10 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
                            tr("Amend Last Commit..."), Core::Id("Git.AmendCommit"),
                            globalcontext, true, SLOT(startAmendCommit()));
 
+    createRepositoryAction(gitContainer,
+                           tr("Merge Tool"), Core::Id("Git.MergeTool"),
+                           globalcontext, true, SLOT(startMergeTool()));
+
     // Subversion in a submenu.
     gitContainer->addSeparator(globalcontext);
 
@@ -565,6 +570,11 @@ void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList
     m_gitClient->diff(m_submitRepository, QStringList(), unstaged, staged);
 }
 
+void GitPlugin::submitEditorMerge(const QStringList &unmerged)
+{
+    m_gitClient->merge(m_submitRepository, unmerged);
+}
+
 void GitPlugin::diffCurrentFile()
 {
     const VcsBase::VcsBasePluginState state = currentState();
@@ -708,6 +718,7 @@ Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const Commit
     if (amend) // Allow for just correcting the message
         submitEditor->setEmptyFileListEnabled(true);
     connect(submitEditor, SIGNAL(diff(QStringList,QStringList)), this, SLOT(submitEditorDiff(QStringList,QStringList)));
+    connect(submitEditor, SIGNAL(merge(QStringList)), this, SLOT(submitEditorMerge(QStringList)));
     return editor;
 }
 
@@ -804,6 +815,13 @@ void GitPlugin::push()
     m_gitClient->synchronousPush(state.topLevel());
 }
 
+void GitPlugin::startMergeTool()
+{
+    const VcsBase::VcsBasePluginState state = currentState();
+    QTC_ASSERT(state.hasTopLevel(), return);
+    m_gitClient->merge(state.topLevel());
+}
+
 // Retrieve member function of git client stored as user data of action
 static inline GitClientMemberFunc memberFunctionFromAction(const QObject *o)
 {
diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h
index 2fdcfd2ee70eaedd8fb83852a58c99dfeaca66e3..c3709fa94b0990f8c05cf95ef97d8f0ff3ba35ea 100644
--- a/src/plugins/git/gitplugin.h
+++ b/src/plugins/git/gitplugin.h
@@ -105,6 +105,7 @@ private slots:
     void diffCurrentProject();
     void diffRepository();
     void submitEditorDiff(const QStringList &unstaged, const QStringList &staged);
+    void submitEditorMerge(const QStringList &unmerged);
     void submitCurrentLog();
     void logFile();
     void blameFile();
@@ -131,6 +132,7 @@ private slots:
     void fetch();
     void pull();
     void push();
+    void startMergeTool();
 
 #ifdef WITH_TESTS
     void testStatusParsing_data();
diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp
index 895efe61a1085e70dd2ca586a843587abb75ab95..c69a3425afcbbcd229daafbd209520d501100fcd 100644
--- a/src/plugins/git/gitsubmiteditor.cpp
+++ b/src/plugins/git/gitsubmiteditor.cpp
@@ -81,7 +81,8 @@ void GitSubmitEditor::setCommitData(const CommitData &d)
 
 void GitSubmitEditor::slotDiffSelected(const QStringList &files)
 {
-    // Sort it apart into staged/unstaged files
+    // Sort it apart into unmerged/staged/unstaged files
+    QStringList unmergedFiles;
     QStringList unstagedFiles;
     QStringList stagedFiles;
     const int fileColumn = fileNameColumn();
@@ -90,7 +91,9 @@ void GitSubmitEditor::slotDiffSelected(const QStringList &files)
         const QString fileName = m_model->item(r, fileColumn)->text();
         if (files.contains(fileName)) {
             const FileStates state = static_cast<FileStates>(m_model->extraData(r).toInt());
-            if (state & StagedFile)
+            if (state & UnmergedFile)
+                unmergedFiles.push_back(fileName);
+            else if (state & StagedFile)
                 stagedFiles.push_back(fileName);
             else if (state != UntrackedFile)
                 unstagedFiles.push_back(fileName);
@@ -98,6 +101,8 @@ void GitSubmitEditor::slotDiffSelected(const QStringList &files)
     }
     if (!unstagedFiles.empty() || !stagedFiles.empty())
         emit diff(unstagedFiles, stagedFiles);
+    if (!unmergedFiles.empty())
+        emit merge(unmergedFiles);
 }
 
 GitSubmitEditorPanelData GitSubmitEditor::panelData() const
diff --git a/src/plugins/git/gitsubmiteditor.h b/src/plugins/git/gitsubmiteditor.h
index 41454ac37ebe82156284c4a613f0381cc7ff5666..2cacaecc2e9fcf837f6c2fb8abb5c1903429d7e4 100644
--- a/src/plugins/git/gitsubmiteditor.h
+++ b/src/plugins/git/gitsubmiteditor.h
@@ -56,6 +56,7 @@ public:
 
 signals:
     void diff(const QStringList &unstagedFiles, const QStringList &stagedFiles);
+    void merge(const QStringList &unmergedFiles);
 
 protected:
     QByteArray fileContents() const;
diff --git a/src/plugins/git/mergetool.cpp b/src/plugins/git/mergetool.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..02845756fa0b440e8d7f60eb0f0ecdb1dbef73e6
--- /dev/null
+++ b/src/plugins/git/mergetool.cpp
@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 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 "gitclient.h"
+#include "gitplugin.h"
+#include "mergetool.h"
+
+#include <QMessageBox>
+#include <QProcess>
+#include <QPushButton>
+#include <QRegExp>
+
+namespace Git {
+namespace Internal {
+
+MergeTool::MergeTool(QObject *parent) :
+    QObject(parent),
+    m_process(0)
+{
+}
+
+MergeTool::~MergeTool()
+{
+    delete m_process;
+}
+
+bool MergeTool::start(const QString &workingDirectory, const QStringList &files)
+{
+    QStringList arguments;
+    arguments << QLatin1String("mergetool") << QLatin1String("-y");
+    GitClient *client = GitPlugin::instance()->gitClient();
+    if (!files.isEmpty()) {
+        if (client->gitVersion() < 0x010708) {
+            QMessageBox::warning(0, tr("Error"), tr("Files input for mergetool requires git >= 1.7.8"));
+            return false;
+        }
+        arguments << files;
+    }
+    m_process = new QProcess(this);
+    m_process->setWorkingDirectory(workingDirectory);
+    m_process->start(QLatin1String("git"), arguments);
+    if (m_process->waitForStarted()) {
+        connect(m_process, SIGNAL(finished(int)), this, SLOT(done()));
+        connect(m_process, SIGNAL(readyRead()), this, SLOT(readData()));
+    }
+    else {
+        delete m_process;
+        m_process = 0;
+        return false;
+    }
+    return true;
+}
+
+MergeTool::FileState MergeTool::waitAndReadStatus(QString &extraInfo)
+{
+    QByteArray state;
+    if (m_process->canReadLine() || (m_process->waitForReadyRead(500) && m_process->canReadLine())) {
+        state = m_process->readLine().trimmed();
+        // "  {local}: modified file"
+        // "  {remote}: deleted"
+        if (!state.isEmpty()) {
+            state = state.mid(state.indexOf(':') + 2);
+            if (state == "deleted")
+                return DeletedState;
+            if (state.startsWith("modified"))
+                return ModifiedState;
+            if (state.startsWith("created"))
+                return CreatedState;
+            QByteArray submodulePrefix("submodule commit ");
+            // "  {local}: submodule commit <hash>"
+            if (state.startsWith(submodulePrefix)) {
+                extraInfo = QString::fromLocal8Bit(state.mid(submodulePrefix.size()));
+                return SubmoduleState;
+            }
+            // "  {local}: a symbolic link -> 'foo.cpp'"
+            QByteArray symlinkPrefix("a symbolic link -> '");
+            if (state.startsWith(symlinkPrefix)) {
+                extraInfo = QString::fromLocal8Bit(state.mid(symlinkPrefix.size()));
+                extraInfo.chop(1); // remove last quote
+                return SymbolicLinkState;
+            }
+        }
+    }
+    return UnknownState;
+}
+
+static MergeTool::MergeType mergeType(const QByteArray &type)
+{
+    if (type == "Normal")
+        return MergeTool::NormalMerge;
+    if (type == "Deleted")
+        return MergeTool::DeletedMerge;
+    if (type == "Submodule")
+        return MergeTool::SubmoduleMerge;
+    else
+        return MergeTool::SymbolicLinkMerge;
+}
+
+QString MergeTool::mergeTypeName()
+{
+    switch (m_mergeType) {
+    case NormalMerge: return tr("Normal");
+    case SubmoduleMerge: return tr("Submodule");
+    case DeletedMerge: return tr("Deleted");
+    case SymbolicLinkMerge: return tr("Symbolic link");
+    }
+    return QString();
+}
+
+QString MergeTool::stateName(MergeTool::FileState state, const QString &extraInfo)
+{
+    switch (state) {
+    case ModifiedState: return tr("Modified");
+    case CreatedState: return tr("Created");
+    case DeletedState: return tr("Deleted");
+    case SubmoduleState: return tr("Submodule commit %1").arg(extraInfo);
+    case SymbolicLinkState: return tr("Symbolic link -> %1").arg(extraInfo);
+    default: break;
+    }
+    return QString();
+}
+
+void MergeTool::chooseAction()
+{
+    m_merging = (m_mergeType == NormalMerge);
+    if (m_merging)
+        return;
+    QMessageBox msgBox;
+    msgBox.setWindowTitle(tr("Merge Conflict"));
+    msgBox.setIcon(QMessageBox::Question);
+    msgBox.setStandardButtons(QMessageBox::Abort);
+    msgBox.setText(tr("%1 merge conflict for '%2'\nLocal: %3\nRemote: %4")
+                   .arg(mergeTypeName())
+                   .arg(m_fileName)
+                   .arg(stateName(m_localState, m_localInfo))
+                   .arg(stateName(m_remoteState, m_remoteInfo))
+                   );
+    switch (m_mergeType) {
+    case SubmoduleMerge:
+    case SymbolicLinkMerge:
+        addButton(&msgBox, tr("&Local"), 'l');
+        addButton(&msgBox, tr("&Remote"), 'r');
+        break;
+    case DeletedMerge:
+        if (m_localState == CreatedState || m_remoteState == CreatedState)
+            addButton(&msgBox, tr("&Created"), 'c');
+        else
+            addButton(&msgBox, tr("&Modified"), 'm');
+        addButton(&msgBox, tr("&Deleted"), 'd');
+        break;
+    default:
+        break;
+    }
+
+    msgBox.exec();
+    QByteArray ba;
+    QVariant key;
+    QAbstractButton *button = msgBox.clickedButton();
+    if (button)
+        key = button->property("key");
+    // either the message box was closed without clicking anything, or abort was clicked
+    if (!key.isValid())
+        key = QVariant(QLatin1Char('a')); // abort
+    ba.append(key.toChar().toLatin1());
+    ba.append('\n');
+    m_process->write(ba);
+}
+
+void MergeTool::addButton(QMessageBox *msgBox, const QString &text, char key)
+{
+    msgBox->addButton(text, QMessageBox::AcceptRole)->setProperty("key", key);
+}
+
+void MergeTool::readData()
+{
+    while (m_process->bytesAvailable()) {
+        QByteArray line = m_process->canReadLine() ? m_process->readLine() : m_process->readAllStandardOutput();
+        // {Normal|Deleted|Submodule|Symbolic link} merge conflict for 'foo.cpp'
+        int index = line.indexOf(" merge conflict for ");
+        if (index != -1) {
+            m_mergeType = mergeType(line.left(index));
+            int quote = line.indexOf('\'');
+            m_fileName = QString::fromLocal8Bit(line.mid(quote + 1, line.lastIndexOf('\'') - quote - 1));
+            m_localState = waitAndReadStatus(m_localInfo);
+            m_remoteState = waitAndReadStatus(m_remoteInfo);
+            chooseAction();
+        } else if (m_merging && line.startsWith("Continue merging")) {
+            if (QMessageBox::question(0, tr("Continue Merging"),
+                                      tr("Continue merging other unresolved paths?"),
+                                      QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
+                m_process->write("y\n");
+            } else {
+                m_process->write("n\n");
+            }
+        }
+    }
+}
+
+void MergeTool::done()
+{
+    QMessageBox::information(0, tr("Done"), tr("Merge done"));
+    deleteLater();
+}
+
+} // namespace Internal
+} // namespace Git
diff --git a/src/plugins/git/mergetool.h b/src/plugins/git/mergetool.h
new file mode 100644
index 0000000000000000000000000000000000000000..03cc5854b1836a14cdb6c673d4552c6fffb94e72
--- /dev/null
+++ b/src/plugins/git/mergetool.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 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 MERGETOOL_H
+#define MERGETOOL_H
+
+#include <QObject>
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+class QProcess;
+class QMessageBox;
+QT_END_NAMESPACE
+
+namespace Git {
+namespace Internal {
+
+class MergeTool : public QObject
+{
+    Q_OBJECT
+
+    enum FileState {
+        UnknownState,
+        ModifiedState,
+        CreatedState,
+        DeletedState,
+        SubmoduleState,
+        SymbolicLinkState
+    };
+
+public:
+    explicit MergeTool(QObject *parent = 0);
+    ~MergeTool();
+    bool start(const QString &workingDirectory, const QStringList &files = QStringList());
+
+    enum MergeType {
+        NormalMerge,
+        SubmoduleMerge,
+        DeletedMerge,
+        SymbolicLinkMerge
+    };
+
+private slots:
+    void readData();
+    void done();
+
+private:
+    FileState waitAndReadStatus(QString &extraInfo);
+    QString mergeTypeName();
+    QString stateName(FileState state, const QString &extraInfo);
+    void chooseAction();
+    void addButton(QMessageBox *msgBox, const QString &text, char key);
+
+    QProcess *m_process;
+    MergeType m_mergeType;
+    QString m_fileName;
+    FileState m_localState;
+    QString m_localInfo;
+    FileState m_remoteState;
+    QString m_remoteInfo;
+    bool m_merging;
+};
+
+} // namespace Internal
+} // namespace Git
+
+#endif // MERGETOOL_H