forked from qt-creator/qt-creator
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:
committed by
Orgad Shaneh
parent
c317114591
commit
34504ad797
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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> ¶meters,
|
||||
const GerritServer &server,
|
||||
const QByteArray &output,
|
||||
QList<GerritChangePtr> &result)
|
||||
{
|
||||
@@ -645,7 +648,7 @@ static bool parseOutput(const QSharedPointer<GerritParameters> ¶meters,
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user