1 | #include "CommitHistoryContextMenu.h" |
2 | |
3 | #include <BranchDlg.h> |
4 | #include <CommitInfo.h> |
5 | //#include <CreateIssueDlg.h> |
6 | //#include <CreatePullRequestDlg.h> |
7 | #include <GitBase.h> |
8 | #include <GitBranches.h> |
9 | #include <GitCache.h> |
10 | #include <GitConfig.h> |
11 | #include <GitHistory.h> |
12 | #include <GitHubRestApi.h> |
13 | #include <GitLocal.h> |
14 | #include <GitPatches.h> |
15 | #include <GitQlientStyles.h> |
16 | #include <GitRemote.h> |
17 | #include <GitServerCache.h> |
18 | #include <GitStashes.h> |
19 | #include <GitTags.h> |
20 | #include <MergePullRequestDlg.h> |
21 | #include <PullDlg.h> |
22 | #include <SquashDlg.h> |
23 | #include <TagDlg.h> |
24 | |
25 | #include <QApplication> |
26 | #include <QClipboard> |
27 | #include <QDesktopServices> |
28 | #include <QFileDialog> |
29 | #include <QMessageBox> |
30 | #include <QProcess> |
31 | |
32 | #include <QLogger.h> |
33 | |
34 | using namespace QLogger; |
35 | |
36 | CommitHistoryContextMenu::(const QSharedPointer<GitCache> &cache, |
37 | const QSharedPointer<GitBase> &git, |
38 | const QSharedPointer<GitServerCache> &gitServerCache, |
39 | const QStringList &shas, QWidget *parent) |
40 | : QMenu(parent) |
41 | , mCache(cache) |
42 | , mGit(git) |
43 | , mGitServerCache(gitServerCache) |
44 | , mGitTags(new GitTags(mGit, mCache)) |
45 | , mShas(shas) |
46 | { |
47 | setAttribute(Qt::WA_DeleteOnClose); |
48 | |
49 | if (shas.count() == 1) |
50 | createIndividualShaMenu(); |
51 | else |
52 | createMultipleShasMenu(); |
53 | } |
54 | |
55 | void CommitHistoryContextMenu::() |
56 | { |
57 | const auto singleSelection = mShas.count() == 1; |
58 | |
59 | if (singleSelection) |
60 | { |
61 | const auto sha = mShas.first(); |
62 | |
63 | if (sha == CommitInfo::ZERO_SHA) |
64 | { |
65 | const auto = addMenu(tr("Stash" )); |
66 | const auto stashAction = stashMenu->addAction(tr("Push" )); |
67 | connect(stashAction, &QAction::triggered, this, &CommitHistoryContextMenu::stashPush); |
68 | |
69 | const auto popAction = stashMenu->addAction(tr("Pop" )); |
70 | connect(popAction, &QAction::triggered, this, &CommitHistoryContextMenu::stashPop); |
71 | } |
72 | |
73 | const auto commitAction = addAction(tr("See diff" )); |
74 | connect(commitAction, &QAction::triggered, this, [this]() { emit signalOpenDiff(mShas.first()); }); |
75 | |
76 | if (sha != CommitInfo::ZERO_SHA) |
77 | { |
78 | const auto = addMenu(tr("Create" )); |
79 | |
80 | const auto createBranchAction = createMenu->addAction(tr("Branch" )); |
81 | connect(createBranchAction, &QAction::triggered, this, &CommitHistoryContextMenu::createBranch); |
82 | |
83 | const auto createTagAction = createMenu->addAction(tr("Tag" )); |
84 | connect(createTagAction, &QAction::triggered, this, &CommitHistoryContextMenu::createTag); |
85 | |
86 | const auto exportAsPatchAction = addAction(tr("Export as patch" )); |
87 | connect(exportAsPatchAction, &QAction::triggered, this, &CommitHistoryContextMenu::exportAsPatch); |
88 | |
89 | addSeparator(); |
90 | |
91 | const auto checkoutCommitAction = addAction(tr("Checkout commit" )); |
92 | connect(checkoutCommitAction, &QAction::triggered, this, &CommitHistoryContextMenu::checkoutCommit); |
93 | |
94 | addBranchActions(sha); |
95 | |
96 | QScopedPointer<GitBranches> git(new GitBranches(mGit)); |
97 | |
98 | if (auto ret = git->getLastCommitOfBranch(mGit->getCurrentBranch()); ret.success) |
99 | { |
100 | const auto lastShaStr = ret.output.trimmed(); |
101 | |
102 | if (lastShaStr == sha) |
103 | { |
104 | const auto amendCommitAction = addAction(tr("Amend" )); |
105 | connect(amendCommitAction, &QAction::triggered, this, |
106 | [this]() { emit signalAmendCommit(mShas.first()); }); |
107 | |
108 | const auto = addMenu(tr("Apply" )); |
109 | |
110 | const auto applyPatchAction = applyMenu->addAction(tr("Patch" )); |
111 | connect(applyPatchAction, &QAction::triggered, this, &CommitHistoryContextMenu::applyPatch); |
112 | |
113 | const auto applyCommitAction = applyMenu->addAction(tr("Commit" )); |
114 | connect(applyCommitAction, &QAction::triggered, this, &CommitHistoryContextMenu::applyCommit); |
115 | |
116 | const auto pushAction = addAction(tr("Push" )); |
117 | connect(pushAction, &QAction::triggered, this, &CommitHistoryContextMenu::push); |
118 | |
119 | const auto pullAction = addAction(tr("Pull" )); |
120 | connect(pullAction, &QAction::triggered, this, &CommitHistoryContextMenu::pull); |
121 | |
122 | const auto fetchAction = addAction(tr("Fetch" )); |
123 | connect(fetchAction, &QAction::triggered, this, &CommitHistoryContextMenu::fetch); |
124 | } |
125 | else if (mCache->isCommitInCurrentGeneologyTree(mShas.first())) |
126 | { |
127 | const auto pushAction = addAction(tr("Push" )); |
128 | connect(pushAction, &QAction::triggered, this, &CommitHistoryContextMenu::push); |
129 | } |
130 | } |
131 | |
132 | const auto = addMenu(tr("Reset" )); |
133 | |
134 | const auto resetSoftAction = resetMenu->addAction(tr("Soft" )); |
135 | connect(resetSoftAction, &QAction::triggered, this, &CommitHistoryContextMenu::resetSoft); |
136 | |
137 | const auto resetMixedAction = resetMenu->addAction(tr("Mixed" )); |
138 | connect(resetMixedAction, &QAction::triggered, this, &CommitHistoryContextMenu::resetMixed); |
139 | |
140 | const auto resetHardAction = resetMenu->addAction(tr("Hard" )); |
141 | connect(resetHardAction, &QAction::triggered, this, &CommitHistoryContextMenu::resetHard); |
142 | |
143 | addSeparator(); |
144 | |
145 | const auto = addMenu(tr("Copy" )); |
146 | |
147 | const auto copyShaAction = copyMenu->addAction(tr("Commit SHA" )); |
148 | connect(copyShaAction, &QAction::triggered, this, |
149 | [this]() { QApplication::clipboard()->setText(mShas.first()); }); |
150 | |
151 | const auto copyTitleAction = copyMenu->addAction(tr("Commit title" )); |
152 | connect(copyTitleAction, &QAction::triggered, this, [this]() { |
153 | const auto title = mCache->commitInfo(mShas.first()).shortLog; |
154 | QApplication::clipboard()->setText(title); |
155 | }); |
156 | } |
157 | } |
158 | |
159 | if (mGitServerCache) |
160 | { |
161 | const auto isGitHub = mGitServerCache->getPlatform() == GitServer::Platform::GitHub; |
162 | const auto = new QMenu(QString::fromUtf8(isGitHub ? "GitHub" : "GitLab" ), this); |
163 | |
164 | addSeparator(); |
165 | addMenu(gitServerMenu); |
166 | |
167 | if (const auto pr = mGitServerCache->getPullRequest(mShas.first()); singleSelection && pr.isValid()) |
168 | { |
169 | const auto prInfo = mGitServerCache->getPullRequest(mShas.first()); |
170 | |
171 | const auto = new QMenu("Checks" , gitServerMenu); |
172 | gitServerMenu->addMenu(checksMenu); |
173 | |
174 | for (const auto &check : prInfo.state.checks) |
175 | { |
176 | const auto link = check.url; |
177 | checksMenu->addAction(QIcon(QString(":/icons/%1" ).arg(check.state)), check.name, this, |
178 | [link]() { QDesktopServices::openUrl(link); }); |
179 | } |
180 | |
181 | if (isGitHub) |
182 | { |
183 | connect(gitServerMenu->addAction(tr("Merge PR" )), &QAction::triggered, this, [this, pr]() { |
184 | const auto mergeDlg = new MergePullRequestDlg(mGit, pr, mShas.first(), this); |
185 | connect(mergeDlg, &MergePullRequestDlg::signalRepositoryUpdated, this, |
186 | &CommitHistoryContextMenu::fullReload); |
187 | |
188 | mergeDlg->exec(); |
189 | }); |
190 | } |
191 | |
192 | connect(gitServerMenu->addAction(tr("Show PR detailed view" )), &QAction::triggered, this, |
193 | [this, num = pr.number]() { emit showPrDetailedView(num); }); |
194 | |
195 | gitServerMenu->addSeparator(); |
196 | } |
197 | } |
198 | } |
199 | |
200 | void CommitHistoryContextMenu::() |
201 | { |
202 | if (mShas.count() == 2) |
203 | { |
204 | const auto diffAction = addAction(tr("See diff" )); |
205 | connect(diffAction, &QAction::triggered, this, [this]() { emit signalOpenCompareDiff(mShas); }); |
206 | } |
207 | |
208 | if (!mShas.contains(CommitInfo::ZERO_SHA)) |
209 | { |
210 | const auto exportAsPatchAction = addAction(tr("Export as patch" )); |
211 | connect(exportAsPatchAction, &QAction::triggered, this, &CommitHistoryContextMenu::exportAsPatch); |
212 | |
213 | const auto copyShaAction = addAction(tr("Copy all SHA" )); |
214 | connect(copyShaAction, &QAction::triggered, this, |
215 | [this]() { QApplication::clipboard()->setText(mShas.join(',')); }); |
216 | |
217 | auto shasInCurrenTree = 0; |
218 | |
219 | for (const auto &sha : qAsConst(mShas)) |
220 | shasInCurrenTree += mCache->isCommitInCurrentGeneologyTree(sha); |
221 | |
222 | if (shasInCurrenTree == 0) |
223 | { |
224 | const auto cherryPickAction = addAction(tr("Cherry pick ALL commits" )); |
225 | connect(cherryPickAction, &QAction::triggered, this, &CommitHistoryContextMenu::cherryPickCommit); |
226 | } |
227 | else if (shasInCurrenTree == mShas.count()) |
228 | { |
229 | const auto cherryPickAction = addAction(tr("Squash commits" )); |
230 | connect(cherryPickAction, &QAction::triggered, this, &CommitHistoryContextMenu::showSquashDialog); |
231 | } |
232 | } |
233 | else |
234 | QLog_Warning("UI" , "WIP selected as part of a series of SHAs" ); |
235 | } |
236 | |
237 | void CommitHistoryContextMenu::() |
238 | { |
239 | QScopedPointer<GitStashes> git(new GitStashes(mGit)); |
240 | const auto ret = git->stash(); |
241 | |
242 | if (ret.success) |
243 | emit logReload(); |
244 | } |
245 | |
246 | void CommitHistoryContextMenu::() |
247 | { |
248 | QScopedPointer<GitStashes> git(new GitStashes(mGit)); |
249 | const auto ret = git->pop(); |
250 | |
251 | if (ret.success) |
252 | emit logReload(); |
253 | } |
254 | |
255 | void CommitHistoryContextMenu::() |
256 | { |
257 | BranchDlg dlg({ mShas.first(), BranchDlgMode::CREATE_FROM_COMMIT, mCache, mGit }); |
258 | dlg.exec(); |
259 | } |
260 | |
261 | void CommitHistoryContextMenu::() |
262 | { |
263 | TagDlg dlg(QSharedPointer<GitBase>::create(mGit->getWorkingDir()), mShas.first()); |
264 | const auto ret = dlg.exec(); |
265 | |
266 | if (ret == QDialog::Accepted) |
267 | emit referencesReload(); // TODO: Optimize |
268 | } |
269 | |
270 | void CommitHistoryContextMenu::() |
271 | { |
272 | QScopedPointer<GitPatches> git(new GitPatches(mGit)); |
273 | const auto ret = git->exportPatch(mShas); |
274 | |
275 | if (ret.success) |
276 | { |
277 | const auto action = QMessageBox::information(this, tr("Patch generated" ), |
278 | tr("<p>The patch has been generated!</p>" |
279 | "<p><b>Commit:</b></p><p>%1</p>" |
280 | "<p><b>Destination:</b> %2</p>" |
281 | "<p><b>File names:</b></p><p>%3</p>" ) |
282 | .arg(mShas.join("<br>" ), mGit->getWorkingDir(), ret.output), |
283 | QMessageBox::Ok, QMessageBox::Open); |
284 | |
285 | if (action == QMessageBox::Open) |
286 | { |
287 | QString fileBrowser; |
288 | |
289 | #ifdef Q_OS_LINUX |
290 | fileBrowser.append("xdg-open" ); |
291 | #elif defined(Q_OS_WIN) |
292 | fileBrowser.append("explorer.exe" ); |
293 | #endif |
294 | |
295 | QProcess::startDetached(fileBrowser, { mGit->getWorkingDir() }); |
296 | } |
297 | } |
298 | } |
299 | |
300 | void CommitHistoryContextMenu::() |
301 | { |
302 | const auto action = qobject_cast<QAction *>(sender()); |
303 | auto isLocal = action->data().toBool(); |
304 | auto branchName = action->text(); |
305 | |
306 | if (isLocal) |
307 | branchName.remove("origin/" ); |
308 | |
309 | QScopedPointer<GitBranches> git(new GitBranches(mGit)); |
310 | const auto ret = isLocal ? git->checkoutLocalBranch(branchName) : git->checkoutRemoteBranch(branchName); |
311 | const auto output = ret.output; |
312 | |
313 | if (ret.success) |
314 | { |
315 | QRegExp rx("by \\d+ commits" ); |
316 | rx.indexIn(ret.output); |
317 | auto value = rx.capturedTexts().constFirst().split(" " ); |
318 | |
319 | if (value.count() == 3 && output.contains("your branch is behind" , Qt::CaseInsensitive)) |
320 | { |
321 | const auto commits = value.at(1).toUInt(); |
322 | (void)commits; |
323 | |
324 | PullDlg pull(mGit, output.split('\n').first()); |
325 | connect(&pull, &PullDlg::signalRepositoryUpdated, this, &CommitHistoryContextMenu::fullReload); |
326 | connect(&pull, &PullDlg::signalPullConflict, this, &CommitHistoryContextMenu::signalPullConflict); |
327 | } |
328 | |
329 | emit logReload(); |
330 | } |
331 | else |
332 | { |
333 | QMessageBox msgBox(QMessageBox::Critical, tr("Error while checking out" ), |
334 | tr("There were problems during the checkout operation. Please, see the detailed " |
335 | "description for more information." ), |
336 | QMessageBox::Ok, this); |
337 | msgBox.setDetailedText(ret.output); |
338 | msgBox.setStyleSheet(GitQlientStyles::getStyles()); |
339 | msgBox.exec(); |
340 | } |
341 | } |
342 | |
343 | void CommitHistoryContextMenu::() |
344 | { |
345 | BranchDlg dlg({ mShas.constFirst(), BranchDlgMode::CREATE_CHECKOUT_FROM_COMMIT, mCache, mGit }); |
346 | dlg.exec(); |
347 | } |
348 | |
349 | void CommitHistoryContextMenu::() |
350 | { |
351 | const auto sha = mShas.first(); |
352 | QLog_Info("UI" , QString("Checking out the commit {%1}" ).arg(sha)); |
353 | |
354 | QScopedPointer<GitLocal> git(new GitLocal(mGit)); |
355 | const auto ret = git->checkoutCommit(sha); |
356 | |
357 | if (ret.success) |
358 | emit logReload(); |
359 | else |
360 | { |
361 | QMessageBox msgBox(QMessageBox::Critical, tr("Error while checking out" ), |
362 | tr("There were problems during the checkout operation. Please, see the detailed " |
363 | "description for more information." ), |
364 | QMessageBox::Ok, this); |
365 | msgBox.setDetailedText(ret.output); |
366 | msgBox.setStyleSheet(GitQlientStyles::getStyles()); |
367 | msgBox.exec(); |
368 | } |
369 | } |
370 | |
371 | void CommitHistoryContextMenu::() |
372 | { |
373 | auto shas = mShas; |
374 | for (const auto &sha : qAsConst(mShas)) |
375 | { |
376 | const auto lastShaBeforeCommit = mGit->getLastCommit().output.trimmed(); |
377 | QScopedPointer<GitLocal> git(new GitLocal(mGit)); |
378 | const auto ret = git->cherryPickCommit(sha); |
379 | |
380 | shas.takeFirst(); |
381 | |
382 | if (ret.success && shas.isEmpty()) |
383 | { |
384 | auto commit = mCache->commitInfo(sha); |
385 | commit.sha = mGit->getLastCommit().output.trimmed(); |
386 | |
387 | mCache->insertCommit(commit); |
388 | mCache->deleteReference(lastShaBeforeCommit, References::Type::LocalBranch, mGit->getCurrentBranch()); |
389 | mCache->insertReference(commit.sha, References::Type::LocalBranch, mGit->getCurrentBranch()); |
390 | |
391 | QScopedPointer<GitHistory> gitHistory(new GitHistory(mGit)); |
392 | const auto ret = gitHistory->getDiffFiles(commit.sha, lastShaBeforeCommit); |
393 | |
394 | mCache->insertRevisionFiles(commit.sha, lastShaBeforeCommit, RevisionFiles(ret.output)); |
395 | |
396 | emit mCache->signalCacheUpdated(); |
397 | emit logReload(); |
398 | } |
399 | else if (!ret.success) |
400 | { |
401 | const auto errorMsg = ret.output; |
402 | |
403 | if (errorMsg.contains("error: could not apply" , Qt::CaseInsensitive) |
404 | && errorMsg.contains("after resolving the conflicts" , Qt::CaseInsensitive)) |
405 | { |
406 | emit signalCherryPickConflict(shas); |
407 | } |
408 | else |
409 | { |
410 | QMessageBox msgBox(QMessageBox::Critical, tr("Error while cherry-pick" ), |
411 | tr("There were problems during the cherry-pich operation. Please, see the detailed " |
412 | "description for more information." ), |
413 | QMessageBox::Ok, this); |
414 | msgBox.setDetailedText(errorMsg); |
415 | msgBox.setStyleSheet(GitQlientStyles::getStyles()); |
416 | msgBox.exec(); |
417 | } |
418 | } |
419 | } |
420 | } |
421 | |
422 | void CommitHistoryContextMenu::() |
423 | { |
424 | const QString fileName(QFileDialog::getOpenFileName(this, tr("Select a patch to apply" ))); |
425 | QScopedPointer<GitPatches> git(new GitPatches(mGit)); |
426 | |
427 | if (!fileName.isEmpty() && git->applyPatch(fileName)) |
428 | emit logReload(); |
429 | } |
430 | |
431 | void CommitHistoryContextMenu::() |
432 | { |
433 | const QString fileName(QFileDialog::getOpenFileName(this, "Select a patch to apply" )); |
434 | QScopedPointer<GitPatches> git(new GitPatches(mGit)); |
435 | |
436 | if (!fileName.isEmpty() && git->applyPatch(fileName, true)) |
437 | emit logReload(); |
438 | } |
439 | |
440 | void CommitHistoryContextMenu::() |
441 | { |
442 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); |
443 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
444 | const auto ret = git->pushCommit(mShas.first(), mGit->getCurrentBranch()); |
445 | QApplication::restoreOverrideCursor(); |
446 | |
447 | if (ret.output.contains("has no upstream branch" )) |
448 | { |
449 | const auto currentBranch = mGit->getCurrentBranch(); |
450 | BranchDlg dlg({ currentBranch, BranchDlgMode::PUSH_UPSTREAM, mCache, mGit }); |
451 | const auto ret = dlg.exec(); |
452 | |
453 | if (ret == QDialog::Accepted) |
454 | emit signalRefreshPRsCache(); |
455 | } |
456 | else if (ret.success) |
457 | { |
458 | const auto currentBranch = mGit->getCurrentBranch(); |
459 | QScopedPointer<GitConfig> git(new GitConfig(mGit)); |
460 | const auto remote = git->getRemoteForBranch(currentBranch); |
461 | |
462 | if (remote.success) |
463 | { |
464 | const auto oldSha = mCache->getShaOfReference(QString("%1/%2" ).arg(remote.output, currentBranch), |
465 | References::Type::RemoteBranches); |
466 | const auto sha = mCache->getShaOfReference(currentBranch, References::Type::LocalBranch); |
467 | mCache->deleteReference(oldSha, References::Type::RemoteBranches, |
468 | QString("%1/%2" ).arg(remote.output, currentBranch)); |
469 | mCache->insertReference(sha, References::Type::RemoteBranches, |
470 | QString("%1/%2" ).arg(remote.output, currentBranch)); |
471 | emit mCache->signalCacheUpdated(); |
472 | emit signalRefreshPRsCache(); |
473 | } |
474 | } |
475 | else |
476 | { |
477 | QMessageBox msgBox(QMessageBox::Critical, tr("Error while pushing" ), |
478 | tr("There were problems during the push operation. Please, see the detailed description " |
479 | "for more information." ), |
480 | QMessageBox::Ok, this); |
481 | msgBox.setDetailedText(ret.output); |
482 | msgBox.setStyleSheet(GitQlientStyles::getStyles()); |
483 | msgBox.exec(); |
484 | } |
485 | } |
486 | |
487 | void CommitHistoryContextMenu::() |
488 | { |
489 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); |
490 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
491 | const auto ret = git->pull(); |
492 | QApplication::restoreOverrideCursor(); |
493 | |
494 | if (ret.success) |
495 | emit fullReload(); |
496 | else |
497 | { |
498 | const auto errorMsg = ret.output; |
499 | |
500 | if (errorMsg.contains("error: could not apply" , Qt::CaseInsensitive) |
501 | && errorMsg.contains("causing a conflict" , Qt::CaseInsensitive)) |
502 | { |
503 | emit signalPullConflict(); |
504 | } |
505 | else |
506 | { |
507 | QMessageBox msgBox(QMessageBox::Critical, tr("Error while pulling" ), |
508 | tr("There were problems during the pull operation. Please, see the detailed " |
509 | "description for more information." ), |
510 | QMessageBox::Ok, this); |
511 | msgBox.setDetailedText(errorMsg); |
512 | msgBox.setStyleSheet(GitQlientStyles::getStyles()); |
513 | msgBox.exec(); |
514 | } |
515 | } |
516 | } |
517 | |
518 | void CommitHistoryContextMenu::() |
519 | { |
520 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
521 | |
522 | if (git->fetch()) |
523 | { |
524 | mGitTags->getRemoteTags(); |
525 | emit fullReload(); |
526 | } |
527 | } |
528 | |
529 | void CommitHistoryContextMenu::() |
530 | { |
531 | QScopedPointer<GitLocal> git(new GitLocal(mGit)); |
532 | const auto previousSha = mGit->getLastCommit().output.trimmed(); |
533 | |
534 | if (git->resetCommit(mShas.first(), GitLocal::CommitResetType::SOFT)) |
535 | { |
536 | mCache->deleteReference(previousSha, References::Type::LocalBranch, mGit->getCurrentBranch()); |
537 | mCache->insertReference(mShas.first(), References::Type::LocalBranch, mGit->getCurrentBranch()); |
538 | |
539 | emit logReload(); |
540 | } |
541 | } |
542 | |
543 | void CommitHistoryContextMenu::() |
544 | { |
545 | QScopedPointer<GitLocal> git(new GitLocal(mGit)); |
546 | const auto previousSha = mGit->getLastCommit().output.trimmed(); |
547 | |
548 | if (git->resetCommit(mShas.first(), GitLocal::CommitResetType::MIXED)) |
549 | { |
550 | mCache->deleteReference(previousSha, References::Type::LocalBranch, mGit->getCurrentBranch()); |
551 | mCache->insertReference(mShas.first(), References::Type::LocalBranch, mGit->getCurrentBranch()); |
552 | |
553 | emit logReload(); |
554 | } |
555 | } |
556 | |
557 | void CommitHistoryContextMenu::() |
558 | { |
559 | const auto retMsg = QMessageBox::warning( |
560 | this, "Reset hard requested!" , "Are you sure you want to reset the branch to this commit in a <b>hard</b> way?" , |
561 | QMessageBox::Ok, QMessageBox::Cancel); |
562 | |
563 | if (retMsg == QMessageBox::Ok) |
564 | { |
565 | const auto previousSha = mGit->getLastCommit().output.trimmed(); |
566 | QScopedPointer<GitLocal> git(new GitLocal(mGit)); |
567 | |
568 | if (git->resetCommit(mShas.first(), GitLocal::CommitResetType::HARD)) |
569 | { |
570 | mCache->deleteReference(previousSha, References::Type::LocalBranch, mGit->getCurrentBranch()); |
571 | mCache->insertReference(mShas.first(), References::Type::LocalBranch, mGit->getCurrentBranch()); |
572 | |
573 | emit logReload(); |
574 | } |
575 | } |
576 | } |
577 | |
578 | void CommitHistoryContextMenu::() |
579 | { |
580 | const auto action = qobject_cast<QAction *>(sender()); |
581 | const auto fromBranch = action->data().toString(); |
582 | |
583 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
584 | const auto currentBranch = mGit->getCurrentBranch(); |
585 | |
586 | emit signalMergeRequired(currentBranch, fromBranch); |
587 | } |
588 | |
589 | void CommitHistoryContextMenu::() |
590 | { |
591 | const auto action = qobject_cast<QAction *>(sender()); |
592 | const auto fromBranch = action->data().toString(); |
593 | |
594 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
595 | const auto currentBranch = mGit->getCurrentBranch(); |
596 | |
597 | emit mergeSqushRequested(currentBranch, fromBranch); |
598 | } |
599 | |
600 | void CommitHistoryContextMenu::(const QString &sha) |
601 | { |
602 | auto remoteBranches = mCache->getReferences(sha, References::Type::RemoteBranches); |
603 | const auto localBranches = mCache->getReferences(sha, References::Type::LocalBranch); |
604 | |
605 | QMap<QString, bool> branchTracking; |
606 | |
607 | if (remoteBranches.isEmpty()) |
608 | { |
609 | for (const auto &branch : localBranches) |
610 | branchTracking.insert(branch, true); |
611 | } |
612 | else |
613 | { |
614 | for (const auto &local : localBranches) |
615 | { |
616 | const auto iter = std::find_if(remoteBranches.begin(), remoteBranches.end(), [local](const QString &remote) { |
617 | if (remote.contains(local)) |
618 | return true; |
619 | return false; |
620 | }); |
621 | |
622 | branchTracking.insert(local, true); |
623 | |
624 | if (iter != remoteBranches.end()) |
625 | remoteBranches.erase(iter); |
626 | } |
627 | for (const auto &remote : remoteBranches) |
628 | branchTracking.insert(remote, false); |
629 | } |
630 | |
631 | QList<QAction *> branchesToCheckout; |
632 | const auto currentBranch = mGit->getCurrentBranch(); |
633 | |
634 | for (const auto &pair : branchTracking.toStdMap()) |
635 | { |
636 | if (!branchTracking.isEmpty() && pair.first != currentBranch |
637 | && pair.first != QString("origin/%1" ).arg(currentBranch)) |
638 | { |
639 | const auto checkoutCommitAction = new QAction(QString(tr("%1" )).arg(pair.first)); |
640 | checkoutCommitAction->setData(pair.second); |
641 | connect(checkoutCommitAction, &QAction::triggered, this, &CommitHistoryContextMenu::checkoutBranch); |
642 | branchesToCheckout.append(checkoutCommitAction); |
643 | } |
644 | } |
645 | |
646 | const auto = !branchesToCheckout.isEmpty() ? addMenu(tr("Checkout branch" )) : this; |
647 | const auto newBranchAction |
648 | = branchMenu->addAction(!branchesToCheckout.isEmpty() ? tr("New Branch" ) : tr("Checkout new branch" )); |
649 | connect(newBranchAction, &QAction::triggered, this, &CommitHistoryContextMenu::createCheckoutBranch); |
650 | |
651 | if (!branchesToCheckout.isEmpty()) |
652 | { |
653 | branchMenu->addSeparator(); |
654 | branchMenu->addActions(branchesToCheckout); |
655 | } |
656 | |
657 | if (!mCache->isCommitInCurrentGeneologyTree(sha)) |
658 | { |
659 | for (const auto &pair : branchTracking.toStdMap()) |
660 | { |
661 | if (!pair.first.isEmpty() && pair.first != currentBranch |
662 | && pair.first != QString("origin/%1" ).arg(currentBranch)) |
663 | { |
664 | // If is the last commit of a branch |
665 | const auto mergeBranchAction = addAction(QString(tr("Merge %1" )).arg(pair.first)); |
666 | mergeBranchAction->setData(pair.first); |
667 | connect(mergeBranchAction, &QAction::triggered, this, &CommitHistoryContextMenu::merge); |
668 | |
669 | const auto mergeSquashBranchAction = addAction(QString(tr("Squash-merge %1" )).arg(pair.first)); |
670 | mergeSquashBranchAction->setData(pair.first); |
671 | connect(mergeSquashBranchAction, &QAction::triggered, this, &CommitHistoryContextMenu::mergeSquash); |
672 | } |
673 | } |
674 | |
675 | addSeparator(); |
676 | |
677 | const auto cherryPickAction = addAction(tr("Cherry pick commit" )); |
678 | connect(cherryPickAction, &QAction::triggered, this, &CommitHistoryContextMenu::cherryPickCommit); |
679 | } |
680 | else |
681 | addSeparator(); |
682 | } |
683 | |
684 | void CommitHistoryContextMenu::() |
685 | { |
686 | if (mCache->pendingLocalChanges()) |
687 | { |
688 | QMessageBox::warning(this, tr("Squash not possible" ), |
689 | tr("Please, make sure there are no pending changes to be commited." )); |
690 | } |
691 | else |
692 | { |
693 | const auto squash = new SquashDlg(mGit, mCache, mShas, this); |
694 | connect(squash, &SquashDlg::changesCommitted, this, &CommitHistoryContextMenu::fullReload); |
695 | squash->exec(); |
696 | } |
697 | } |
698 | |