forked from qt-creator/qt-creator
Git: Reduce sync processes
Change-Id: I5d83636d4a018464ba6f248aa818fb8f840df9fc Reviewed-by: André Hartmann <aha_1980@gmx.de> Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
committed by
Orgad Shaneh
parent
6a0a4ac5e4
commit
d20f543619
@@ -229,6 +229,7 @@ public:
|
||||
QString currentSha;
|
||||
QDateTime currentDateTime;
|
||||
QStringList obsoleteLocalBranches;
|
||||
std::unique_ptr<TaskTree> refreshTask;
|
||||
bool oldBranchesIncluded = false;
|
||||
|
||||
struct OldEntry
|
||||
@@ -399,50 +400,83 @@ void BranchModel::clear()
|
||||
d->obsoleteLocalBranches.clear();
|
||||
}
|
||||
|
||||
bool BranchModel::refresh(const FilePath &workingDirectory, QString *errorMessage)
|
||||
void BranchModel::refresh(const FilePath &workingDirectory, ShowError showError)
|
||||
{
|
||||
if (d->refreshTask) {
|
||||
endResetModel(); // for the running task tree.
|
||||
d->refreshTask.reset(); // old running tree is reset, no handlers are being called
|
||||
}
|
||||
beginResetModel();
|
||||
clear();
|
||||
if (workingDirectory.isEmpty()) {
|
||||
endResetModel();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
d->currentSha = d->client->synchronousTopRevision(workingDirectory, &d->currentDateTime);
|
||||
QStringList args = {"--format=%(objectname)\t%(refname)\t%(upstream:short)\t"
|
||||
"%(*objectname)\t%(committerdate:raw)\t%(*committerdate:raw)",
|
||||
"refs/heads/**",
|
||||
"refs/remotes/**"};
|
||||
if (d->client->settings().showTags.value())
|
||||
args << "refs/tags/**";
|
||||
QString output;
|
||||
if (!d->client->synchronousForEachRefCmd(workingDirectory, args, &output, errorMessage)) {
|
||||
using namespace Tasking;
|
||||
const Process topRevisionProc =
|
||||
d->client->topRevision(workingDirectory,
|
||||
[=](const QString &ref, const QDateTime &dateTime) {
|
||||
d->currentSha = ref;
|
||||
d->currentDateTime = dateTime;
|
||||
});
|
||||
|
||||
const auto setupForEachRef = [=](QtcProcess &process) {
|
||||
d->workingDirectory = workingDirectory;
|
||||
QStringList args = {"for-each-ref",
|
||||
"--format=%(objectname)\t%(refname)\t%(upstream:short)\t"
|
||||
"%(*objectname)\t%(committerdate:raw)\t%(*committerdate:raw)",
|
||||
"refs/heads/**",
|
||||
"refs/remotes/**"};
|
||||
if (d->client->settings().showTags.value())
|
||||
args << "refs/tags/**";
|
||||
d->client->setupCommand(process, workingDirectory, args);
|
||||
};
|
||||
|
||||
const auto forEachRefDone = [=](const QtcProcess &process) {
|
||||
const QString output = process.stdOut();
|
||||
const QStringList lines = output.split('\n');
|
||||
for (const QString &l : lines)
|
||||
d->parseOutputLine(l);
|
||||
d->flushOldEntries();
|
||||
|
||||
d->updateAllUpstreamStatus(d->rootNode->children.at(LocalBranches));
|
||||
if (d->currentBranch) {
|
||||
if (d->currentBranch->isLocal())
|
||||
d->currentBranch = nullptr;
|
||||
setCurrentBranch();
|
||||
}
|
||||
if (!d->currentBranch) {
|
||||
BranchNode *local = d->rootNode->children.at(LocalBranches);
|
||||
d->currentBranch = d->headNode = new BranchNode(
|
||||
Tr::tr("Detached HEAD"), "HEAD", {}, d->currentDateTime);
|
||||
local->prepend(d->headNode);
|
||||
}
|
||||
};
|
||||
|
||||
const auto forEachRefError = [=](const QtcProcess &process) {
|
||||
if (showError == ShowError::No)
|
||||
return;
|
||||
const QString message = Tr::tr("Cannot run \"%1\" in \"%2\": %3")
|
||||
.arg("git for-each-ref")
|
||||
.arg(workingDirectory.toUserOutput())
|
||||
.arg(process.cleanedStdErr());
|
||||
VcsBase::VcsOutputWindow::appendError(message);
|
||||
};
|
||||
|
||||
const auto finalize = [this] {
|
||||
endResetModel();
|
||||
return false;
|
||||
}
|
||||
d->refreshTask.release()->deleteLater();
|
||||
};
|
||||
|
||||
d->workingDirectory = workingDirectory;
|
||||
const QStringList lines = output.split('\n');
|
||||
for (const QString &l : lines)
|
||||
d->parseOutputLine(l);
|
||||
d->flushOldEntries();
|
||||
|
||||
d->updateAllUpstreamStatus(d->rootNode->children.at(LocalBranches));
|
||||
if (d->currentBranch) {
|
||||
if (d->currentBranch->isLocal())
|
||||
d->currentBranch = nullptr;
|
||||
setCurrentBranch();
|
||||
}
|
||||
if (!d->currentBranch) {
|
||||
BranchNode *local = d->rootNode->children.at(LocalBranches);
|
||||
d->currentBranch = d->headNode = new BranchNode(Tr::tr("Detached HEAD"), "HEAD", QString(),
|
||||
d->currentDateTime);
|
||||
local->prepend(d->headNode);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
|
||||
return true;
|
||||
const Group root {
|
||||
topRevisionProc,
|
||||
Process(setupForEachRef, forEachRefDone, forEachRefError),
|
||||
OnGroupDone(finalize),
|
||||
OnGroupError(finalize)
|
||||
};
|
||||
d->refreshTask.reset(new TaskTree(root));
|
||||
d->refreshTask->start();
|
||||
}
|
||||
|
||||
void BranchModel::setCurrentBranch()
|
||||
@@ -469,7 +503,7 @@ void BranchModel::renameBranch(const QString &oldName, const QString &newName)
|
||||
&output, &errorMessage))
|
||||
VcsOutputWindow::appendError(errorMessage);
|
||||
else
|
||||
refresh(d->workingDirectory, &errorMessage);
|
||||
refresh(d->workingDirectory);
|
||||
}
|
||||
|
||||
void BranchModel::renameTag(const QString &oldName, const QString &newName)
|
||||
@@ -482,7 +516,7 @@ void BranchModel::renameTag(const QString &oldName, const QString &newName)
|
||||
&output, &errorMessage)) {
|
||||
VcsOutputWindow::appendError(errorMessage);
|
||||
} else {
|
||||
refresh(d->workingDirectory, &errorMessage);
|
||||
refresh(d->workingDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ public:
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
void clear();
|
||||
bool refresh(const Utils::FilePath &workingDirectory, QString *errorMessage);
|
||||
enum class ShowError { No, Yes };
|
||||
void refresh(const Utils::FilePath &workingDirectory, ShowError showError = ShowError::No);
|
||||
|
||||
void renameBranch(const QString &oldName, const QString &newName);
|
||||
void renameTag(const QString &oldName, const QString &newName);
|
||||
|
||||
@@ -160,9 +160,7 @@ void BranchView::refresh(const FilePath &repository, bool force)
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
QString errorMessage;
|
||||
if (!m_model->refresh(m_repository, &errorMessage))
|
||||
VcsBase::VcsOutputWindow::appendError(errorMessage);
|
||||
m_model->refresh(m_repository, BranchModel::ShowError::Yes);
|
||||
}
|
||||
|
||||
void BranchView::refreshCurrentBranch()
|
||||
@@ -225,6 +223,7 @@ void BranchView::slotCustomContextMenu(const QPoint &point)
|
||||
const bool isTag = m_model->isTag(index);
|
||||
const bool hasActions = m_model->isLeaf(index);
|
||||
const bool currentLocal = m_model->isLocal(currentBranch);
|
||||
std::unique_ptr<TaskTree> taskTree;
|
||||
|
||||
QMenu contextMenu;
|
||||
contextMenu.addAction(Tr::tr("&Add..."), this, &BranchView::add);
|
||||
@@ -268,19 +267,19 @@ void BranchView::slotCustomContextMenu(const QPoint &point)
|
||||
resetMenu->addAction(Tr::tr("&Mixed"), this, [this] { reset("mixed"); });
|
||||
resetMenu->addAction(Tr::tr("&Soft"), this, [this] { reset("soft"); });
|
||||
contextMenu.addMenu(resetMenu);
|
||||
QString mergeTitle;
|
||||
if (isFastForwardMerge()) {
|
||||
contextMenu.addAction(Tr::tr("&Merge \"%1\" into \"%2\" (Fast-Forward)")
|
||||
.arg(indexName, currentName),
|
||||
this, [this] { merge(true); });
|
||||
mergeTitle = Tr::tr("Merge \"%1\" into \"%2\" (No &Fast-Forward)")
|
||||
.arg(indexName, currentName);
|
||||
} else {
|
||||
mergeTitle = Tr::tr("&Merge \"%1\" into \"%2\"")
|
||||
.arg(indexName, currentName);
|
||||
}
|
||||
QAction *mergeAction = contextMenu.addAction(Tr::tr("&Merge \"%1\" into \"%2\"")
|
||||
.arg(indexName, currentName),
|
||||
this,
|
||||
[this] { merge(false); });
|
||||
taskTree.reset(onFastForwardMerge([&] {
|
||||
auto ffMerge = new QAction(
|
||||
Tr::tr("&Merge \"%1\" into \"%2\" (Fast-Forward)").arg(indexName, currentName));
|
||||
connect(ffMerge, &QAction::triggered, this, [this] { merge(true); });
|
||||
contextMenu.insertAction(mergeAction, ffMerge);
|
||||
mergeAction->setText(Tr::tr("Merge \"%1\" into \"%2\" (No &Fast-Forward)")
|
||||
.arg(indexName, currentName));
|
||||
}));
|
||||
|
||||
contextMenu.addAction(mergeTitle, this, [this] { merge(false); });
|
||||
contextMenu.addAction(Tr::tr("&Rebase \"%1\" on \"%2\"")
|
||||
.arg(currentName, indexName),
|
||||
this, &BranchView::rebase);
|
||||
@@ -523,13 +522,50 @@ bool BranchView::reset(const QByteArray &resetType)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BranchView::isFastForwardMerge()
|
||||
TaskTree *BranchView::onFastForwardMerge(const std::function<void()> &callback)
|
||||
{
|
||||
using namespace Tasking;
|
||||
|
||||
const QModelIndex selected = selectedIndex();
|
||||
QTC_CHECK(selected != m_model->currentBranch());
|
||||
|
||||
const QString branch = m_model->fullName(selected, true);
|
||||
return GitClient::instance()->isFastForwardMerge(m_repository, branch);
|
||||
|
||||
struct FastForwardStorage
|
||||
{
|
||||
QString mergeBase;
|
||||
QString topRevision;
|
||||
};
|
||||
|
||||
const TreeStorage<FastForwardStorage> storage;
|
||||
|
||||
GitClient *client = GitClient::instance();
|
||||
const auto setupMergeBase = [=](QtcProcess &process) {
|
||||
client->setupCommand(process, m_repository, {"merge-base", "HEAD", branch});
|
||||
};
|
||||
const auto onMergeBaseDone = [storage](const QtcProcess &process) {
|
||||
storage->mergeBase = process.cleanedStdOut().trimmed();
|
||||
};
|
||||
|
||||
const Process topRevisionProc = client->topRevision(
|
||||
m_repository,
|
||||
[storage](const QString &revision, const QDateTime &) {
|
||||
storage->topRevision = revision;
|
||||
});
|
||||
|
||||
const Group root {
|
||||
Storage(storage),
|
||||
parallel,
|
||||
Process(setupMergeBase, onMergeBaseDone),
|
||||
topRevisionProc,
|
||||
OnGroupDone([storage, callback] {
|
||||
if (storage->mergeBase == storage->topRevision)
|
||||
callback();
|
||||
})
|
||||
};
|
||||
auto taskTree = new TaskTree(root);
|
||||
taskTree->start();
|
||||
return taskTree;
|
||||
}
|
||||
|
||||
bool BranchView::merge(bool allowFastForward)
|
||||
|
||||
@@ -20,6 +20,7 @@ QT_END_NAMESPACE;
|
||||
namespace Utils {
|
||||
class ElidingLabel;
|
||||
class NavigationTreeView;
|
||||
class TaskTree;
|
||||
} // Utils
|
||||
|
||||
namespace Git::Internal {
|
||||
@@ -54,7 +55,7 @@ private:
|
||||
bool remove();
|
||||
bool rename();
|
||||
bool reset(const QByteArray &resetType);
|
||||
bool isFastForwardMerge();
|
||||
Utils::TaskTree *onFastForwardMerge(const std::function<void()> &callback);
|
||||
bool merge(bool allowFastForward);
|
||||
void rebase();
|
||||
bool cherryPick();
|
||||
|
||||
@@ -1730,19 +1730,28 @@ bool GitClient::synchronousRevParseCmd(const FilePath &workingDirectory, const Q
|
||||
}
|
||||
|
||||
// Retrieve head revision
|
||||
QString GitClient::synchronousTopRevision(const FilePath &workingDirectory, QDateTime *dateTime)
|
||||
Utils::Tasking::Process GitClient::topRevision(
|
||||
const FilePath &workingDirectory,
|
||||
const std::function<void(const QString &, const QDateTime &)> &callback)
|
||||
{
|
||||
const QStringList arguments = {"show", "-s", "--pretty=format:%H:%ct", HEAD};
|
||||
const CommandResult result = vcsSynchronousExec(workingDirectory, arguments, RunFlags::NoOutput);
|
||||
if (result.result() != ProcessResult::FinishedWithSuccess)
|
||||
return QString();
|
||||
const QStringList output = result.cleanedStdOut().trimmed().split(':');
|
||||
if (dateTime && output.size() > 1) {
|
||||
bool ok = false;
|
||||
const qint64 timeT = output.at(1).toLongLong(&ok);
|
||||
*dateTime = ok ? QDateTime::fromSecsSinceEpoch(timeT) : QDateTime();
|
||||
}
|
||||
return output.first();
|
||||
using namespace Tasking;
|
||||
|
||||
const auto setupProcess = [=](QtcProcess &process) {
|
||||
setupCommand(process, workingDirectory, {"show", "-s", "--pretty=format:%H:%ct", HEAD});
|
||||
};
|
||||
const auto onProcessDone = [=](const QtcProcess &process) {
|
||||
const QStringList output = process.cleanedStdOut().trimmed().split(':');
|
||||
QDateTime dateTime;
|
||||
if (output.size() > 1) {
|
||||
bool ok = false;
|
||||
const qint64 timeT = output.at(1).toLongLong(&ok);
|
||||
if (ok)
|
||||
dateTime = QDateTime::fromSecsSinceEpoch(timeT);
|
||||
}
|
||||
callback(output.first(), dateTime);
|
||||
};
|
||||
|
||||
return Process(setupProcess, onProcessDone);
|
||||
}
|
||||
|
||||
bool GitClient::isRemoteCommit(const FilePath &workingDirectory, const QString &commit)
|
||||
@@ -1752,13 +1761,6 @@ bool GitClient::isRemoteCommit(const FilePath &workingDirectory, const QString &
|
||||
return !result.rawStdOut().isEmpty();
|
||||
}
|
||||
|
||||
bool GitClient::isFastForwardMerge(const FilePath &workingDirectory, const QString &branch)
|
||||
{
|
||||
const CommandResult result = vcsSynchronousExec(workingDirectory,
|
||||
{"merge-base", HEAD, branch}, RunFlags::NoOutput);
|
||||
return result.cleanedStdOut().trimmed() == synchronousTopRevision(workingDirectory);
|
||||
}
|
||||
|
||||
// Format an entry in a one-liner for selection list using git log.
|
||||
QString GitClient::synchronousShortDescription(const FilePath &workingDirectory, const QString &revision,
|
||||
const QString &format) const
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/futuresynchronizer.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
@@ -241,9 +242,10 @@ public:
|
||||
QString synchronousTopic(const Utils::FilePath &workingDirectory) const;
|
||||
bool synchronousRevParseCmd(const Utils::FilePath &workingDirectory, const QString &ref,
|
||||
QString *output, QString *errorMessage = nullptr) const;
|
||||
QString synchronousTopRevision(const Utils::FilePath &workingDirectory, QDateTime *dateTime = nullptr);
|
||||
Utils::Tasking::Process topRevision(
|
||||
const Utils::FilePath &workingDirectory,
|
||||
const std::function<void(const QString &, const QDateTime &)> &callback);
|
||||
bool isRemoteCommit(const Utils::FilePath &workingDirectory, const QString &commit);
|
||||
bool isFastForwardMerge(const Utils::FilePath &workingDirectory, const QString &branch);
|
||||
|
||||
void fetch(const Utils::FilePath &workingDirectory, const QString &remote);
|
||||
void pull(const Utils::FilePath &workingDirectory, bool rebase);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <utils/commandline.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
@@ -89,6 +90,16 @@ VcsCommand *VcsBaseClientImpl::createCommand(const FilePath &workingDirectory,
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void VcsBaseClientImpl::setupCommand(Utils::QtcProcess &process,
|
||||
const FilePath &workingDirectory,
|
||||
const QStringList &args) const
|
||||
{
|
||||
process.setEnvironment(processEnvironment());
|
||||
process.setWorkingDirectory(workingDirectory);
|
||||
process.setCommand({vcsBinary(), args});
|
||||
process.setUseCtrlCStub(true);
|
||||
}
|
||||
|
||||
void VcsBaseClientImpl::enqueueJob(VcsCommand *cmd, const QStringList &args,
|
||||
const ExitCodeInterpreter &interpreter) const
|
||||
{
|
||||
|
||||
@@ -23,6 +23,10 @@ class QTextCodec;
|
||||
class QToolBar;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
class QtcProcess;
|
||||
}
|
||||
|
||||
namespace VcsBase {
|
||||
|
||||
class CommandResult;
|
||||
@@ -56,6 +60,10 @@ public:
|
||||
VcsCommand *createCommand(const Utils::FilePath &workingDirectory,
|
||||
VcsBaseEditorWidget *editor = nullptr) const;
|
||||
|
||||
void setupCommand(Utils::QtcProcess &process,
|
||||
const Utils::FilePath &workingDirectory,
|
||||
const QStringList &args) const;
|
||||
|
||||
void enqueueJob(VcsCommand *cmd, const QStringList &args,
|
||||
const Utils::ExitCodeInterpreter &interpreter = {}) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user