forked from qt-creator/qt-creator
374 lines
12 KiB
C++
374 lines
12 KiB
C++
/**************************************************************************
|
|
**
|
|
** This file is part of Qt Creator
|
|
**
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|
**
|
|
** Contact: Nokia Corporation (info@qt.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 info@qt.nokia.com.
|
|
**
|
|
**************************************************************************/
|
|
|
|
#include "branchdialog.h"
|
|
#include "branchmodel.h"
|
|
#include "gitclient.h"
|
|
#include "gitplugin.h"
|
|
#include "ui_branchdialog.h"
|
|
#include "stashdialog.h" // Label helpers
|
|
|
|
#include <vcsbase/vcsbaseoutputwindow.h>
|
|
|
|
#include <QtGui/QItemSelectionModel>
|
|
#include <QtGui/QPushButton>
|
|
#include <QtGui/QMessageBox>
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
enum { debug = 0 };
|
|
|
|
// Single selection helper
|
|
static inline int selectedRow(const QAbstractItemView *listView)
|
|
{
|
|
const QModelIndexList indexList = listView->selectionModel()->selectedIndexes();
|
|
if (indexList.size() == 1)
|
|
return indexList.front().row();
|
|
return -1;
|
|
}
|
|
|
|
// Helper to select a row. No sooner said then done
|
|
static inline void selectListRow(QAbstractItemView *iv, int row)
|
|
{
|
|
const QModelIndex index = iv->model()->index(row, 0);
|
|
iv->selectionModel()->select(index, QItemSelectionModel::Select);
|
|
}
|
|
|
|
namespace Git {
|
|
namespace Internal {
|
|
|
|
static inline GitClient *gitClient()
|
|
{
|
|
return GitPlugin::instance()->gitClient();
|
|
}
|
|
|
|
BranchDialog::BranchDialog(QWidget *parent) :
|
|
QDialog(parent),
|
|
m_ui(new Ui::BranchDialog),
|
|
m_checkoutButton(0),
|
|
m_diffButton(0),
|
|
m_logButton(0),
|
|
m_refreshButton(0),
|
|
m_deleteButton(0),
|
|
m_localModel(new LocalBranchModel(gitClient(), this)),
|
|
m_remoteModel(new RemoteBranchModel(gitClient(), this))
|
|
{
|
|
setModal(false);
|
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
|
setAttribute(Qt::WA_DeleteOnClose, true); // Do not update unnecessarily
|
|
|
|
m_ui->setupUi(this);
|
|
|
|
m_checkoutButton = m_ui->buttonBox->addButton(tr("Checkout"), QDialogButtonBox::ActionRole);
|
|
connect(m_checkoutButton, SIGNAL(clicked()), this, SLOT(slotCheckoutSelectedBranch()));
|
|
|
|
m_diffButton = m_ui->buttonBox->addButton(tr("Diff"), QDialogButtonBox::ActionRole);
|
|
connect(m_diffButton, SIGNAL(clicked()), this, SLOT(slotDiffSelected()));
|
|
|
|
m_logButton = m_ui->buttonBox->addButton(tr("Log"), QDialogButtonBox::ActionRole);
|
|
connect(m_logButton, SIGNAL(clicked()), this, SLOT(slotLog()));
|
|
|
|
m_refreshButton = m_ui->buttonBox->addButton(tr("Refresh"), QDialogButtonBox::ActionRole);
|
|
connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(slotRefresh()));
|
|
|
|
m_deleteButton = m_ui->buttonBox->addButton(tr("Delete..."), QDialogButtonBox::ActionRole);
|
|
connect(m_deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteSelectedBranch()));
|
|
|
|
connect(m_ui->localBranchListView, SIGNAL(doubleClicked(QModelIndex)), this,
|
|
SLOT(slotLocalBranchActivated()));
|
|
connect(m_ui->remoteBranchListView, SIGNAL(doubleClicked(QModelIndex)), this,
|
|
SLOT(slotRemoteBranchActivated(QModelIndex)));
|
|
|
|
connect(m_localModel, SIGNAL(newBranchEntered(QString)), this, SLOT(slotCreateLocalBranch(QString)));
|
|
m_ui->localBranchListView->setModel(m_localModel);
|
|
m_ui->remoteBranchListView->setModel(m_remoteModel);
|
|
|
|
connect(m_ui->localBranchListView->selectionModel(),
|
|
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
|
this, SLOT(slotEnableButtons(QItemSelection)));
|
|
connect(m_ui->remoteBranchListView->selectionModel(),
|
|
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
|
this, SLOT(slotEnableButtons(QItemSelection)));
|
|
|
|
slotEnableButtons();
|
|
}
|
|
|
|
BranchDialog::~BranchDialog()
|
|
{
|
|
delete m_ui;
|
|
}
|
|
|
|
void BranchDialog::refresh(const QString &repository, bool force)
|
|
{
|
|
if (m_repository == repository && !force)
|
|
return;
|
|
// Refresh
|
|
m_repository = repository;
|
|
m_ui->repositoryLabel->setText(StashDialog::msgRepositoryLabel(m_repository));
|
|
if (m_repository.isEmpty()) {
|
|
m_localModel->clear();
|
|
m_remoteModel->clear();
|
|
} else {
|
|
QString errorMessage;
|
|
const bool success = m_localModel->refresh(m_repository, &errorMessage)
|
|
&& m_remoteModel->refresh(m_repository, &errorMessage);
|
|
if (!success)
|
|
VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
|
|
}
|
|
slotEnableButtons();
|
|
}
|
|
|
|
int BranchDialog::selectedLocalBranchIndex() const
|
|
{
|
|
return selectedRow(m_ui->localBranchListView);
|
|
}
|
|
|
|
int BranchDialog::selectedRemoteBranchIndex() const
|
|
{
|
|
return selectedRow(m_ui->remoteBranchListView);
|
|
}
|
|
|
|
void BranchDialog::slotEnableButtons(const QItemSelection &selected)
|
|
{
|
|
if (!selected.indexes().isEmpty()) {
|
|
if (selected.indexes().at(0).model() == m_localModel)
|
|
m_ui->remoteBranchListView->clearSelection();
|
|
else
|
|
m_ui->localBranchListView->clearSelection();
|
|
}
|
|
|
|
// We can switch to or delete branches that are not current.
|
|
const int selectedLocalRow = selectedLocalBranchIndex();
|
|
const bool hasRepository = !m_repository.isEmpty();
|
|
const bool hasLocalSelection = selectedLocalRow != -1 && !m_localModel->isNewBranchRow(selectedLocalRow);
|
|
const bool otherLocalSelected = hasLocalSelection && selectedLocalRow != m_localModel->currentBranch();
|
|
const bool branchSelected = hasLocalSelection || selectedRemoteBranchIndex() != -1;
|
|
|
|
m_checkoutButton->setEnabled(otherLocalSelected);
|
|
m_diffButton->setEnabled(branchSelected);
|
|
m_logButton->setEnabled(branchSelected);
|
|
m_deleteButton->setEnabled(otherLocalSelected);
|
|
m_refreshButton->setEnabled(hasRepository);
|
|
// Also disable <New Branch> entry of list view
|
|
m_ui->localBranchListView->setEnabled(hasRepository);
|
|
m_ui->remoteBranchListView->setEnabled(hasRepository);
|
|
}
|
|
|
|
void BranchDialog::slotRefresh()
|
|
{
|
|
refresh(m_repository, true);
|
|
}
|
|
|
|
void BranchDialog::selectLocalBranch(const QString &b)
|
|
{
|
|
// Select the newly created branch
|
|
const int row = m_localModel->findBranchByName(b);
|
|
if (row != -1)
|
|
selectListRow(m_ui->localBranchListView, row);
|
|
}
|
|
|
|
bool BranchDialog::ask(const QString &title, const QString &what, bool defaultButton)
|
|
{
|
|
return QMessageBox::question(this, title, what, QMessageBox::Yes|QMessageBox::No,
|
|
defaultButton ? QMessageBox::Yes : QMessageBox::No) == QMessageBox::Yes;
|
|
}
|
|
|
|
/* Prompt to delete a local branch and do so. */
|
|
void BranchDialog::slotDeleteSelectedBranch()
|
|
{
|
|
const int idx = selectedLocalBranchIndex();
|
|
if (idx == -1)
|
|
return;
|
|
const QString name = m_localModel->branchName(idx);
|
|
if (!ask(tr("Delete Branch"), tr("Would you like to delete the branch '%1'?").arg(name), true))
|
|
return;
|
|
QString errorMessage;
|
|
bool ok = false;
|
|
do {
|
|
QString output;
|
|
QStringList args(QLatin1String("-D"));
|
|
args << name;
|
|
if (!gitClient()->synchronousBranchCmd(m_repository, args, &output, &errorMessage))
|
|
break;
|
|
if (!m_localModel->refresh(m_repository, &errorMessage))
|
|
break;
|
|
ok = true;
|
|
} while (false);
|
|
slotEnableButtons();
|
|
if (!ok)
|
|
QMessageBox::warning(this, tr("Failed to delete branch"), errorMessage);
|
|
}
|
|
|
|
void BranchDialog::slotCreateLocalBranch(const QString &branchName)
|
|
{
|
|
// Create
|
|
QString output;
|
|
QString errorMessage;
|
|
bool ok = false;
|
|
do {
|
|
if (!gitClient()->synchronousBranchCmd(m_repository, QStringList(branchName), &output, &errorMessage))
|
|
break;
|
|
if (!m_localModel->refresh(m_repository, &errorMessage))
|
|
break;
|
|
ok = true;
|
|
} while (false);
|
|
if (!ok) {
|
|
QMessageBox::warning(this, tr("Failed to create branch"), errorMessage);
|
|
return;
|
|
}
|
|
selectLocalBranch(branchName);
|
|
}
|
|
|
|
void BranchDialog::slotLocalBranchActivated()
|
|
{
|
|
if (m_checkoutButton->isEnabled())
|
|
m_checkoutButton->animateClick();
|
|
}
|
|
|
|
void BranchDialog::slotDiffSelected()
|
|
{
|
|
int idx = selectedLocalBranchIndex();
|
|
if (idx != -1) {
|
|
gitClient()->diffBranch(m_repository, QStringList(), m_localModel->branchName(idx));
|
|
return;
|
|
}
|
|
idx = selectedRemoteBranchIndex();
|
|
if (idx != -1)
|
|
gitClient()->diffBranch(m_repository, QStringList(), m_remoteModel->branchName(idx));
|
|
}
|
|
|
|
void BranchDialog::slotLog()
|
|
{
|
|
int idx = selectedLocalBranchIndex();
|
|
if (idx != -1) {
|
|
gitClient()->graphLog(m_repository, m_localModel->branchName(idx));
|
|
return;
|
|
}
|
|
idx = selectedRemoteBranchIndex();
|
|
if (idx != -1)
|
|
gitClient()->graphLog(m_repository, m_remoteModel->branchName(idx));
|
|
}
|
|
|
|
/* Ask to stash away changes and then close dialog and do an asynchronous
|
|
* checkout. */
|
|
void BranchDialog::slotCheckoutSelectedBranch()
|
|
{
|
|
const int idx = selectedLocalBranchIndex();
|
|
if (idx == -1)
|
|
return;
|
|
const QString name = m_localModel->branchName(idx);
|
|
QString errorMessage;
|
|
switch (gitClient()->ensureStash(m_repository, &errorMessage)) {
|
|
case GitClient::StashUnchanged:
|
|
case GitClient::Stashed:
|
|
case GitClient::NotStashed:
|
|
break;
|
|
case GitClient::StashCanceled:
|
|
return;
|
|
case GitClient::StashFailed:
|
|
QMessageBox::warning(this, tr("Failed to stash"), errorMessage);
|
|
return;
|
|
}
|
|
if (gitClient()->synchronousCheckoutBranch(m_repository, name, &errorMessage)) {
|
|
refresh(m_repository, true);
|
|
} else {
|
|
QMessageBox::warning(this, tr("Checkout failed"), errorMessage);
|
|
}
|
|
}
|
|
|
|
void BranchDialog::slotRemoteBranchActivated(const QModelIndex &i)
|
|
{
|
|
// Double click on a remote branch (origin/foo): Switch to matching
|
|
// local (foo) one or offer to create a tracking branch.
|
|
const QString remoteName = m_remoteModel->branchName(i.row());
|
|
// build the name of the corresponding local branch
|
|
// and look for it in the local model.
|
|
const int slashPos = remoteName.indexOf(QLatin1Char('/'));
|
|
if (slashPos == -1)
|
|
return;
|
|
const QString localBranch = remoteName.mid(slashPos + 1);
|
|
if (localBranch == QLatin1String("HEAD") || localBranch == QLatin1String("master"))
|
|
return;
|
|
const int localIndex = m_localModel->findBranchByName(localBranch);
|
|
if (debug)
|
|
qDebug() << Q_FUNC_INFO << remoteName << localBranch << localIndex;
|
|
// There is a matching a local one!
|
|
if (localIndex != -1) {
|
|
// Is it the current one? Just close.
|
|
if (m_localModel->currentBranch() == localIndex) {
|
|
accept();
|
|
return;
|
|
}
|
|
// Nope, select and trigger checkout
|
|
selectListRow(m_ui->localBranchListView, localIndex);
|
|
slotLocalBranchActivated();
|
|
return;
|
|
}
|
|
// Does not exist yet. Ask to create.
|
|
const QString msg = tr("Would you like to create a local branch '%1' tracking the remote branch '%2'?").arg(localBranch, remoteName);
|
|
if (!ask(tr("Create branch"), msg, true))
|
|
return;
|
|
QStringList args(QLatin1String("--track"));
|
|
args << localBranch << remoteName;
|
|
QString errorMessage;
|
|
bool ok = false;
|
|
do {
|
|
QString output;
|
|
if (!gitClient()->synchronousBranchCmd(m_repository, args, &output, &errorMessage))
|
|
break;
|
|
if (!m_localModel->refresh(m_repository, &errorMessage))
|
|
break;
|
|
ok = true;
|
|
} while (false);
|
|
if (!ok) {
|
|
QMessageBox::warning(this, tr("Failed to create a tracking branch"), errorMessage);
|
|
return;
|
|
}
|
|
// Select it
|
|
selectLocalBranch(localBranch);
|
|
}
|
|
|
|
void BranchDialog::changeEvent(QEvent *e)
|
|
{
|
|
QDialog::changeEvent(e);
|
|
switch (e->type()) {
|
|
case QEvent::LanguageChange:
|
|
m_ui->retranslateUi(this);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace Internal
|
|
} // namespace Git
|