From 7df112b687c8694153ef3a5938921104b6e75115 Mon Sep 17 00:00:00 2001
From: Petar Perisin <petar.perisin@gmail.com>
Date: Fri, 21 Dec 2012 23:49:29 +0100
Subject: [PATCH] Git: Added Merge and Rebase

Added git functions - "Merge" and "Rebase"
They are in the "Branches" dialog:
- Merge - merge selected branch into current one
- Rebase - rebase current branch on selected one

Task-number: QTCREATORBUG-8367

Change-Id: I9ed306c64d5d4b7bd1d58730a5e1009f0bd4ec0e
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Tobias Hunger <tobias.hunger@digia.com>
---
 src/plugins/git/branchdialog.cpp |  39 +++++++++
 src/plugins/git/branchdialog.h   |   2 +
 src/plugins/git/branchdialog.ui  | 133 +++++++++++++++++++++----------
 src/plugins/git/gitclient.cpp    |  23 +++++-
 src/plugins/git/gitclient.h      |   6 +-
 5 files changed, 156 insertions(+), 47 deletions(-)

diff --git a/src/plugins/git/branchdialog.cpp b/src/plugins/git/branchdialog.cpp
index 59eaa1ec7eb..b39adce43c9 100644
--- a/src/plugins/git/branchdialog.cpp
+++ b/src/plugins/git/branchdialog.cpp
@@ -65,6 +65,8 @@ BranchDialog::BranchDialog(QWidget *parent) :
     connect(m_ui->removeButton, SIGNAL(clicked()), this, SLOT(remove()));
     connect(m_ui->diffButton, SIGNAL(clicked()), this, SLOT(diff()));
     connect(m_ui->logButton, SIGNAL(clicked()), this, SLOT(log()));
+    connect(m_ui->mergeButton, SIGNAL(clicked()), this, SLOT(merge()));
+    connect(m_ui->rebaseButton, SIGNAL(clicked()), this, SLOT(rebase()));
 
     m_ui->branchView->setModel(m_model);
 
@@ -102,11 +104,14 @@ void BranchDialog::enableButtons()
     const bool currentSelected = hasSelection && idx == m_model->currentBranch();
     const bool isLocal = m_model->isLocal(idx);
     const bool isLeaf = m_model->isLeaf(idx);
+    const bool currentLocal = m_model->isLocal(m_model->currentBranch());
 
     m_ui->removeButton->setEnabled(hasSelection && !currentSelected && isLocal && isLeaf);
     m_ui->logButton->setEnabled(hasSelection && isLeaf);
     m_ui->diffButton->setEnabled(hasSelection && isLeaf);
     m_ui->checkoutButton->setEnabled(hasSelection && !currentSelected && isLeaf);
+    m_ui->rebaseButton->setEnabled(hasSelection && !currentSelected && isLeaf && currentLocal);
+    m_ui->mergeButton->setEnabled(hasSelection && !currentSelected && isLeaf && currentLocal);
 }
 
 void BranchDialog::refresh()
@@ -194,6 +199,40 @@ void BranchDialog::log()
     GitPlugin::instance()->gitClient()->graphLog(m_repository, branchName);
 }
 
+void BranchDialog::merge()
+{
+    QModelIndex idx = selectedIndex();
+    QTC_CHECK(m_model->isLocal(m_model->currentBranch())); // otherwise the button would not be enabled!
+    QTC_CHECK(idx != m_model->currentBranch());            // otherwise the button would not be enabled!
+
+    const QString branch = m_model->branchName(idx);
+    GitClient *gitClient = GitPlugin::instance()->gitClient();
+    QString stashMessage;
+
+    if (gitClient->gitStatus(m_repository, StatusMode(NoUntracked | NoSubmodules)) == GitClient::StatusChanged)
+        stashMessage = gitClient->synchronousStash(m_repository, QLatin1String("merge"));
+
+    if (gitClient->synchronousMerge(m_repository, branch) && (!stashMessage.isEmpty()))
+        gitClient->stashPop(m_repository);
+}
+
+void BranchDialog::rebase()
+{
+    QModelIndex idx = selectedIndex();
+    QTC_CHECK(m_model->isLocal(m_model->currentBranch())); // otherwise the button would not be enabled!
+    QTC_CHECK(idx != m_model->currentBranch());            // otherwise the button would not be enabled!
+
+    const QString baseBranch = m_model->branchName(idx);
+    GitClient *gitClient = GitPlugin::instance()->gitClient();
+    QString stashMessage;
+
+    if (gitClient->gitStatus(m_repository, StatusMode(NoUntracked | NoSubmodules)) == GitClient::StatusChanged)
+        stashMessage = gitClient->synchronousStash(m_repository, QLatin1String("rebase"));
+
+    if (gitClient->synchronousRebase(m_repository, baseBranch) && (!stashMessage.isEmpty()))
+        gitClient->stashPop(m_repository);
+}
+
 void BranchDialog::changeEvent(QEvent *e)
 {
     QDialog::changeEvent(e);
diff --git a/src/plugins/git/branchdialog.h b/src/plugins/git/branchdialog.h
index cded4f1c038..5e06e343deb 100644
--- a/src/plugins/git/branchdialog.h
+++ b/src/plugins/git/branchdialog.h
@@ -71,6 +71,8 @@ private slots:
     void remove();
     void diff();
     void log();
+    void merge();
+    void rebase();
 
 protected:
     void changeEvent(QEvent *e);
diff --git a/src/plugins/git/branchdialog.ui b/src/plugins/git/branchdialog.ui
index 1689119b666..5c9f1fd462f 100644
--- a/src/plugins/git/branchdialog.ui
+++ b/src/plugins/git/branchdialog.ui
@@ -51,17 +51,14 @@
      <property name="title">
       <string>Branches</string>
      </property>
-     <layout class="QGridLayout" name="gridLayout">
+     <layout class="QHBoxLayout" name="horizontalLayout_2">
       <property name="topMargin">
        <number>4</number>
       </property>
       <property name="bottomMargin">
        <number>4</number>
       </property>
-      <property name="verticalSpacing">
-       <number>9</number>
-      </property>
-      <item row="0" column="0" colspan="3">
+      <item>
        <widget class="QTreeView" name="branchView">
         <property name="rootIsDecorated">
          <bool>false</bool>
@@ -74,40 +71,95 @@
         </attribute>
        </widget>
       </item>
-      <item row="1" column="0">
-       <widget class="QPushButton" name="addButton">
-        <property name="text">
-         <string>&amp;Add...</string>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="2">
-       <widget class="QPushButton" name="removeButton">
-        <property name="text">
-         <string>&amp;Remove</string>
-        </property>
-       </widget>
-      </item>
-      <item row="2" column="0">
-       <widget class="QPushButton" name="diffButton">
-        <property name="text">
-         <string>&amp;Diff</string>
-        </property>
-       </widget>
-      </item>
-      <item row="2" column="2">
-       <widget class="QPushButton" name="logButton">
-        <property name="text">
-         <string>&amp;Log</string>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="1">
-       <widget class="QPushButton" name="checkoutButton">
-        <property name="text">
-         <string>&amp;Checkout</string>
+      <item>
+       <layout class="QVBoxLayout" name="verticalLayout">
+        <property name="spacing">
+         <number>15</number>
         </property>
-       </widget>
+        <item>
+         <layout class="QVBoxLayout" name="verticalLayout_2">
+          <property name="spacing">
+           <number>3</number>
+          </property>
+          <item>
+           <widget class="QPushButton" name="addButton">
+            <property name="text">
+             <string>&amp;Add...</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="removeButton">
+            <property name="text">
+             <string>&amp;Remove</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="checkoutButton">
+            <property name="text">
+             <string>&amp;Checkout</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <layout class="QVBoxLayout" name="verticalLayout_4">
+          <property name="spacing">
+           <number>3</number>
+          </property>
+          <item>
+           <widget class="QPushButton" name="diffButton">
+            <property name="text">
+             <string>&amp;Diff</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="logButton">
+            <property name="text">
+             <string>&amp;Log</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <layout class="QVBoxLayout" name="verticalLayout_5">
+          <property name="spacing">
+           <number>3</number>
+          </property>
+          <item>
+           <widget class="QPushButton" name="mergeButton">
+            <property name="text">
+             <string>&amp;Merge</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="rebaseButton">
+            <property name="text">
+             <string>Re&amp;base</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
       </item>
      </layout>
     </widget>
@@ -126,11 +178,6 @@
  </widget>
  <tabstops>
   <tabstop>branchView</tabstop>
-  <tabstop>addButton</tabstop>
-  <tabstop>checkoutButton</tabstop>
-  <tabstop>removeButton</tabstop>
-  <tabstop>diffButton</tabstop>
-  <tabstop>logButton</tabstop>
   <tabstop>buttonBox</tabstop>
   <tabstop>refreshButton</tabstop>
  </tabstops>
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index ac42ce7cc88..988883ed2c7 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -2096,7 +2096,7 @@ bool GitClient::synchronousFetch(const QString &workingDirectory, const QString
     return resp.result == Utils::SynchronousProcessResponse::Finished;
 }
 
-bool GitClient::synchronousPullOrRebase(const QString &workingDirectory, const QStringList &arguments, bool rebase)
+bool GitClient::synchronousMergeOrRebase(const QString &workingDirectory, const QStringList &arguments, bool rebase)
 {
     // Disable UNIX terminals to suppress SSH prompting.
     const unsigned flags = VcsBase::VcsBasePlugin::SshPasswordPrompt|VcsBase::VcsBasePlugin::ShowStdOutInLogWindow;
@@ -2115,14 +2115,14 @@ bool GitClient::synchronousPull(const QString &workingDirectory, bool rebase)
     QStringList arguments(QLatin1String("pull"));
     if (rebase)
         arguments << QLatin1String("--rebase");
-    return synchronousPullOrRebase(workingDirectory, arguments, rebase);
+    return synchronousMergeOrRebase(workingDirectory, arguments, rebase);
 }
 
 bool GitClient::synchronousRebaseContinue(const QString &workingDirectory)
 {
     QStringList arguments(QLatin1String("rebase"));
     arguments << QLatin1String("--continue");
-    return synchronousPullOrRebase(workingDirectory, arguments, true);
+    return synchronousMergeOrRebase(workingDirectory, arguments, true);
 }
 
 void GitClient::handleMergeConflicts(const QString &workingDir, bool rebase)
@@ -2196,6 +2196,23 @@ bool GitClient::synchronousPush(const QString &workingDirectory, const QString &
     return resp.result == Utils::SynchronousProcessResponse::Finished;
 }
 
+bool GitClient::synchronousMerge(const QString &workingDirectory, const QString &branch)
+{
+    QStringList arguments(QLatin1String("merge"));
+    arguments << branch;
+    return synchronousMergeOrRebase(workingDirectory, arguments, false);
+}
+
+bool GitClient::synchronousRebase(const QString &workingDirectory, const QString &baseBranch,
+                                  const QString &topicBranch)
+{
+    QStringList arguments(QLatin1String("rebase"));
+    arguments << baseBranch;
+    if (!topicBranch.isEmpty())
+        arguments << topicBranch;
+    return synchronousMergeOrRebase(workingDirectory, arguments, true);
+}
+
 QString GitClient::msgNoChangedFiles()
 {
     return tr("There are no modified files.");
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index afc6f4968bd..8482febc92b 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -179,6 +179,10 @@ public:
     bool synchronousPull(const QString &workingDirectory, bool rebase);
     bool synchronousRebaseContinue(const QString &workingDirectory);
     bool synchronousPush(const QString &workingDirectory, const QString &remote = QString());
+    bool synchronousMerge(const QString &workingDirectory, const QString &branch);
+    bool synchronousRebase(const QString &workingDirectory,
+                           const QString &baseBranch,
+                           const QString &topicBranch = QString());
 
     // git svn support (asynchronous).
     void synchronousSubversionFetch(const QString &workingDirectory);
@@ -296,7 +300,7 @@ private:
                          QString *errorMessage,
                          bool revertStaging);
     void connectRepositoryChanged(const QString & repository, VcsBase::Command *cmd);
-    bool synchronousPullOrRebase(const QString &workingDirectory, const QStringList &arguments, bool rebase);
+    bool synchronousMergeOrRebase(const QString &workingDirectory, const QStringList &arguments, bool rebase);
     void handleMergeConflicts(const QString &workingDir, bool rebase);
     bool tryLauchingGitK(const QProcessEnvironment &env,
                          const QString &workingDirectory,
-- 
GitLab