Skip to content
Snippets Groups Projects
Commit 352cf143 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Fixes: Add way to create a new local branch in the git plugin

Task: 205821
Details: Split up the branch model into remote branch base class and extended local branch class with <New Branch> row.
parent 800baec8
No related branches found
No related tags found
No related merge requests found
......@@ -58,8 +58,9 @@ bool BranchDialog::init(GitClient *client, const QString &workingDirectory, QStr
}
m_ui->repositoryFieldLabel->setText(m_repoDirectory);
m_localModel = new BranchModel(client, BranchModel::LocalBranches, this);
m_remoteModel = new BranchModel(client, BranchModel::RemoteBranches, this);
m_localModel = new LocalBranchModel(client, this);
connect(m_localModel, SIGNAL(newBranchCreated(QString)), this, SLOT(slotNewLocalBranchCreated(QString)));
m_remoteModel = new RemoteBranchModel(client, this);
if (!m_localModel->refresh(workingDirectory, errorMessage)
|| !m_remoteModel->refresh(workingDirectory, errorMessage))
return false;
......@@ -93,13 +94,23 @@ void BranchDialog::slotEnableButtons()
const int selectedLocalRow = selectedLocalBranchIndex();
const int currentLocalBranch = m_localModel->currentBranch();
const bool hasSelection = selectedLocalRow != -1;
const bool hasSelection = selectedLocalRow != -1 && !m_localModel->isNewBranchRow(selectedLocalRow);
const bool currentIsNotSelected = hasSelection && selectedLocalRow != currentLocalBranch;
m_checkoutButton->setEnabled(currentIsNotSelected);
m_deleteButton->setEnabled(currentIsNotSelected);
}
void BranchDialog::slotNewLocalBranchCreated(const QString &b)
{
// Select the newly created branch
const int row = m_localModel->findBranchByName(b);
if (row != -1) {
const QModelIndex index = m_localModel->index(row);
m_ui->localBranchListView->selectionModel()->select(index, QItemSelectionModel::Select);
}
}
bool BranchDialog::ask(const QString &title, const QString &what, bool defaultButton)
{
return QMessageBox::question(this, title, what, QMessageBox::Yes|QMessageBox::No,
......
......@@ -14,7 +14,8 @@ namespace Git {
}
class GitClient;
class BranchModel;
class LocalBranchModel;
class RemoteBranchModel;
/* Branch dialog: Display a list of local branches at the top
* and remote branches below. Offers to checkout/delete local
......@@ -39,6 +40,7 @@ namespace Git {
void slotCheckoutSelectedBranch();
void slotDeleteSelectedBranch();
void slotLocalBranchActivated();
void slotNewLocalBranchCreated(const QString &);
private:
bool ask(const QString &title, const QString &what, bool defaultButton);
......@@ -51,8 +53,8 @@ namespace Git {
QPushButton *m_checkoutButton;
QPushButton *m_deleteButton;
BranchModel *m_localModel;
BranchModel *m_remoteModel;
LocalBranchModel *m_localModel;
RemoteBranchModel *m_remoteModel;
QString m_repoDirectory;
};
} // namespace Internal
......
......@@ -2,6 +2,8 @@
#include "gitclient.h"
#include <QtCore/QDebug>
#include <QtCore/QRegExp>
#include <QtCore/QTimer>
enum { debug = 0 };
......@@ -10,7 +12,7 @@ namespace Git {
// Parse a branch line: " *name sha description". Return true if it is
// the current one
bool BranchModel::Branch::parse(const QString &lineIn, bool *isCurrent)
bool RemoteBranchModel::Branch::parse(const QString &lineIn, bool *isCurrent)
{
if (debug)
qDebug() << Q_FUNC_INFO << lineIn;
......@@ -28,40 +30,41 @@ bool BranchModel::Branch::parse(const QString &lineIn, bool *isCurrent)
return true;
}
static inline Qt::ItemFlags typeToModelFlags(BranchModel::Type t)
// ------ RemoteBranchModel
RemoteBranchModel::RemoteBranchModel(GitClient *client, QObject *parent) :
QAbstractListModel(parent),
m_flags(Qt::ItemIsSelectable|Qt::ItemIsEnabled),
m_client(client)
{
Qt::ItemFlags rc = Qt::ItemIsSelectable|Qt::ItemIsEnabled;
if (t == BranchModel::LocalBranches)
rc |= Qt::ItemIsUserCheckable;
return rc;
}
// --- BranchModel
BranchModel::BranchModel(GitClient *client, Type type, QObject *parent) :
QAbstractListModel(parent),
m_type(type),
m_flags(typeToModelFlags(type)),
m_client(client),
m_currentBranch(-1)
bool RemoteBranchModel::refresh(const QString &workingDirectory, QString *errorMessage)
{
int currentBranch;
return refreshBranches(workingDirectory, true, &currentBranch, errorMessage);
}
int BranchModel::currentBranch() const
QString RemoteBranchModel::branchName(int row) const
{
return m_currentBranch;
return m_branches.at(row).name;
}
QString BranchModel::branchName(int row) const
QString RemoteBranchModel::workingDirectory() const
{
return m_branches.at(row).name;
return m_workingDirectory;
}
int BranchModel::rowCount(const QModelIndex & /* parent */) const
int RemoteBranchModel::branchCount() const
{
return m_branches.size();
}
QVariant BranchModel::data(const QModelIndex &index, int role) const
int RemoteBranchModel::rowCount(const QModelIndex & /* parent */) const
{
return branchCount();
}
QVariant RemoteBranchModel::data(const QModelIndex &index, int role) const
{
const int row = index.row();
switch (role) {
......@@ -72,36 +75,52 @@ QVariant BranchModel::data(const QModelIndex &index, int role) const
m_branches.at(row).toolTip = toolTip(m_branches.at(row).currentSHA);
return m_branches.at(row).toolTip;
break;
case Qt::CheckStateRole:
if (m_type == RemoteBranches)
return QVariant();
return row == m_currentBranch ? Qt::Checked : Qt::Unchecked;
default:
break;
}
return QVariant();
}
Qt::ItemFlags BranchModel::flags(const QModelIndex & /*index */) const
Qt::ItemFlags RemoteBranchModel::flags(const QModelIndex & /* index */) const
{
return m_flags;
}
bool BranchModel::refresh(const QString &workingDirectory, QString *errorMessage)
QString RemoteBranchModel::toolTip(const QString &sha) const
{
// Show the sha description excluding diff as toolTip
QString output;
QString errorMessage;
if (!m_client->synchronousShow(m_workingDirectory, sha, &output, &errorMessage))
return errorMessage;
// Remove 'diff' output
const int diffPos = output.indexOf(QLatin1String("\ndiff --"));
if (diffPos != -1)
output.remove(diffPos, output.size() - diffPos);
return output;
}
bool RemoteBranchModel::runGitBranchCommand(const QString &workingDirectory, const QStringList &additionalArgs, QString *output, QString *errorMessage)
{
return m_client->synchronousBranchCmd(workingDirectory, additionalArgs, output, errorMessage);
}
bool RemoteBranchModel::refreshBranches(const QString &workingDirectory, bool remoteBranches,
int *currentBranch, QString *errorMessage)
{
// Run branch command with verbose.
QStringList branchArgs(QLatin1String("-v"));
QString output;
if (m_type == RemoteBranches)
*currentBranch = -1;
if (remoteBranches)
branchArgs.push_back(QLatin1String("-r"));
if (!m_client->synchronousBranchCmd(workingDirectory, branchArgs, &output, errorMessage))
if (!runGitBranchCommand(workingDirectory, branchArgs, &output, errorMessage))
return false;
if (debug)
qDebug() << Q_FUNC_INFO << workingDirectory << output;
// Parse output
m_workingDirectory = workingDirectory;
m_branches.clear();
m_currentBranch = -1;
const QStringList branches = output.split(QLatin1Char('\n'));
const int branchCount = branches.size();
bool isCurrent;
......@@ -110,25 +129,122 @@ bool BranchModel::refresh(const QString &workingDirectory, QString *errorMessage
if (newBranch.parse(branches.at(b), &isCurrent)) {
m_branches.push_back(newBranch);
if (isCurrent)
m_currentBranch = b;
*currentBranch = b;
}
}
reset();
return true;
}
QString BranchModel::toolTip(const QString &sha) const
int RemoteBranchModel::findBranchByName(const QString &name) const
{
// Show the sha description excluding diff as toolTip
const int count = branchCount();
for (int i = 0; i < count; i++)
if (branchName(i) == name)
return i;
return -1;
}
// --- LocalBranchModel
LocalBranchModel::LocalBranchModel(GitClient *client, QObject *parent) :
RemoteBranchModel(client, parent),
m_typeHere(tr("<New branch>")),
m_typeHereToolTip(tr("Type to create a new branch")),
m_currentBranch(-1)
{
}
int LocalBranchModel::currentBranch() const
{
return m_currentBranch;
}
bool LocalBranchModel::isNewBranchRow(int row) const
{
return row >= branchCount();
}
Qt::ItemFlags LocalBranchModel::flags(const QModelIndex & index) const
{
if (isNewBranchRow(index))
return Qt::ItemIsEditable|Qt::ItemIsSelectable|Qt::ItemIsEnabled| Qt::ItemIsUserCheckable;
return RemoteBranchModel::flags(index) | Qt::ItemIsUserCheckable;
}
int LocalBranchModel::rowCount(const QModelIndex & /* parent */) const
{
return branchCount() + 1;
}
QVariant LocalBranchModel::data(const QModelIndex &index, int role) const
{
if (isNewBranchRow(index)) {
switch (role) {
case Qt::DisplayRole:
return m_typeHere;
case Qt::ToolTipRole:
return m_typeHereToolTip;
case Qt::CheckStateRole:
return QVariant(false);
}
return QVariant();
}
if (role == Qt::CheckStateRole)
return index.row() == m_currentBranch ? Qt::Checked : Qt::Unchecked;
return RemoteBranchModel::data(index, role);
}
bool LocalBranchModel::refresh(const QString &workingDirectory, QString *errorMessage)
{
return refreshBranches(workingDirectory, false, &m_currentBranch, errorMessage);
}
bool LocalBranchModel::checkNewBranchName(const QString &name) const
{
// Syntax
const QRegExp pattern(QLatin1String("[a-zA-Z0-9-_]+"));
if (!pattern.exactMatch(name))
return false;
// existing
if (findBranchByName(name) != -1)
return false;
return true;
}
bool LocalBranchModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// Verify
if (role != Qt::EditRole || index.row() < branchCount())
return false;
const QString branchName = value.toString();
const bool ok = checkNewBranchName(branchName);
if (debug)
qDebug() << Q_FUNC_INFO << branchName << ok;
if (!ok)
return false;
// Create
QString output;
QString errorMessage;
if (!m_client->synchronousShow(m_workingDirectory, sha, &output, &errorMessage))
return errorMessage;
// Remove 'diff' output
const int diffPos = output.indexOf(QLatin1String("\ndiff --"));
if (diffPos != -1)
output.remove(diffPos, output.size() - diffPos);
return output;
if (!runGitBranchCommand(workingDirectory(), QStringList(branchName), &output, &errorMessage))
return false;
m_newBranch = branchName;
// Start a delayed complete refresh and return true for now.
QTimer::singleShot(0, this, SLOT(slotNewBranchDelayedRefresh()));
return true;
}
void LocalBranchModel::slotNewBranchDelayedRefresh()
{
if (debug)
qDebug() << Q_FUNC_INFO;
QString errorMessage;
if (!refresh(workingDirectory(), &errorMessage)) {
qWarning("%s", qPrintable(errorMessage));
return;
}
emit newBranchCreated(m_newBranch);
}
}
......
......@@ -3,27 +3,25 @@
#include <QtCore/QAbstractListModel>
#include <QtCore/QList>
#include <QtCore/QVariant>
namespace Git {
namespace Internal {
class GitClient;
/* A model to list git branches in a simple list of branch names. Local
* branches will have a read-only checkbox indicating the current one. The
* [delayed] tooltip describes the latest commit. */
/* A read-only model to list git remote branches in a simple list of branch names.
* The tooltip describes the latest commit (delayed creation).
* Provides virtuals to be able to derive a local branch model with the notion
* of a "current branch". */
class BranchModel : public QAbstractListModel {
class RemoteBranchModel : public QAbstractListModel {
Q_OBJECT
public:
enum Type { LocalBranches, RemoteBranches };
explicit RemoteBranchModel(GitClient *client, QObject *parent = 0);
explicit BranchModel(GitClient *client,
Type type = LocalBranches,
QObject *parent = 0);
bool refresh(const QString &workingDirectory, QString *errorMessage);
virtual bool refresh(const QString &workingDirectory, QString *errorMessage);
int currentBranch() const;
QString branchName(int row) const;
// QAbstractListModel
......@@ -31,9 +29,12 @@ public:
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
private:
QString toolTip(const QString &sha) const;
int branchCount() const;
QString workingDirectory() const;
int findBranchByName(const QString &name) const;
protected:
struct Branch {
bool parse(const QString &line, bool *isCurrent);
......@@ -43,13 +44,61 @@ private:
};
typedef QList<Branch> BranchList;
const Type m_type;
/* Parse git output and populate m_branches. */
bool refreshBranches(const QString &workingDirectory, bool remoteBranches,
int *currentBranch, QString *errorMessage);
bool runGitBranchCommand(const QString &workingDirectory, const QStringList &additionalArgs, QString *output, QString *errorMessage);
private:
QString toolTip(const QString &sha) const;
const Qt::ItemFlags m_flags;
GitClient *m_client;
GitClient *m_client;
QString m_workingDirectory;
BranchList m_branches;
};
/* LocalBranchModel: Extends RemoteBranchModel by a read-only
* checkable column indicating the current branch. Provides an
* editable "Type here" new-branch-row at the bottom to create
* a new branch. */
class LocalBranchModel : public RemoteBranchModel {
Q_OBJECT
public:
explicit LocalBranchModel(GitClient *client,
QObject *parent = 0);
virtual bool refresh(const QString &workingDirectory, QString *errorMessage);
// is this the "type here" row?
bool isNewBranchRow(int row) const;
bool isNewBranchRow(const QModelIndex & index) const { return isNewBranchRow(index.row()); }
int currentBranch() const;
// QAbstractListModel
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
signals:
void newBranchCreated(const QString &);
private slots:
void slotNewBranchDelayedRefresh();
private:
bool checkNewBranchName(const QString &name) const;
const QVariant m_typeHere;
const QVariant m_typeHereToolTip;
int m_currentBranch;
QString m_newBranch;
};
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment