Commit 4a527646 authored by Waqar Ahmed's avatar Waqar Ahmed
Browse files

Git: Allow amending last commit message

parent f8109d30
......@@ -162,3 +162,33 @@ QVector<GitUtils::Branch> GitUtils::getAllBranches(const QString &repo)
{
return getAllBranchesAndTags(repo, static_cast<RefType>(RefType::Head | RefType::Remote));
}
std::pair<QString, QString> GitUtils::getLastCommitMessage(const QString &repo)
{
// git log -1 --pretty=%B
QProcess git;
git.setWorkingDirectory(repo);
const QStringList args{QStringLiteral("log"), QStringLiteral("-1"), QStringLiteral("--pretty=%B")};
git.start(QStringLiteral("git"), args, QProcess::ReadOnly);
if (git.waitForStarted() && git.waitForFinished(-1)) {
if (git.exitCode() != 0 || git.exitStatus() != QProcess::NormalExit) {
return {};
}
QList<QByteArray> output = git.readAllStandardOutput().split('\n');
if (output.isEmpty()) {
return {};
}
QString msg = QString::fromUtf8(output.at(0));
QString desc;
if (output.size() > 1) {
desc = std::accumulate(output.cbegin() + 1, output.cend(), QString::fromUtf8(output.at(1)), [](const QString &line, const QByteArray &ba) {
return QString(line + QString::fromUtf8(ba) + QStringLiteral("\n"));
});
desc = desc.trimmed();
}
return {msg, desc};
}
return {};
}
......@@ -83,6 +83,8 @@ QVector<Branch> getAllBranches(const QString &repo);
* @brief get all local and remote branches + tags
*/
QVector<Branch> getAllBranchesAndTags(const QString &repo, RefType ref = RefType::All);
std::pair<QString, QString> getLastCommitMessage(const QString &repo);
}
Q_DECLARE_TYPEINFO(GitUtils::Branch, Q_MOVABLE_TYPE);
......
......@@ -4,6 +4,8 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "gitcommitdialog.h"
#include "git/gitutils.h"
#include "gitwidget.h"
#include <QCoreApplication>
#include <QDebug>
......@@ -61,6 +63,8 @@ static void changeTextColorToRed(QLineEdit *lineEdit, const QColor &red)
GitCommitDialog::GitCommitDialog(const QString &lastCommit, const QFont &font, QWidget *parent, Qt::WindowFlags f)
: QDialog(parent, f)
{
Q_ASSERT(parent);
setWindowTitle(i18n("Commit Changes"));
ok.setText(i18n("Commit"));
......@@ -78,14 +82,17 @@ GitCommitDialog::GitCommitDialog(const QString &lastCommit, const QFont &font, Q
m_pe.setPlaceholderText(i18n("Extended commit description..."));
m_pe.setFont(font);
/** Dialog's main layout **/
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->setContentsMargins(4, 4, 4, 4);
setLayout(vlayout);
/** Setup the label at the top **/
QHBoxLayout *hLayoutLine = new QHBoxLayout;
hLayoutLine->addStretch();
hLayoutLine->addWidget(&m_leLen);
/** Setup plaintextedit and line edit **/
vlayout->addLayout(hLayoutLine);
vlayout->addWidget(&m_le);
vlayout->addWidget(&m_pe);
......@@ -94,21 +101,37 @@ GitCommitDialog::GitCommitDialog(const QString &lastCommit, const QFont &font, Q
m_pe.resize(width, m_pe.height());
resize(width, fm.averageCharWidth() * 52);
// restore last message ?
if (!lastCommit.isEmpty()) {
auto msgs = lastCommit.split(QStringLiteral("[[\n\n]]"));
if (!msgs.isEmpty()) {
m_le.setText(msgs.at(0));
if (msgs.length() > 1) {
m_pe.setPlainText(msgs.at(1));
}
}
}
loadCommitMessage(lastCommit);
auto bottomLayout = new QHBoxLayout;
/** Setup checkboxes at the bottom **/
m_cbSignOff.setChecked(false);
m_cbSignOff.setText(i18n("Sign off"));
vlayout->addWidget(&m_cbSignOff);
bottomLayout->addWidget(&m_cbSignOff);
m_cbAmend.setChecked(false);
m_cbAmend.setText(i18n("Amend"));
m_cbAmend.setToolTip(i18n("Amend Last Commit"));
connect(&m_cbAmend, &QCheckBox::stateChanged, this, [this](int state) {
if (state != Qt::Checked) {
ok.setText(i18n("Commit"));
setWindowTitle(i18n("Commit Changes"));
return;
}
setWindowTitle(i18n("Amending Commit"));
ok.setText(i18n("Amend"));
const auto [msg, desc] = GitUtils::getLastCommitMessage(static_cast<GitWidget *>(this->parentWidget())->dotGitPath());
m_le.setText(msg);
m_pe.setPlainText(desc);
});
bottomLayout->addWidget(&m_cbAmend);
bottomLayout->addStretch();
vlayout->addLayout(bottomLayout);
/** Setup Ok / Cancel Button **/
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addStretch();
hLayout->addWidget(&ok);
......@@ -122,10 +145,29 @@ GitCommitDialog::GitCommitDialog(const QString &lastCommit, const QFont &font, Q
vlayout->addLayout(hLayout);
/**
* Setup highlighting which changes text color to red if
* it crosses the 72 char threshold
*/
auto hl = new BadLengthHighlighter(m_pe.document(), 72);
Q_UNUSED(hl)
}
void GitCommitDialog::loadCommitMessage(const QString &lastCommit)
{
if (lastCommit.isEmpty()) {
return;
}
// restore last message ?
auto msgs = lastCommit.split(QStringLiteral("[[\n\n]]"));
if (!msgs.isEmpty()) {
m_le.setText(msgs.at(0));
if (msgs.length() > 1) {
m_pe.setPlainText(msgs.at(1));
}
}
}
QString GitCommitDialog::subject() const
{
return m_le.text();
......@@ -141,13 +183,23 @@ bool GitCommitDialog::signoff() const
return m_cbSignOff.isChecked();
}
bool GitCommitDialog::amendingLastCommit() const
{
return m_cbAmend.isChecked();
}
void GitCommitDialog::setAmendingCommit()
{
m_cbAmend.setChecked(true);
}
void GitCommitDialog::updateLineSizeLabel()
{
const QColor red = KColorScheme().foreground(KColorScheme::NegativeText).color();
int len = m_le.text().length();
if (len < 52) {
m_leLen.setText(i18nc("Number of characters", "%1 / 52", QString::number(len)));
} else {
const QColor red = KColorScheme().foreground(KColorScheme::NegativeText).color();
changeTextColorToRed(&m_le, red);
m_leLen.setText(i18nc("Number of characters", "<span style=\"color:%1;\">%2</span> / 52", red.name(), QString::number(len)));
}
......
......@@ -24,9 +24,12 @@ public:
QString subject() const;
QString description() const;
bool signoff() const;
bool amendingLastCommit() const;
void setAmendingCommit();
private:
Q_SLOT void updateLineSizeLabel();
void loadCommitMessage(const QString &lastCommit);
QLineEdit m_le;
QPlainTextEdit m_pe;
......@@ -35,6 +38,7 @@ private:
QLabel m_leLen;
QLabel m_peLen;
QCheckBox m_cbSignOff;
QCheckBox m_cbAmend;
};
#endif // GITCOMMITDIALOG_H
......@@ -233,7 +233,7 @@ GitWidget::GitWidget(KateProject *project, KTextEditor::MainWindow *mainWindow,
m_mainView->setLayout(layout);
connect(&m_gitStatusWatcher, &QFutureWatcher<GitUtils::GitParsedStatus>::finished, this, &GitWidget::parseStatusReady);
connect(m_commitBtn, &QPushButton::clicked, this, &GitWidget::opencommitChangesDialog);
connect(m_commitBtn, &QPushButton::clicked, this, &GitWidget::openCommitChangesDialog);
// single / double click
connect(m_treeView, &QTreeView::clicked, this, &GitWidget::treeViewSingleClicked);
......@@ -516,12 +516,18 @@ void GitWidget::launchExternalDiffTool(const QString &file, bool staged)
git.startDetached(QStringLiteral("git"), args, m_gitPath);
}
void GitWidget::commitChanges(const QString &msg, const QString &desc, bool signOff)
void GitWidget::commitChanges(const QString &msg, const QString &desc, bool signOff, bool amend)
{
auto args = QStringList{QStringLiteral("commit")};
if (amend) {
args.append(QStringLiteral("--amend"));
}
if (signOff) {
args.append(QStringLiteral("-s"));
}
args.append(QStringLiteral("-m"));
args.append(msg);
if (!desc.isEmpty()) {
......@@ -607,9 +613,9 @@ void GitWidget::applyDiff(const QString &fileName, bool staged, bool hunk, KText
git->start(QProcess::ReadOnly);
}
void GitWidget::opencommitChangesDialog()
void GitWidget::openCommitChangesDialog(bool amend)
{
if (m_model->stagedFiles().isEmpty()) {
if (!amend && m_model->stagedFiles().isEmpty()) {
return sendMessage(i18n("Nothing to commit. Please stage your changes first."), true);
}
......@@ -623,6 +629,10 @@ void GitWidget::opencommitChangesDialog()
GitCommitDialog *dialog = new GitCommitDialog(m_commitMessage, font, this);
if (amend) {
dialog->setAmendingCommit();
}
connect(dialog, &QDialog::finished, this, [this, dialog](int res) {
dialog->deleteLater();
if (res == QDialog::Accepted) {
......@@ -630,7 +640,7 @@ void GitWidget::opencommitChangesDialog()
return sendMessage(i18n("Commit message cannot be empty."), true);
}
m_commitMessage = dialog->subject() + QStringLiteral("[[\n\n]]") + dialog->description();
commitChanges(dialog->subject(), dialog->description(), dialog->signoff());
commitChanges(dialog->subject(), dialog->description(), dialog->signoff(), dialog->amendingLastCommit());
}
});
......@@ -804,6 +814,10 @@ void GitWidget::buildMenu()
});
r->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
m_gitMenu->addAction(QIcon::fromTheme(QStringLiteral("document-edit")), i18n("Amend Last Commit"), this, [this] {
openCommitChangesDialog(/* amend = */ true);
});
auto a = m_gitMenu->addAction(i18n("Checkout Branch"), this, [this] {
BranchCheckoutDialog bd(m_mainWin->window(), m_pluginView, m_project->baseDir());
bd.openDialog();
......
......@@ -49,6 +49,11 @@ public:
// will just proxy the message to the plugin view
void sendMessage(const QString &message, bool warn);
QString dotGitPath() const
{
return m_gitPath;
}
private:
QToolButton *m_menuBtn;
QToolButton *m_commitBtn;
......@@ -85,7 +90,7 @@ private:
void openAtHEAD(const QString &file);
void showDiff(const QString &file, bool staged);
void launchExternalDiffTool(const QString &file, bool staged);
void commitChanges(const QString &msg, const QString &desc, bool signOff);
void commitChanges(const QString &msg, const QString &desc, bool signOff, bool amend = false);
void applyDiff(const QString &fileName, bool staged, bool hunk, KTextEditor::View *v);
void numStatForStatus(QVector<GitUtils::StatusItem> &list, bool modified);
void branchCompareFiles(const QString &from, const QString &to);
......@@ -104,7 +109,7 @@ private:
private Q_SLOTS:
void parseStatusReady();
void opencommitChangesDialog();
void openCommitChangesDialog(bool amend = false);
void handleClick(const QModelIndex &idx, ClickAction clickAction);
void treeViewSingleClicked(const QModelIndex &idx);
void treeViewDoubleClicked(const QModelIndex &idx);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment