Commit cb333323 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

VCS[git]: Add branch combo to the checkout wizard.

Provide UI with manual refresh button in BaseCheckoutWizardPage.
Change ProcessCheckoutJob to be able to execute several steps.
Implement in git.
parent 1ae9940b
......@@ -84,8 +84,9 @@ QSharedPointer<VCSBase::AbstractCheckoutJob> CheckoutWizard::createJob(const QLi
args << QLatin1String("checkout") << repository;
const QString workingDirectory = cwp->path();
*checkoutPath = workingDirectory + QLatin1Char('/') + repository;
VCSBase::AbstractCheckoutJob *job = new VCSBase::ProcessCheckoutJob(binary, settings.addOptions(args),
workingDirectory);
VCSBase::ProcessCheckoutJob *job = new VCSBase::ProcessCheckoutJob;
job->addStep(binary, settings.addOptions(args), workingDirectory);
return QSharedPointer<VCSBase::AbstractCheckoutJob>(job);
}
......
......@@ -39,6 +39,7 @@ CheckoutWizardPage::CheckoutWizardPage(QWidget *parent) :
setSubTitle(tr("Specify repository and path."));
setRepositoryLabel(tr("Repository:"));
setDirectoryVisible(false);
setBranchSelectorVisible(false);
}
} // namespace Internal
......
......@@ -34,6 +34,8 @@
#include <vcsbase/checkoutjobs.h>
#include <utils/qtcassert.h>
#include <QtGui/QCheckBox>
namespace Git {
struct CloneWizardPagePrivate {
......@@ -42,12 +44,14 @@ struct CloneWizardPagePrivate {
const QString mainLinePostfix;
const QString gitPostFix;
const QString protocolDelimiter;
QCheckBox *deleteMasterCheckBox;
};
CloneWizardPagePrivate::CloneWizardPagePrivate() :
mainLinePostfix(QLatin1String("/mainline.git")),
gitPostFix(QLatin1String(".git")),
protocolDelimiter(QLatin1String("://"))
protocolDelimiter(QLatin1String("://")),
deleteMasterCheckBox(0)
{
}
......@@ -58,6 +62,9 @@ CloneWizardPage::CloneWizardPage(QWidget *parent) :
setTitle(tr("Location"));
setSubTitle(tr("Specify repository URL, checkout directory and path."));
setRepositoryLabel(tr("Clone URL:"));
d->deleteMasterCheckBox = new QCheckBox(tr("Delete master branch"));
addControl(d->deleteMasterCheckBox);
setDeleteMasterBranch(true);
}
CloneWizardPage::~CloneWizardPage()
......@@ -65,6 +72,16 @@ CloneWizardPage::~CloneWizardPage()
delete d;
}
bool CloneWizardPage::deleteMasterBranch() const
{
return d->deleteMasterCheckBox->isChecked();
}
void CloneWizardPage::setDeleteMasterBranch(bool v)
{
d->deleteMasterCheckBox->setChecked(v);
}
QString CloneWizardPage::directoryFromRepository(const QString &urlIn) const
{
/* Try to figure out a good directory name from something like:
......@@ -105,17 +122,55 @@ QString CloneWizardPage::directoryFromRepository(const QString &urlIn) const
QSharedPointer<VCSBase::AbstractCheckoutJob> CloneWizardPage::createCheckoutJob(QString *checkoutPath) const
{
const Internal::GitClient *client = Internal::GitPlugin::instance()->gitClient();
QStringList args = client->binary();
const QString workingDirectory = path();
const QString checkoutDir = directory();
*checkoutPath = workingDirectory + QLatin1Char('/') + checkoutDir;
QStringList baseArgs = client->binary();
const QString binary = baseArgs.front();
baseArgs.pop_front();
QStringList args;
args << QLatin1String("clone") << repository() << checkoutDir;
const QString binary = args.front();
args.pop_front();
VCSBase::AbstractCheckoutJob *job =
new VCSBase::ProcessCheckoutJob(binary, args, workingDirectory,
client->processEnvironment());
VCSBase::ProcessCheckoutJob *job = new VCSBase::ProcessCheckoutJob;
const QProcessEnvironment env = client->processEnvironment();
// 1) Basic checkout step
job->addStep(binary, baseArgs + args, workingDirectory, env);
const QString checkoutBranch = branch();
// 2) Checkout branch, change to checkoutDir
const QString masterBranch = QLatin1String("master");
if (!checkoutBranch.isEmpty() && checkoutBranch != masterBranch) {
// Create branch
args.clear();
args << QLatin1String("branch") << QLatin1String("--track")
<< checkoutBranch << (QLatin1String("origin/") + checkoutBranch);
job->addStep(binary, baseArgs + args, checkoutDir, env);
// Checkout branch
args.clear();
args << QLatin1String("checkout") << checkoutBranch;
job->addStep(binary, baseArgs + args, checkoutDir, env);
// Delete master if desired
if (deleteMasterBranch()) {
args.clear();
args << QLatin1String("branch") << QLatin1String("-D") << masterBranch;
job->addStep(binary, baseArgs + args, checkoutDir, env);
}
}
return QSharedPointer<VCSBase::AbstractCheckoutJob>(job);
}
QStringList CloneWizardPage::branches(const QString &repository, int *current)
{
// Run git on remote repository if URL is complete
*current = 0;
if (!repository.endsWith(d->gitPostFix))
return QStringList();
const QStringList branches = Internal::GitPlugin::instance()->gitClient()->synchronousRepositoryBranches(repository);
*current = branches.indexOf(QLatin1String("master"));
return branches;
}
} // namespace Git
......@@ -46,6 +46,7 @@ struct CloneWizardPagePrivate;
class CloneWizardPage : public VCSBase::BaseCheckoutWizardPage
{
Q_OBJECT
Q_PROPERTY(bool deleteMasterBranch READ deleteMasterBranch WRITE setDeleteMasterBranch)
public:
explicit CloneWizardPage(QWidget *parent = 0);
virtual ~CloneWizardPage();
......@@ -54,6 +55,10 @@ public:
protected:
virtual QString directoryFromRepository(const QString &r) const;
virtual QStringList branches(const QString &repository, int *current);
bool deleteMasterBranch() const;
void setDeleteMasterBranch(bool v);
private:
CloneWizardPagePrivate *d;
......
......@@ -1265,6 +1265,28 @@ GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory,
return StatusChanged;
}
// Quietly retrieve branch list of remote repository URL
QStringList GitClient::synchronousRepositoryBranches(const QString &repositoryURL)
{
QStringList arguments(QLatin1String("ls-remote"));
arguments << QLatin1String("--heads") << repositoryURL;
const unsigned flags =
VCSBase::VCSBasePlugin::SshPasswordPrompt|
VCSBase::VCSBasePlugin::SuppressStdErrInLogWindow|
VCSBase::VCSBasePlugin::SuppressFailMessageInLogWindow;
const Utils::SynchronousProcessResponse resp = synchronousGit(QString(), arguments, flags);
QStringList branches;
if (resp.result == Utils::SynchronousProcessResponse::Finished) {
// split "82bfad2f51d34e98b18982211c82220b8db049b<tab>refs/heads/master"
foreach(const QString &line, resp.stdOut.split(QLatin1Char('\n'))) {
const int slashPos = line.lastIndexOf(QLatin1Char('/'));
if (slashPos != -1)
branches.push_back(line.mid(slashPos + 1));
}
}
return branches;
}
void GitClient::launchGitK(const QString &workingDirectory)
{
VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance();
......
......@@ -205,6 +205,7 @@ public:
bool *onBranch = 0);
void launchGitK(const QString &workingDirectory);
QStringList synchronousRepositoryBranches(const QString &repositoryURL);
GitSettings settings() const;
void setSettings(const GitSettings &s);
......
......@@ -86,7 +86,7 @@ QSharedPointer<VCSBase::AbstractCheckoutJob> CloneWizard::createJob(const QList<
args << QLatin1String("clone") << page->repository() << directory;
*checkoutPath = path + QLatin1Char('/') + directory;
return QSharedPointer<VCSBase::AbstractCheckoutJob>(new VCSBase::ProcessCheckoutJob(settings.binary(),
args, path));
VCSBase::ProcessCheckoutJob *job = new VCSBase::ProcessCheckoutJob;
job->addStep(settings.binary(), args, path);
return QSharedPointer<VCSBase::AbstractCheckoutJob>(job);
}
......@@ -37,6 +37,7 @@ CloneWizardPage::CloneWizardPage(QWidget *parent)
setTitle(tr("Location"));
setSubTitle(tr("Specify repository URL, checkout directory and path."));
setRepositoryLabel(tr("Clone URL:"));
setBranchSelectorVisible(false);
}
QString CloneWizardPage::directoryFromRepository(const QString &repository) const
......
......@@ -86,8 +86,8 @@ QSharedPointer<VCSBase::AbstractCheckoutJob> CheckoutWizard::createJob(const QLi
const QStringList completeArgs = settings.hasAuthentication() ?
SubversionPlugin::addAuthenticationOptions(args, settings.user, settings.password) :
args;
VCSBase::AbstractCheckoutJob *job = new VCSBase::ProcessCheckoutJob(binary, completeArgs,
workingDirectory);
VCSBase::ProcessCheckoutJob *job = new VCSBase::ProcessCheckoutJob;
job->addStep(binary, completeArgs, workingDirectory);
return QSharedPointer<VCSBase::AbstractCheckoutJob>(job);
}
......
......@@ -38,6 +38,7 @@ CheckoutWizardPage::CheckoutWizardPage(QWidget *parent) :
setTitle(tr("Location"));
setSubTitle(tr("Specify repository URL, checkout directory and path."));
setRepositoryLabel(tr("Repository:"));
setBranchSelectorVisible(false);
}
QString CheckoutWizardPage::directoryFromRepository(const QString &repoIn) const
......
......@@ -30,6 +30,8 @@
#include "basecheckoutwizardpage.h"
#include "ui_basecheckoutwizardpage.h"
#include <QtGui/QIcon>
namespace VCSBase {
struct BaseCheckoutWizardPagePrivate {
......@@ -54,6 +56,10 @@ BaseCheckoutWizardPage::BaseCheckoutWizardPage(QWidget *parent) :
d->ui.pathChooser->setExpectedKind(Utils::PathChooser::Directory);
connect(d->ui.pathChooser, SIGNAL(validChanged()), this, SLOT(slotChanged()));
d->ui.branchComboBox->setEnabled(false);
d->ui.branchRefreshToolButton->setIcon(QIcon(QLatin1String(":/locator/images/reload.png")));
connect(d->ui.branchRefreshToolButton, SIGNAL(clicked()), this, SLOT(slotRefreshBranches()));
}
BaseCheckoutWizardPage::~BaseCheckoutWizardPage()
......@@ -61,6 +67,28 @@ BaseCheckoutWizardPage::~BaseCheckoutWizardPage()
delete d;
}
void BaseCheckoutWizardPage::addControl(QWidget *w)
{
d->ui.formLayout->addRow(w);
}
void BaseCheckoutWizardPage::addControl(QString &description, QWidget *w)
{
d->ui.formLayout->addRow(description, w);
}
bool BaseCheckoutWizardPage::isBranchSelectorVisible() const
{
return d->ui.branchComboBox->isVisible();
}
void BaseCheckoutWizardPage::setBranchSelectorVisible(bool v)
{
d->ui.branchComboBox->setVisible(v);
d->ui.branchRefreshToolButton->setVisible(v);
d->ui.branchLabel->setVisible(v);
}
void BaseCheckoutWizardPage::setRepositoryLabel(const QString &l)
{
d->ui.repositoryLabel->setText(l);
......@@ -112,6 +140,35 @@ void BaseCheckoutWizardPage::setRepository(const QString &r)
d->ui.repositoryLineEdit->setText(r);
}
QString BaseCheckoutWizardPage::branch() const
{
return d->ui.branchComboBox->currentText();
}
void BaseCheckoutWizardPage::setBranch(const QString &b)
{
const int index = d->ui.branchComboBox->findText(b);
if (index != -1)
d->ui.branchComboBox->setCurrentIndex(index);
}
void BaseCheckoutWizardPage::slotRefreshBranches()
{
if (!isBranchSelectorVisible())
return;
// Refresh branch list on demand. This is hard to make
// automagically since there can be network slowness/timeouts, etc.
int current;
const QStringList branchList = branches(repository(), &current);
d->ui.branchComboBox->clear();
d->ui.branchComboBox->setEnabled(branchList.size() > 1);
if (!branchList.isEmpty()) {
d->ui.branchComboBox->addItems(branchList);
if (current >= 0 && current < branchList.size())
d->ui.branchComboBox->setCurrentIndex(current);
}
}
void BaseCheckoutWizardPage::slotRepositoryChanged(const QString &repo)
{
// Derive directory name from repository unless user manually edited it.
......@@ -125,6 +182,11 @@ QString BaseCheckoutWizardPage::directoryFromRepository(const QString &r) const
return r;
}
QStringList BaseCheckoutWizardPage::branches(const QString &, int *)
{
return QStringList();
}
void BaseCheckoutWizardPage::slotDirectoryEdited()
{
d->m_directoryEdited = true;
......
......@@ -49,6 +49,7 @@ struct BaseCheckoutWizardPagePrivate;
class VCSBASE_EXPORT BaseCheckoutWizardPage : public QWizardPage {
Q_OBJECT
Q_PROPERTY(bool isBranchSelectorVisible READ isBranchSelectorVisible WRITE setBranchSelectorVisible)
public:
BaseCheckoutWizardPage(QWidget *parent = 0);
~BaseCheckoutWizardPage();
......@@ -65,22 +66,36 @@ public:
bool isRepositoryReadOnly() const;
void setRepositoryReadOnly(bool v);
QString branch() const;
void setBranch(const QString &);
virtual bool isComplete() const;
bool isBranchSelectorVisible() const ;
protected:
void changeEvent(QEvent *e);
void setRepositoryLabel(const QString &l);
void setDirectoryVisible(bool v);
void setBranchSelectorVisible(bool v);
/* Determine a checkout directory name from
* repository URL, that is, "protocol:/project" -> "project". */
virtual QString directoryFromRepository(const QString &r) const;
/* Return list of branches of that repository, defaults to empty. */
virtual QStringList branches(const QString &repository, int *current);
/* Add additional controls */
void addControl(QWidget *w);
void addControl(QString &description, QWidget *w);
private slots:
void slotRepositoryChanged(const QString &url);
void slotDirectoryEdited();
void slotChanged();
void slotRefreshBranches();
private:
BaseCheckoutWizardPagePrivate *d;
......
......@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>464</width>
<height>302</height>
<width>483</width>
<height>237</height>
</rect>
</property>
<property name="windowTitle">
......@@ -40,7 +40,35 @@
</widget>
</item>
<item row="2" column="1">
<widget class="Utils::PathChooser" name="pathChooser"/>
<widget class="Utils::PathChooser" name="pathChooser" native="true"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="branchLabel">
<property name="text">
<string>Branches:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="branchHorizontalLayout">
<item>
<widget class="QComboBox" name="branchComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="branchRefreshToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
......
......@@ -30,10 +30,13 @@
#include "checkoutjobs.h"
#include <vcsbaseplugin.h>
#include <vcsbaseoutputwindow.h>
#include <QtCore/QDebug>
#include <QtCore/QQueue>
#include <QtCore/QDir>
#include <utils/synchronousprocess.h>
#include <utils/qtcassert.h>
enum { debug = 0 };
namespace VCSBase {
......@@ -43,15 +46,27 @@ AbstractCheckoutJob::AbstractCheckoutJob(QObject *parent) :
{
}
struct ProcessCheckoutJobStep
{
ProcessCheckoutJobStep() {}
explicit ProcessCheckoutJobStep(const QString &bin,
const QStringList &args,
const QString &workingDir,
QProcessEnvironment env) :
binary(bin), arguments(args), workingDirectory(workingDir), environment(env) {}
QString binary;
QStringList arguments;
QString workingDirectory;
QProcessEnvironment environment;
};
struct ProcessCheckoutJobPrivate {
ProcessCheckoutJobPrivate(const QString &binary,
const QStringList &args,
const QString &workingDirectory,
QProcessEnvironment env);
ProcessCheckoutJobPrivate();
QSharedPointer<QProcess> process;
const QString binary;
const QStringList args;
QQueue<ProcessCheckoutJobStep> stepQueue;
QString binary;
};
// Use a terminal-less process to suppress SSH prompts.
......@@ -63,30 +78,15 @@ static inline QSharedPointer<QProcess> createProcess()
return Utils::SynchronousProcess::createProcess(flags);
}
ProcessCheckoutJobPrivate::ProcessCheckoutJobPrivate(const QString &b,
const QStringList &a,
const QString &workingDirectory,
QProcessEnvironment processEnv) :
process(createProcess()),
binary(b),
args(a)
ProcessCheckoutJobPrivate::ProcessCheckoutJobPrivate() :
process(createProcess())
{
if (!workingDirectory.isEmpty())
process->setWorkingDirectory(workingDirectory);
VCSBasePlugin::setProcessEnvironment(&processEnv, false);
process->setProcessEnvironment(processEnv);
}
ProcessCheckoutJob::ProcessCheckoutJob(const QString &binary,
const QStringList &args,
const QString &workingDirectory,
const QProcessEnvironment &env,
QObject *parent) :
ProcessCheckoutJob::ProcessCheckoutJob(QObject *parent) :
AbstractCheckoutJob(parent),
d(new ProcessCheckoutJobPrivate(binary, args, workingDirectory, env))
d(new ProcessCheckoutJobPrivate)
{
if (debug)
qDebug() << "ProcessCheckoutJob" << binary << args << workingDirectory;
connect(d->process.data(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(slotError(QProcess::ProcessError)));
connect(d->process.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotFinished(int,QProcess::ExitStatus)));
connect(d->process.data(), SIGNAL(readyReadStandardOutput()), this, SLOT(slotOutput()));
......@@ -99,6 +99,16 @@ ProcessCheckoutJob::~ProcessCheckoutJob()
delete d;
}
void ProcessCheckoutJob::addStep(const QString &binary,
const QStringList &args,
const QString &workingDirectory,
const QProcessEnvironment &env)
{
if (debug)
qDebug() << "ProcessCheckoutJob::addStep" << binary << args << workingDirectory;
d->stepQueue.enqueue(ProcessCheckoutJobStep(binary, args, workingDirectory, env));
}
void ProcessCheckoutJob::slotOutput()
{
const QByteArray data = d->process->readAllStandardOutput();
......@@ -130,7 +140,7 @@ void ProcessCheckoutJob::slotFinished (int exitCode, QProcess::ExitStatus exitSt
case QProcess::NormalExit:
emit output(tr("The process terminated with exit code %1.").arg(exitCode));
if (exitCode == 0) {
emit succeeded();
slotNext();
} else {
emit failed(tr("The process returned exit code %1.").arg(exitCode));
}
......@@ -143,7 +153,28 @@ void ProcessCheckoutJob::slotFinished (int exitCode, QProcess::ExitStatus exitSt
void ProcessCheckoutJob::start()
{
d->process->start(d->binary, d->args);
QTC_ASSERT(!d->stepQueue.empty(), return)
slotNext();
}
void ProcessCheckoutJob::slotNext()
{
if (d->stepQueue.isEmpty()) {
emit succeeded();
return;
}
// Launch next
const ProcessCheckoutJobStep step = d->stepQueue.dequeue();
d->process->setWorkingDirectory(step.workingDirectory);
// Set up SSH correctly.
QProcessEnvironment processEnv = step.environment;
VCSBasePlugin::setProcessEnvironment(&processEnv, false);
d->process->setProcessEnvironment(processEnv);
d->binary = step.binary;
emit output(VCSBaseOutputWindow::msgExecutionLogEntry(step.workingDirectory, d->binary, step.arguments));
d->process->start(d->binary, step.arguments);
}
void ProcessCheckoutJob::cancel()
......
......@@ -72,13 +72,14 @@ class VCSBASE_EXPORT ProcessCheckoutJob : public AbstractCheckoutJob
{
Q_OBJECT
public:
explicit ProcessCheckoutJob(const QString &binary,
const QStringList &args,
const QString &workingDirectory = QString(),
const QProcessEnvironment &env = QProcessEnvironment::systemEnvironment(),
QObject *parent = 0);
explicit ProcessCheckoutJob(QObject *parent = 0);
virtual ~ProcessCheckoutJob();
void addStep(const QString &binary,
const QStringList &args,
const QString &workingDirectory = QString(),
const QProcessEnvironment &env = QProcessEnvironment::systemEnvironment());
virtual void start();
virtual void cancel();
......@@ -86,6 +87,7 @@ private slots:
void slotError(QProcess::ProcessError error);
void slotFinished (int exitCode, QProcess::ExitStatus exitStatus);
void slotOutput();
void slotNext();
private:
ProcessCheckoutJobPrivate *d;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment