diff --git a/src/plugins/git/git.pro b/src/plugins/git/git.pro index 756865db59a1ab5dcd8f1f1a93f681dbb954caaa..fb887a2e55e559d31c13d34b6de66c6d6dda9f0a 100644 --- a/src/plugins/git/git.pro +++ b/src/plugins/git/git.pro @@ -27,7 +27,8 @@ HEADERS += gitplugin.h \ gitutils.h \ remotemodel.h \ remotedialog.h \ - branchadddialog.h + branchadddialog.h \ + resetdialog.h SOURCES += gitplugin.cpp \ gitclient.cpp \ @@ -48,7 +49,8 @@ SOURCES += gitplugin.cpp \ gitutils.cpp \ remotemodel.cpp \ remotedialog.cpp \ - branchadddialog.cpp + branchadddialog.cpp \ + resetdialog.cpp FORMS += changeselectiondialog.ui \ settingspage.ui \ diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index a810c9c33e6c22e511ee9bfcba67ec5e743ad182..1186ca8abcd6fcc0ee36b40db54d98ddcd581104 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -825,6 +825,30 @@ void GitClient::addFile(const QString &workingDirectory, const QString &fileName executeGit(workingDirectory, arguments, 0, true); } +bool GitClient::synchronousLog(const QString &workingDirectory, const QStringList &arguments, + QString *output, QString *errorMessageIn) +{ + QByteArray outputText; + QByteArray errorText; + QStringList allArguments; + allArguments << QLatin1String("log") << QLatin1String(GitClient::noColorOption); + allArguments.append(arguments); + const bool rc = fullySynchronousGit(workingDirectory, allArguments, &outputText, &errorText); + if (rc) { + *output = commandOutputFromLocal8Bit(outputText); + } else { + const QString errorMessage = tr("Cannot obtain log of \"%1\": %2"). + arg(QDir::toNativeSeparators(workingDirectory), + commandOutputFromLocal8Bit(errorText)); + if (errorMessageIn) { + *errorMessageIn = errorMessage; + } else { + outputWindow()->appendError(errorMessage); + } + } + return rc; +} + // Warning: 'intendToAdd' works only from 1.6.1 onwards bool GitClient::synchronousAdd(const QString &workingDirectory, bool intendToAdd, diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 011085223803824eaff679d70de4b71fc6ad65c2..4b9856b242121424098f3b748de1150714eee611 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -107,6 +107,9 @@ public: void checkoutBranch(const QString &workingDirectory, const QString &branch); void hardReset(const QString &workingDirectory, const QString &commit = QString()); void addFile(const QString &workingDirectory, const QString &fileName); + bool synchronousLog(const QString &workingDirectory, + const QStringList &arguments, + QString *output, QString *errorMessage = 0); bool synchronousAdd(const QString &workingDirectory, // Warning: Works only from 1.6.1 onwards bool intendToAdd, diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 427ff643506faa78539c572bcbccedd8b3b089e0..5f0b1568aaa3d3b3672378e95885dff711e0a244 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -45,6 +45,7 @@ #include "gitoriousclonewizard.h" #include "stashdialog.h" #include "settingspage.h" +#include "resetdialog.h" #include <gerritplugin.h> @@ -643,15 +644,10 @@ void GitPlugin::undoRepositoryChanges() { const VcsBase::VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - const QString msg = tr("Undo all pending changes to the repository\n%1?").arg(QDir::toNativeSeparators(state.topLevel())); - const QMessageBox::StandardButton answer - = QMessageBox::question(Core::ICore::mainWindow(), - tr("Undo Changes"), msg, - QMessageBox::Yes|QMessageBox::No, - QMessageBox::No); - if (answer == QMessageBox::No) - return; - m_gitClient->hardReset(state.topLevel(), QString()); + + ResetDialog dialog; + if (dialog.runDialog(state.topLevel())) + m_gitClient->hardReset(state.topLevel(), dialog.commit()); } void GitPlugin::stageFile() diff --git a/src/plugins/git/resetdialog.cpp b/src/plugins/git/resetdialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..225ce2a0d66de1f8738c0125f19ffd9dd1f564b4 --- /dev/null +++ b/src/plugins/git/resetdialog.cpp @@ -0,0 +1,145 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "resetdialog.h" +#include "gitplugin.h" +#include "gitclient.h" + +#include <QTreeView> +#include <QLabel> +#include <QDialogButtonBox> +#include <QPushButton> +#include <QStandardItemModel> +#include <QItemSelectionModel> +#include <QVBoxLayout> +#include <QDir> + +namespace Git { +namespace Internal { + +enum Columns +{ + Sha1Column, + SubjectColumn, + ColumnCount +}; + +ResetDialog::ResetDialog(QWidget *parent) + : QDialog(parent) + , m_treeView(new QTreeView(this)) + , m_model(new QStandardItemModel(0, ColumnCount, this)) + , m_dialogButtonBox(new QDialogButtonBox(this)) +{ + QStringList headers; + headers << tr("Sha1")<< tr("Subject"); + m_model->setHorizontalHeaderLabels(headers); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(new QLabel(tr("Reset to:"))); + m_treeView->setModel(m_model); + m_treeView->setMinimumWidth(300); + m_treeView->setUniformRowHeights(true); + m_treeView->setRootIsDecorated(false); + m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows); + layout->addWidget(m_treeView); + layout->addWidget(m_dialogButtonBox); + m_dialogButtonBox->addButton(QDialogButtonBox::Cancel); + QPushButton *okButton = m_dialogButtonBox->addButton(QDialogButtonBox::Ok); + connect(m_treeView, SIGNAL(doubleClicked(QModelIndex)), + okButton, SLOT(animateClick())); + + connect(m_dialogButtonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_dialogButtonBox, SIGNAL(rejected()), this, SLOT(reject())); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + resize(600, 400); +} + +bool ResetDialog::runDialog(const QString &repository) +{ + setWindowTitle(tr("Undo Changes to %1").arg(QDir::toNativeSeparators(repository))); + + if (!populateLog(repository) || !m_model->rowCount()) + return QDialog::Rejected; + + m_treeView->selectionModel()->select(m_model->index(0, 0), + QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + + return exec() == QDialog::Accepted; +} + +QString ResetDialog::commit() const +{ + // Return Sha1, or empty for top commit. + if (const QStandardItem *sha1Item = currentItem(Sha1Column)) + return sha1Item->row() ? sha1Item->text() : QString(); + return QString(); +} + +bool ResetDialog::populateLog(const QString &repository) +{ + if (const int rowCount = m_model->rowCount()) + m_model->removeRows(0, rowCount); + + // Retrieve log using a custom format "Sha1:Subject" + GitClient *client = GitPlugin::instance()->gitClient(); + QStringList arguments; + arguments << QLatin1String("--max-count=30") << QLatin1String("--format=%h:%s"); + QString output; + if (!client->synchronousLog(repository, arguments, &output)) + return false; + foreach (const QString &line, output.split(QLatin1Char('\n'))) { + const int colonPos = line.indexOf(QLatin1Char(':')); + if (colonPos != -1) { + QList<QStandardItem *> row; + for (int c = 0; c < ColumnCount; ++c) { + QStandardItem *item = new QStandardItem; + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + row.push_back(item); + } + row[Sha1Column]->setText(line.left(colonPos)); + row[SubjectColumn]->setText(line.right(line.size() - colonPos - 1)); + m_model->appendRow(row); + } + } + return true; +} + +const QStandardItem *ResetDialog::currentItem(int column) const +{ + const QModelIndex currentIndex = m_treeView->selectionModel()->currentIndex(); + if (currentIndex.isValid()) + return m_model->item(currentIndex.row(), column); + return 0; +} + +} // namespace Internal +} // namespace Git diff --git a/src/plugins/git/resetdialog.h b/src/plugins/git/resetdialog.h new file mode 100644 index 0000000000000000000000000000000000000000..a9945aaee82c7b38573ff1e04ec688581f06b4bd --- /dev/null +++ b/src/plugins/git/resetdialog.h @@ -0,0 +1,73 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef GIT_INTERNAL_RESETDIALOG_H +#define GIT_INTERNAL_RESETDIALOG_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QTreeView; +class QDialogButtonBox; +class QStandardItemModel; +class QStandardItem; +QT_END_NAMESPACE + +namespace Git { +namespace Internal { + +// A dialog that lists SHA1 and subject of the changes +// for reset --hard. + +class ResetDialog : public QDialog +{ + Q_OBJECT +public: + explicit ResetDialog(QWidget *parent = 0); + + bool runDialog(const QString &repository); + + QString commit() const; + +private: + bool populateLog(const QString &repository); + const QStandardItem *currentItem(int column = 0) const; + + QTreeView *m_treeView; + QStandardItemModel *m_model; + QDialogButtonBox *m_dialogButtonBox; +}; + +} // namespace Internal +} // namespace Git + +#endif // GIT_INTERNAL_RESETDIALOG_H