1 | #include "SquashDlg.h" |
2 | #include "ui_SquashDlg.h" |
3 | |
4 | #include <CheckBox.h> |
5 | #include <GitBase.h> |
6 | #include <GitBranches.h> |
7 | #include <GitCache.h> |
8 | #include <GitLocal.h> |
9 | #include <GitMerge.h> |
10 | #include <GitQlientSettings.h> |
11 | #include <GitWip.h> |
12 | |
13 | #include <QLabel> |
14 | #include <QMessageBox> |
15 | #include <QUuid> |
16 | |
17 | SquashDlg::SquashDlg(const QSharedPointer<GitBase> git, const QSharedPointer<GitCache> &cache, const QStringList &shas, |
18 | QWidget *parent) |
19 | : QDialog(parent) |
20 | , mGit(git) |
21 | , mCache(cache) |
22 | , mShas(shas) |
23 | , ui(new Ui::SquashDlg) |
24 | { |
25 | ui->setupUi(this); |
26 | |
27 | setAttribute(Qt::WA_DeleteOnClose); |
28 | |
29 | mTitleMaxLength = GitQlientSettings().globalValue("commitTitleMaxLength" , mTitleMaxLength).toInt(); |
30 | |
31 | ui->lCounter->setText(QString::number(mTitleMaxLength)); |
32 | ui->leCommitTitle->setMaxLength(mTitleMaxLength); |
33 | |
34 | auto description = QString("This is a combination of %1 commits:\n\n" ).arg(shas.count()); |
35 | |
36 | const auto commitsLayout = new QGridLayout(); |
37 | commitsLayout->setContentsMargins(10, 10, 10, 10); |
38 | commitsLayout->setSpacing(10); |
39 | commitsLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); |
40 | |
41 | auto row = 0; |
42 | for (const auto &sha : shas) |
43 | { |
44 | const auto shortSha = sha.left(8); |
45 | const auto commitTitle = mCache->commitInfo(sha).shortLog; |
46 | description.append(QString("Commit %1: %2 - %3\n\n" ).arg(row + 1).arg(shortSha, commitTitle)); |
47 | |
48 | commitsLayout->addWidget(new QLabel(QString("<strong>(%1)</strong>" ).arg(shortSha)), row, 0); |
49 | commitsLayout->addWidget(new QLabel(commitTitle), row, 1); |
50 | ++row; |
51 | } |
52 | |
53 | commitsLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding), row, 0); |
54 | |
55 | ui->commitsFrame->setLayout(commitsLayout); |
56 | ui->scrollArea->setWidgetResizable(true); |
57 | ui->teDescription->setText(description); |
58 | |
59 | connect(ui->leCommitTitle, &QLineEdit::textChanged, this, &SquashDlg::updateCounter); |
60 | connect(ui->leCommitTitle, &QLineEdit::returnPressed, this, &SquashDlg::accept); |
61 | } |
62 | |
63 | SquashDlg::~SquashDlg() |
64 | { |
65 | delete ui; |
66 | } |
67 | |
68 | void SquashDlg::accept() |
69 | { |
70 | QString msg; |
71 | |
72 | if (checkMsg(msg)) |
73 | { |
74 | const auto revInfo = mCache->commitInfo(CommitInfo::ZERO_SHA); |
75 | |
76 | QScopedPointer<GitWip> git(new GitWip(mGit, mCache)); |
77 | git->updateWip(); |
78 | |
79 | const auto lastChild = mCache->commitInfo(mShas.last()); |
80 | |
81 | QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); |
82 | |
83 | if (lastChild.getChildsCount() == 1) |
84 | { |
85 | if (lastChild.isInWorkingBranch()) |
86 | { |
87 | // Reset soft to the first commit to squash |
88 | QScopedPointer<GitLocal> gitLocal(new GitLocal(mGit)); |
89 | gitLocal->resetCommit(mShas.constFirst(), GitLocal::CommitResetType::SOFT); |
90 | gitLocal->ammend(msg); |
91 | } |
92 | else |
93 | { |
94 | QScopedPointer<GitBranches> gitBranches(new GitBranches(mGit)); |
95 | |
96 | // Create auxiliar branch for rebase |
97 | const auto auxBranch1 = QUuid::createUuid().toString(); |
98 | const auto commitOfAuxBranch1 = lastChild.getFirstChildSha(); |
99 | gitBranches->createBranchAtCommit(commitOfAuxBranch1, auxBranch1); |
100 | |
101 | // Create auxiliar branch for merge squash |
102 | const auto auxBranch2 = QUuid::createUuid().toString(); |
103 | gitBranches->createBranchAtCommit(mShas.last(), auxBranch2); |
104 | |
105 | // Create auxiliar branch for final rebase |
106 | const auto auxBranch3 = QUuid::createUuid().toString(); |
107 | const auto lastCommit = mCache->commitInfo(CommitInfo::ZERO_SHA).firstParent(); |
108 | gitBranches->createBranchAtCommit(lastCommit, auxBranch3); |
109 | |
110 | // Reset hard to the first commit to squash |
111 | QScopedPointer<GitLocal> gitLocal(new GitLocal(mGit)); |
112 | gitLocal->resetCommit(mShas.constFirst(), GitLocal::CommitResetType::HARD); |
113 | |
114 | // Merge squash auxiliar branch 2 |
115 | QScopedPointer<GitMerge> gitMerge(new GitMerge(mGit, mCache)); |
116 | const auto ret = gitMerge->squashMerge(mGit->getCurrentBranch(), { auxBranch2 }, msg); |
117 | |
118 | gitBranches->removeLocalBranch(auxBranch2); |
119 | |
120 | // Rebase auxiliar branch 1 |
121 | const auto destBranch = mGit->getCurrentBranch(); |
122 | gitLocal->cherryPickCommit(commitOfAuxBranch1); |
123 | gitBranches->rebaseOnto(destBranch, auxBranch1, auxBranch3); |
124 | gitBranches->removeLocalBranch(auxBranch1); |
125 | gitBranches->checkoutLocalBranch(destBranch); |
126 | gitMerge->merge(destBranch, { auxBranch3 }); |
127 | gitBranches->removeLocalBranch(auxBranch3); |
128 | } |
129 | } |
130 | |
131 | QApplication::restoreOverrideCursor(); |
132 | |
133 | emit changesCommitted(); |
134 | |
135 | ui->leCommitTitle->clear(); |
136 | ui->teDescription->clear(); |
137 | |
138 | QDialog::accept(); |
139 | } |
140 | } |
141 | |
142 | void SquashDlg::updateCounter(const QString &text) |
143 | { |
144 | ui->lCounter->setText(QString::number(mTitleMaxLength - text.count())); |
145 | } |
146 | |
147 | bool SquashDlg::checkMsg(QString &msg) |
148 | { |
149 | const auto title = ui->leCommitTitle->text(); |
150 | |
151 | if (title.isEmpty()) |
152 | { |
153 | QMessageBox::warning(this, "Commit changes" , "Please, add a title." ); |
154 | return false; |
155 | } |
156 | |
157 | msg = title; |
158 | |
159 | if (!ui->teDescription->toPlainText().isEmpty()) |
160 | { |
161 | auto description = QString("\n\n%1" ).arg(ui->teDescription->toPlainText()); |
162 | description.remove(QRegExp("(^|\\n)\\s*#[^\\n]*" )); // strip comments |
163 | msg += description; |
164 | } |
165 | |
166 | msg.replace(QRegExp("[ \\t\\r\\f\\v]+\\n" ), "\n" ); // strip line trailing cruft |
167 | msg = msg.trimmed(); |
168 | |
169 | if (msg.isEmpty()) |
170 | { |
171 | QMessageBox::warning(this, "Commit changes" , "Please, add a title." ); |
172 | return false; |
173 | } |
174 | |
175 | QString subj(msg.section('\n', 0, 0, QString::SectionIncludeTrailingSep)); |
176 | QString body(msg.section('\n', 1).trimmed()); |
177 | msg = subj + '\n' + body + '\n'; |
178 | |
179 | return true; |
180 | } |
181 | |