| 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 |  | 
|---|