1#include "BranchContextMenu.h"
2
3#include <BranchDlg.h>
4#include <GitBase.h>
5#include <GitBranches.h>
6#include <GitCache.h>
7#include <GitConfig.h>
8#include <GitQlientStyles.h>
9#include <GitRemote.h>
10
11#include <QApplication>
12#include <QClipboard>
13#include <QMessageBox>
14
15#include <utility>
16
17BranchContextMenu::BranchContextMenu(BranchContextMenuConfig config, QWidget *parent)
18 : QMenu(parent)
19 , mConfig(std::move(config))
20{
21 setAttribute(Qt::WA_DeleteOnClose);
22
23 connect(addAction(tr("Copy name")), &QAction::triggered, this,
24 [this] { QApplication::clipboard()->setText(mConfig.branchSelected); });
25
26 addSeparator();
27
28 if (mConfig.isLocal)
29 {
30 connect(addAction(tr("Pull")), &QAction::triggered, this, &BranchContextMenu::pull);
31 connect(addAction(tr("Fetch")), &QAction::triggered, this, &BranchContextMenu::fetch);
32 connect(addAction(tr("Push")), &QAction::triggered, this, &BranchContextMenu::push);
33 }
34
35 if (mConfig.currentBranch == mConfig.branchSelected)
36 connect(addAction(tr("Push force")), &QAction::triggered, this, &BranchContextMenu::pushForce);
37
38 addSeparator();
39
40 connect(addAction(tr("Create branch")), &QAction::triggered, this, &BranchContextMenu::createBranch);
41 connect(addAction(tr("Create && checkout branch")), &QAction::triggered, this,
42 &BranchContextMenu::createCheckoutBranch);
43 connect(addAction(tr("Checkout branch")), &QAction::triggered, this, &BranchContextMenu::signalCheckoutBranch);
44
45 if (mConfig.currentBranch != mConfig.branchSelected)
46 {
47 const auto actionName = tr("Merge %1 into %2").arg(mConfig.branchSelected, mConfig.currentBranch);
48 connect(addAction(actionName), &QAction::triggered, this, &BranchContextMenu::merge);
49
50 const auto mergeSquashAction = tr("Squash-merge %1 into %2").arg(mConfig.branchSelected, mConfig.currentBranch);
51 connect(addAction(mergeSquashAction), &QAction::triggered, this, &BranchContextMenu::mergeSquash);
52 }
53
54 addSeparator();
55
56 connect(addAction(tr("Rename")), &QAction::triggered, this, &BranchContextMenu::rename);
57 connect(addAction(tr("Delete")), &QAction::triggered, this, &BranchContextMenu::deleteBranch);
58}
59
60void BranchContextMenu::pull()
61{
62 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
63 QScopedPointer<GitRemote> git(new GitRemote(mConfig.mGit));
64 const auto ret = git->pull();
65 QApplication::restoreOverrideCursor();
66
67 if (ret.success)
68 emit fullReload();
69 else
70 {
71 const auto errorMsg = ret.output;
72
73 if (errorMsg.contains("error: could not apply", Qt::CaseInsensitive)
74 && errorMsg.contains("causing a conflict", Qt::CaseInsensitive))
75 {
76 emit signalPullConflict();
77 }
78 else
79 {
80 QMessageBox msgBox(QMessageBox::Critical, tr("Error while pulling"),
81 tr("There were problems during the pull operation. Please, see the detailed "
82 "description for more information."),
83 QMessageBox::Ok, this);
84 msgBox.setDetailedText(errorMsg);
85 msgBox.setStyleSheet(GitQlientStyles::getStyles());
86 msgBox.exec();
87 }
88 }
89}
90
91void BranchContextMenu::fetch()
92{
93 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
94 QScopedPointer<GitRemote> git(new GitRemote(mConfig.mGit));
95 const auto ret = git->fetch();
96 QApplication::restoreOverrideCursor();
97
98 if (ret)
99 {
100 emit signalFetchPerformed();
101 emit fullReload();
102 }
103 else
104 QMessageBox::critical(this, tr("Fetch failed"), tr("There were some problems while fetching. Please try again."));
105}
106
107void BranchContextMenu::push()
108{
109 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
110 QScopedPointer<GitRemote> git(new GitRemote(mConfig.mGit));
111 const auto ret
112 = mConfig.currentBranch == mConfig.branchSelected ? git->push() : git->pushBranch(mConfig.branchSelected);
113 QApplication::restoreOverrideCursor();
114
115 if (ret.output.contains("has no upstream branch"))
116 {
117 BranchDlg dlg({ mConfig.branchSelected, BranchDlgMode::PUSH_UPSTREAM, mConfig.mCache, mConfig.mGit });
118 dlg.exec();
119 }
120 else if (ret.success)
121 {
122 QScopedPointer<GitConfig> git(new GitConfig(mConfig.mGit));
123 const auto remote = git->getRemoteForBranch(mConfig.branchSelected);
124
125 if (remote.success)
126 {
127 const auto oldSha = mConfig.mCache->getShaOfReference(
128 QString("%1/%2").arg(remote.output, mConfig.branchSelected), References::Type::RemoteBranches);
129 const auto sha = mConfig.mCache->getShaOfReference(mConfig.branchSelected, References::Type::LocalBranch);
130 mConfig.mCache->deleteReference(oldSha, References::Type::RemoteBranches,
131 QString("%1/%2").arg(remote.output, mConfig.branchSelected));
132 mConfig.mCache->insertReference(sha, References::Type::RemoteBranches,
133 QString("%1/%2").arg(remote.output, mConfig.branchSelected));
134 emit mConfig.mCache->signalCacheUpdated();
135 emit logReload();
136 }
137 }
138 else
139 {
140 QMessageBox msgBox(QMessageBox::Critical, tr("Error while pushing"),
141 tr("There were problems during the push operation. Please, see the detailed description "
142 "for more information."),
143 QMessageBox::Ok, this);
144 msgBox.setDetailedText(ret.output);
145 msgBox.setStyleSheet(GitQlientStyles::getStyles());
146 msgBox.exec();
147 }
148}
149
150void BranchContextMenu::pushForce()
151{
152 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
153 QScopedPointer<GitRemote> git(new GitRemote(mConfig.mGit));
154 const auto ret = git->push(true);
155 QApplication::restoreOverrideCursor();
156
157 if (ret.success)
158 {
159 emit signalRefreshPRsCache();
160 emit fullReload();
161 }
162 else
163 {
164 QMessageBox msgBox(QMessageBox::Critical, tr("Error while pulling"),
165 tr("There were problems during the pull operation. Please, see the detailed description "
166 "for more information."),
167 QMessageBox::Ok, this);
168 msgBox.setDetailedText(ret.output);
169 msgBox.setStyleSheet(GitQlientStyles::getStyles());
170 msgBox.exec();
171 }
172}
173
174void BranchContextMenu::createBranch()
175{
176 BranchDlg dlg({ mConfig.branchSelected, BranchDlgMode::CREATE, mConfig.mCache, mConfig.mGit });
177 dlg.exec();
178}
179
180void BranchContextMenu::createCheckoutBranch()
181{
182 BranchDlg dlg({ mConfig.branchSelected, BranchDlgMode::CREATE_CHECKOUT, mConfig.mCache, mConfig.mGit });
183 dlg.exec();
184}
185
186void BranchContextMenu::merge()
187{
188 emit signalMergeRequired(mConfig.currentBranch, mConfig.branchSelected);
189}
190
191void BranchContextMenu::mergeSquash()
192{
193 emit mergeSqushRequested(mConfig.currentBranch, mConfig.branchSelected);
194}
195
196void BranchContextMenu::rename()
197{
198 BranchDlg dlg({ mConfig.branchSelected, BranchDlgMode::RENAME, mConfig.mCache, mConfig.mGit });
199 dlg.exec();
200}
201
202void BranchContextMenu::deleteBranch()
203{
204 if (!mConfig.isLocal && mConfig.branchSelected == "master")
205 QMessageBox::critical(this, tr("Delete master?!"), tr("You are not allowed to delete remote master."),
206 QMessageBox::Ok);
207 else
208 {
209 auto ret = QMessageBox::warning(this, tr("Delete branch!"), tr("Are you sure you want to delete the branch?"),
210 QMessageBox::Ok, QMessageBox::Cancel);
211
212 if (ret == QMessageBox::Ok)
213 {
214 const auto type = mConfig.isLocal ? References::Type::LocalBranch : References::Type::RemoteBranches;
215 const auto sha = mConfig.mCache->getShaOfReference(mConfig.branchSelected, type);
216 QScopedPointer<GitBranches> git(new GitBranches(mConfig.mGit));
217 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
218 const auto ret2 = mConfig.isLocal ? git->removeLocalBranch(mConfig.branchSelected)
219 : git->removeRemoteBranch(mConfig.branchSelected);
220 QApplication::restoreOverrideCursor();
221
222 if (ret2.success)
223 {
224 mConfig.mCache->deleteReference(sha, type, mConfig.branchSelected);
225 emit mConfig.mCache->signalCacheUpdated();
226 emit logReload();
227 }
228 else
229 QMessageBox::critical(
230 this, tr("Delete a branch failed"),
231 tr("There were some problems while deleting the branch:<br><br> %1").arg(ret2.output));
232 }
233 }
234}
235