Gerrit: Determine server by git remote

Currently SSH only.

Change-Id: Ic29ca20e4c63cb5c692e9dc196ba205f0cbc9c6f
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Orgad Shaneh
2017-01-22 00:30:20 +02:00
committed by Orgad Shaneh
parent c317114591
commit 34504ad797
8 changed files with 128 additions and 37 deletions

View File

@@ -27,12 +27,17 @@
#include "gerritmodel.h"
#include "gerritparameters.h"
#include <coreplugin/icore.h>
#include "../gitplugin.h"
#include "../gitclient.h"
#include <utils/qtcassert.h>
#include <utils/fancylineedit.h>
#include <utils/itemviews.h>
#include <utils/progressindicator.h>
#include <utils/theme/theme.h>
#include <coreplugin/icore.h>
#include <utils/asconst.h>
#include <utils/hostosinfo.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
@@ -57,9 +62,12 @@ static const int layoutSpacing = 5;
static const int maxTitleWidth = 350;
GerritDialog::GerritDialog(const QSharedPointer<GerritParameters> &p,
const QSharedPointer<GerritServer> &s,
const QString &repository,
QWidget *parent)
: QDialog(parent)
, m_parameters(p)
, m_server(s)
, m_filterModel(new QSortFilterProxyModel(this))
, m_model(new GerritModel(p, this))
, m_queryModel(new QStringListModel(this))
@@ -72,7 +80,6 @@ GerritDialog::GerritDialog(const QSharedPointer<GerritParameters> &p,
, m_repositoryChooserLabel(new QLabel(tr("Apply in:") + ' ', this))
, m_fetchRunning(false)
{
setWindowTitle(tr("Gerrit %1@%2").arg(p->server.user, p->server.host));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
QGroupBox *changesGroup = new QGroupBox(tr("Changes"));
@@ -171,6 +178,7 @@ GerritDialog::GerritDialog(const QSharedPointer<GerritParameters> &p,
mainLayout->addWidget(splitter);
mainLayout->addWidget(m_buttonBox);
m_repositoryChooser->setPath(repository);
slotCurrentChanged();
slotRefresh();
@@ -255,10 +263,69 @@ void GerritDialog::slotRefresh()
{
const QString &query = m_queryLineEdit->text().trimmed();
updateCompletions(query);
m_model->refresh(query);
updateRemote();
m_model->refresh(m_server, query);
m_treeView->sortByColumn(-1);
}
void GerritDialog::updateRemote()
{
const QString repository = m_repositoryChooser->path();
if (m_repository == repository || !m_repositoryChooser->isValid())
return;
static const QRegularExpression sshPattern(
"^(?:(?<protocol>[^:]+)://)?(?:(?<user>[^@]+)@)?(?<host>[^:/]+)(?::(?<port>\\d+))?");
m_repository = repository;
*m_server = m_parameters->server;
QString errorMessage; // Mute errors. We'll just fallback to the defaults
QMap<QString, QString> remotesList =
Git::Internal::GitPlugin::client()->synchronousRemotesList(repository, &errorMessage);
QStringList remoteUrls;
// Prefer a remote named gerrit
const QString gerritRemote = remotesList.value("gerrit");
if (!gerritRemote.isEmpty()) {
remoteUrls << gerritRemote;
remotesList.remove("gerrit");
}
remoteUrls.append(remotesList.values());
GerritServer server;
for (const QString &r : Utils::asConst(remoteUrls)) {
// Skip local remotes (refer to the root or relative path)
if (r.isEmpty() || r.startsWith('/') || r.startsWith('.'))
continue;
// On Windows, local paths typically starts with <drive>:
if (Utils::HostOsInfo::isWindowsHost() && r[1] == ':')
continue;
QRegularExpressionMatch match = sshPattern.match(r);
if (match.hasMatch()) {
const QString protocol = match.captured("protocol");
if (protocol == "https")
server.type = GerritServer::Https;
else if (protocol == "http")
server.type = GerritServer::Http;
else if (protocol.isEmpty() || protocol == "ssh")
server.type = GerritServer::Ssh;
else
continue;
const QString user = match.captured("user");
server.user = user.isEmpty() ? m_parameters->server.user : user;
server.host = match.captured("host");
server.port = match.captured("port").toUShort();
// Only Ssh is currently supported. In order to extend support for http[s],
// we need to move this logic to the model, and attempt connection to each
// remote (do it only on refresh, not on each path change)
if (server.type == GerritServer::Ssh) {
*m_server = server;
break;
}
}
}
QString user = m_server->user;
if (!user.isEmpty())
user.append('@');
setWindowTitle(tr("Gerrit %1%2").arg(user, m_server->host));
}
void GerritDialog::manageProgressIndicator()
{
if (m_model->state() == GerritModel::Running) {

View File

@@ -51,9 +51,10 @@ class TreeView;
namespace Gerrit {
namespace Internal {
class GerritParameters;
class GerritModel;
class GerritChange;
class GerritModel;
class GerritParameters;
class GerritServer;
class QueryValidatingLineEdit;
class GerritDialog : public QDialog
@@ -61,6 +62,8 @@ class GerritDialog : public QDialog
Q_OBJECT
public:
explicit GerritDialog(const QSharedPointer<GerritParameters> &p,
const QSharedPointer<GerritServer> &s,
const QString &repository,
QWidget *parent = 0);
~GerritDialog();
QString repositoryPath() const;
@@ -83,6 +86,7 @@ private:
void slotFetchCherryPick();
void slotFetchCheckout();
void slotRefresh();
void updateRemote();
void manageProgressIndicator();
@@ -93,6 +97,7 @@ private:
void updateButtons();
const QSharedPointer<GerritParameters> m_parameters;
const QSharedPointer<GerritServer> m_server;
QSortFilterProxyModel *m_filterModel;
GerritModel *m_model;
QStringListModel *m_queryModel;
@@ -109,6 +114,7 @@ private:
QLabel *m_repositoryChooserLabel;
Utils::ProgressIndicator *m_progressIndicator;
QTimer m_progressIndicatorTimer;
QString m_repository;
bool m_fetchRunning;
};

View File

@@ -84,10 +84,12 @@ QDebug operator<<(QDebug d, const GerritChange &c)
}
// Format default Url for a change
static inline QString defaultUrl(const QSharedPointer<GerritParameters> &p, int gerritNumber)
static inline QString defaultUrl(const QSharedPointer<GerritParameters> &p,
const GerritServer &server,
int gerritNumber)
{
QString result = QLatin1String(p->https ? "https://" : "http://");
result += p->server.host;
result += server.host;
result += '/';
result += QString::number(gerritNumber);
return result;
@@ -194,14 +196,9 @@ QString GerritChange::filterString() const
return result;
}
QStringList GerritChange::gitFetchArguments(const QSharedPointer<GerritParameters> &p) const
QStringList GerritChange::gitFetchArguments(const GerritServer &server) const
{
QStringList arguments;
const QString url = "ssh://" + p->server.sshHostArgument()
+ ':' + QString::number(p->server.port) + '/'
+ project;
arguments << "fetch" << url << currentPatchSet.ref;
return arguments;
return { "fetch", server.url() + '/' + project, currentPatchSet.ref };
}
// Helper class that runs ssh gerrit queries from a list of query argument
@@ -215,6 +212,7 @@ class QueryContext : public QObject {
public:
QueryContext(const QStringList &queries,
const QSharedPointer<GerritParameters> &p,
const GerritServer &server,
QObject *parent = 0);
~QueryContext();
@@ -254,13 +252,16 @@ enum { timeOutMS = 30000 };
QueryContext::QueryContext(const QStringList &queries,
const QSharedPointer<GerritParameters> &p,
const GerritServer &server,
QObject *parent)
: QObject(parent)
, m_queries(queries)
, m_currentQuery(0)
, m_baseArguments({ p->ssh, p->portFlag, QString::number(p->server.port),
p->server.sshHostArgument(), "gerrit" })
{
m_baseArguments << p->ssh;
if (server.port)
m_baseArguments << p->portFlag << QString::number(server.port);
m_baseArguments << server.sshHostArgument() << "gerrit";
connect(&m_process, &QProcess::readyReadStandardError,
this, &QueryContext::readyReadStandardError);
connect(&m_process, &QProcess::readyReadStandardOutput,
@@ -505,13 +506,14 @@ QStandardItem *GerritModel::itemForNumber(int number) const
return 0;
}
void GerritModel::refresh(const QString &query)
void GerritModel::refresh(const QSharedPointer<GerritServer> &server, const QString &query)
{
if (m_query) {
qWarning("%s: Another query is still running", Q_FUNC_INFO);
return;
}
clearData();
m_server = server;
// Assemble list of queries
@@ -521,17 +523,17 @@ void GerritModel::refresh(const QString &query)
else
{
const QString statusOpenQuery = "status:open";
if (m_parameters->server.user.isEmpty()) {
if (m_server->user.isEmpty()) {
queries.push_back(statusOpenQuery);
} else {
// Owned by:
queries.push_back(statusOpenQuery + " owner:" + m_parameters->server.user);
queries.push_back(statusOpenQuery + " owner:" + m_server->user);
// For Review by:
queries.push_back(statusOpenQuery + " reviewer:" + m_parameters->server.user);
queries.push_back(statusOpenQuery + " reviewer:" + m_server->user);
}
}
m_query = new QueryContext(queries, m_parameters, this);
m_query = new QueryContext(queries, m_parameters, *m_server, this);
connect(m_query, &QueryContext::queryFinished, this, &GerritModel::queryFinished);
connect(m_query, &QueryContext::finished, this, &GerritModel::queriesFinished);
emit refreshStateChanged(true);
@@ -572,6 +574,7 @@ void GerritModel::setState(GerritModel::QueryState s)
*/
static bool parseOutput(const QSharedPointer<GerritParameters> &parameters,
const GerritServer &server,
const QByteArray &output,
QList<GerritChangePtr> &result)
{
@@ -645,7 +648,7 @@ static bool parseOutput(const QSharedPointer<GerritParameters> &parameters,
change->number = object.value(numberKey).toString().toInt();
change->url = object.value(urlKey).toString();
if (change->url.isEmpty()) // No "canonicalWebUrl" is in gerrit.config.
change->url = defaultUrl(parameters, change->number);
change->url = defaultUrl(parameters, server, change->number);
change->title = object.value(titleKey).toString();
const QJsonObject ownerJ = object.value(ownerKey).toObject();
change->owner = ownerJ.value(ownerNameKey).toString();
@@ -743,7 +746,7 @@ bool gerritChangeLessThan(const GerritChangePtr &c1, const GerritChangePtr &c2)
void GerritModel::queryFinished(const QByteArray &output)
{
QList<GerritChangePtr> changes;
setState(parseOutput(m_parameters, output, changes) ? Ok : Error);
setState(parseOutput(m_parameters, *m_server, output, changes) ? Ok : Error);
// Populate a hash with indices for faster access.
QHash<int, int> numberIndexHash;

View File

@@ -35,6 +35,7 @@ QT_END_NAMESPACE
namespace Gerrit {
namespace Internal {
class GerritServer;
class GerritParameters;
class QueryContext;
@@ -67,7 +68,7 @@ class GerritChange
public:
bool isValid() const { return number && !url.isEmpty() && !project.isEmpty(); }
QString filterString() const;
QStringList gitFetchArguments(const QSharedPointer<GerritParameters> &p) const;
QStringList gitFetchArguments(const GerritServer &server) const;
QString url;
int number = 0;
@@ -119,7 +120,7 @@ public:
enum QueryState { Idle, Running, Ok, Error };
QueryState state() const { return m_state; }
void refresh(const QString &query);
void refresh(const QSharedPointer<GerritServer> &server, const QString &query);
signals:
void refreshStateChanged(bool isRefreshing); // For disabling the "Refresh" button.
@@ -137,6 +138,7 @@ private:
QList<QStandardItem *> changeToRow(const GerritChangePtr &c) const;
const QSharedPointer<GerritParameters> m_parameters;
QSharedPointer<GerritServer> m_server;
QueryContext *m_query = 0;
QueryState m_state = Idle;
QString m_userName;

View File

@@ -108,6 +108,14 @@ QString GerritServer::sshHostArgument() const
return user.isEmpty() ? host : (user + '@' + host);
}
QString GerritServer::url() const
{
QString res = "ssh://" + sshHostArgument();
if (port)
res += ':' + QString::number(port);
return res;
}
bool GerritParameters::equals(const GerritParameters &rhs) const
{
return server == rhs.server && ssh == rhs.ssh && https == rhs.https;

View File

@@ -46,6 +46,7 @@ public:
GerritServer(const QString &host, unsigned short port, const QString &user, HostType type);
bool operator==(const GerritServer &other) const;
QString sshHostArgument() const;
QString url() const;
QString host;
QString user;

View File

@@ -92,7 +92,7 @@ class FetchContext : public QObject
public:
FetchContext(const QSharedPointer<GerritChange> &change,
const QString &repository, const Utils::FileName &git,
const QSharedPointer<GerritParameters> &p,
const GerritServer &server,
FetchMode fm, QObject *parent = 0);
~FetchContext();
void start();
@@ -120,7 +120,7 @@ private:
const QString m_repository;
const FetchMode m_fetchMode;
const Utils::FileName m_git;
const QSharedPointer<GerritParameters> m_parameters;
const GerritServer m_server;
State m_state;
QProcess m_process;
QFutureInterface<void> m_progress;
@@ -129,14 +129,14 @@ private:
FetchContext::FetchContext(const QSharedPointer<GerritChange> &change,
const QString &repository, const Utils::FileName &git,
const QSharedPointer<GerritParameters> &p,
const GerritServer &server,
FetchMode fm, QObject *parent)
: QObject(parent)
, m_change(change)
, m_repository(repository)
, m_fetchMode(fm)
, m_git(git)
, m_parameters(p)
, m_server(server)
, m_state(FetchState)
{
connect(&m_process, &QProcess::errorOccurred, this, &FetchContext::processError);
@@ -169,7 +169,7 @@ void FetchContext::start()
fp->setKeepOnFinish(FutureProgress::HideOnFinish);
m_progress.reportStarted();
// Order: initialize future before starting the process in case error handling is invoked.
const QStringList args = m_change->gitFetchArguments(m_parameters);
const QStringList args = m_change->gitFetchArguments(m_server);
VcsBase::VcsOutputWindow::appendCommand(m_repository, m_git, args);
m_process.start(m_git.toString(), args);
m_process.closeWriteChannel();
@@ -266,6 +266,7 @@ void FetchContext::terminate()
GerritPlugin::GerritPlugin(QObject *parent)
: QObject(parent)
, m_parameters(new GerritParameters)
, m_server(new GerritServer)
, m_gerritCommand(0), m_pushToGerritCommand(0)
{
}
@@ -348,6 +349,7 @@ void GerritPlugin::push(const QString &topLevel)
// Open or raise the Gerrit dialog window.
void GerritPlugin::openView()
{
const QString repository = GitPlugin::instance()->currentState().topLevel();
if (m_dialog.isNull()) {
while (!m_parameters->isValid()) {
Core::AsynchronousMessageBox::warning(tr("Error"),
@@ -355,7 +357,7 @@ void GerritPlugin::openView()
if (!ICore::showOptionsDialog("Gerrit"))
return;
}
GerritDialog *gd = new GerritDialog(m_parameters, ICore::mainWindow());
GerritDialog *gd = new GerritDialog(m_parameters, m_server, repository, ICore::mainWindow());
gd->setModal(false);
connect(gd, &GerritDialog::fetchDisplay, this,
[this](const QSharedPointer<GerritChange> &change) { fetch(change, FetchDisplay); });
@@ -368,7 +370,7 @@ void GerritPlugin::openView()
m_dialog = gd;
}
if (!m_dialog->isVisible())
m_dialog->setCurrentPath(GitPlugin::instance()->currentState().topLevel());
m_dialog->setCurrentPath(repository);
const Qt::WindowStates state = m_dialog->windowState();
if (state & Qt::WindowMinimized)
m_dialog->setWindowState(state & ~Qt::WindowMinimized);
@@ -414,7 +416,7 @@ void GerritPlugin::fetch(const QSharedPointer<GerritChange> &change, int mode)
foreach (QString remote, remotes) {
if (remote.endsWith(".git"))
remote.chop(4);
if (remote.contains(m_parameters->server.host) && remote.endsWith(change->project)) {
if (remote.contains(m_server->host) && remote.endsWith(change->project)) {
verifiedRepository = true;
break;
}
@@ -426,7 +428,7 @@ void GerritPlugin::fetch(const QSharedPointer<GerritChange> &change, int mode)
QString remote = submoduleData.url;
if (remote.endsWith(".git"))
remote.chop(4);
if (remote.contains(m_parameters->server.host) && remote.endsWith(change->project)
if (remote.contains(m_server->host) && remote.endsWith(change->project)
&& QFile::exists(repository + '/' + submoduleData.dir)) {
repository = QDir::cleanPath(repository + '/' + submoduleData.dir);
verifiedRepository = true;
@@ -440,7 +442,7 @@ void GerritPlugin::fetch(const QSharedPointer<GerritChange> &change, int mode)
ICore::mainWindow(), tr("Remote Not Verified"),
tr("Change host %1\nand project %2\n\nwere not verified among remotes"
" in %3. Select different folder?")
.arg(m_parameters->server.host,
.arg(m_server->host,
change->project,
QDir::toNativeSeparators(repository)),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
@@ -472,7 +474,7 @@ void GerritPlugin::fetch(const QSharedPointer<GerritChange> &change, int mode)
return;
FetchContext *fc = new FetchContext(change, repository, git,
m_parameters, FetchMode(mode), this);
*m_server, FetchMode(mode), this);
connect(fc, &QObject::destroyed, this, &GerritPlugin::fetchFinished);
emit fetchStarted(change);
fc->start();

View File

@@ -46,8 +46,9 @@ namespace Gerrit {
namespace Internal {
class GerritChange;
class GerritParameters;
class GerritDialog;
class GerritParameters;
class GerritServer;
class GerritPlugin : public QObject
{
@@ -77,6 +78,7 @@ private:
void fetch(const QSharedPointer<GerritChange> &change, int mode);
QSharedPointer<GerritParameters> m_parameters;
QSharedPointer<GerritServer> m_server;
QPointer<GerritDialog> m_dialog;
Core::Command *m_gerritCommand;
Core::Command *m_pushToGerritCommand;