diff options
Diffstat (limited to 'src/plugins/git')
-rw-r--r-- | src/plugins/git/branchmodel.cpp | 104 | ||||
-rw-r--r-- | src/plugins/git/branchmodel.h | 3 | ||||
-rw-r--r-- | src/plugins/git/branchview.cpp | 70 | ||||
-rw-r--r-- | src/plugins/git/branchview.h | 3 | ||||
-rw-r--r-- | src/plugins/git/gitclient.cpp | 46 | ||||
-rw-r--r-- | src/plugins/git/gitclient.h | 6 |
6 files changed, 154 insertions, 78 deletions
diff --git a/src/plugins/git/branchmodel.cpp b/src/plugins/git/branchmodel.cpp index 3c4971fa73..7b57df0739 100644 --- a/src/plugins/git/branchmodel.cpp +++ b/src/plugins/git/branchmodel.cpp @@ -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)) { - endResetModel(); - return false; - } + 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); + }; - d->workingDirectory = workingDirectory; - const QStringList lines = output.split('\n'); - for (const QString &l : lines) - d->parseOutputLine(l); - d->flushOldEntries(); + 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); + } + }; - 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); - } + 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); + }; - endResetModel(); + const auto finalize = [this] { + endResetModel(); + d->refreshTask.release()->deleteLater(); + }; - 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); } } diff --git a/src/plugins/git/branchmodel.h b/src/plugins/git/branchmodel.h index 580af61298..de126e9a5e 100644 --- a/src/plugins/git/branchmodel.h +++ b/src/plugins/git/branchmodel.h @@ -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); diff --git a/src/plugins/git/branchview.cpp b/src/plugins/git/branchview.cpp index 528ec288f9..df9c255c58 100644 --- a/src/plugins/git/branchview.cpp +++ b/src/plugins/git/branchview.cpp @@ -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) diff --git a/src/plugins/git/branchview.h b/src/plugins/git/branchview.h index 9a1a3c1e45..c1ac77c82b 100644 --- a/src/plugins/git/branchview.h +++ b/src/plugins/git/branchview.h @@ -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(); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index e419449fe0..66f03aa974 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -171,7 +171,7 @@ GitDiffEditorController::GitDiffEditorController(IDocument *document, VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine()); }; const auto onDiffDone = [diffInputStorage](const QtcProcess &process) { - *diffInputStorage.activeStorage() = process.cleanedStdOut(); + *diffInputStorage = process.cleanedStdOut(); }; const Group root { @@ -258,7 +258,7 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin }; const auto onStagingDone = [storage, diffInputStorage] { - *diffInputStorage.activeStorage() = storage->m_stagedOutput + storage->m_unstagedOutput; + *diffInputStorage = storage->m_stagedOutput + storage->m_unstagedOutput; }; const Group root { @@ -455,7 +455,7 @@ ShowController::ShowController(IDocument *document, const QString &id) VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine()); }; const auto onDiffDone = [diffInputStorage](const QtcProcess &process) { - *diffInputStorage.activeStorage() = process.cleanedStdOut(); + *diffInputStorage = process.cleanedStdOut(); }; const Group root { @@ -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 diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 2dfd6ddaaa..5709a53b86 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -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); |