diff --git a/src/dialogs/AmendDialog.cpp b/src/dialogs/AmendDialog.cpp new file mode 100644 index 00000000..d7e8f970 --- /dev/null +++ b/src/dialogs/AmendDialog.cpp @@ -0,0 +1,208 @@ +#include "AmendDialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum Row { Author = 0, Committer, CommitMessageLabel, CommitMessage, Buttons }; + +class DateSelectionGroupWidget : public QGroupBox { + Q_OBJECT +public: + DateSelectionGroupWidget(QWidget *parent = nullptr) + : QGroupBox(tr("Datetime source"), parent) { + QHBoxLayout *l = new QHBoxLayout(); + + current = new QRadioButton(tr("Current"), this); + current->setObjectName("Current"); + manual = new QRadioButton(tr("Manual"), this); + manual->setObjectName("Manual"); + original = new QRadioButton(tr("Original"), this); + original->setObjectName("Original"); + + current->setChecked(true); + + connect(current, &QRadioButton::clicked, + [this]() { emit typeChanged(type()); }); + connect(manual, &QRadioButton::clicked, + [this]() { emit typeChanged(type()); }); + connect(original, &QRadioButton::clicked, + [this]() { emit typeChanged(type()); }); + + l->addWidget(current); + l->addWidget(manual); + l->addWidget(original); + setLayout(l); + } + ContributorInfo::SelectedDateTimeType type() { + if (original->isChecked()) { + return ContributorInfo::SelectedDateTimeType::Original; + } else if (manual->isChecked()) { + return ContributorInfo::SelectedDateTimeType::Manual; + } + return ContributorInfo::SelectedDateTimeType::Current; + } + +signals: + void typeChanged(ContributorInfo::SelectedDateTimeType); + +private: + QRadioButton *current; + QRadioButton *manual; + QRadioButton *original; +}; + +class InfoBox : public QGroupBox { + Q_OBJECT +public: + enum LocalRow { Name = 0, Email, DateType, CommitDate }; + + InfoBox(const QString &title, const git::Signature &signature, + QWidget *parent = nullptr) + : QGroupBox(title + ":", parent), m_signature(signature) { + + setObjectName(title); + + auto *l = new QVBoxLayout(); + + auto *lName = new QLabel(tr("Name:"), this); + m_name = new QLineEdit(signature.name(), this); + m_name->setObjectName("Name"); + auto *hName = new QHBoxLayout(); + hName->addWidget(lName); + hName->addWidget(m_name); + + auto *lEmail = new QLabel(tr("Email:"), this); + m_email = new QLineEdit(signature.email(), this); + m_email->setObjectName("Email"); + auto *hEmail = new QHBoxLayout(); + hEmail->addWidget(lEmail); + hEmail->addWidget(m_email); + + m_commitDateType = new DateSelectionGroupWidget(this); + m_commitDateType->setObjectName(title + "CommitDateType"); + m_lCommitDate = new QLabel(tr("Commit date:"), this); + m_commitDate = new QDateTimeEdit(signature.date().toLocalTime(), this); + m_commitDate->setObjectName(title + "CommitDate"); + QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Preferred); + m_commitDate->setSizePolicy(sp); + auto *hDate = new QHBoxLayout(); + hDate->addWidget(m_lCommitDate); + hDate->addWidget(m_commitDate); + + l->addLayout(hName); + l->addLayout(hEmail); + l->addWidget(m_commitDateType); + l->addLayout(hDate); + + setLayout(l); + + dateTimeTypeChanged(m_commitDateType->type()); + + connect(m_commitDateType, &DateSelectionGroupWidget::typeChanged, this, + &InfoBox::dateTimeTypeChanged); + } + + ContributorInfo getInfo() const { + ContributorInfo ci; + ci.name = name(); + ci.email = email(); + ci.commitDate = commitDate(); + ci.commitDateType = commitDateType(); + + return ci; + } + +private slots: + void dateTimeTypeChanged(const ContributorInfo::SelectedDateTimeType type) { + const auto enabled = type == ContributorInfo::SelectedDateTimeType::Manual; + m_lCommitDate->setVisible(enabled); + m_commitDate->setVisible(enabled); + } + +private: + QString name() const { return m_name->text(); } + + QString email() const { return m_email->text(); } + + QDateTime commitDate() const { + if (commitDateType() == ContributorInfo::SelectedDateTimeType::Original) { + return m_signature.date().toLocalTime(); + } else { + return m_commitDate->dateTime(); + } + } + + ContributorInfo::SelectedDateTimeType commitDateType() const { + return m_commitDateType->type(); + } + + QLineEdit *m_name; + QLineEdit *m_email; + QDateTimeEdit *m_commitDate; + QLabel *m_lCommitDate; + DateSelectionGroupWidget *m_commitDateType; + + git::Signature m_signature; +}; + +AmendDialog::AmendDialog(const git::Signature &author, + const git::Signature &committer, + const QString &commitMessage, QWidget *parent) + : QDialog(parent) { + + auto *l = new QGridLayout(); + + // author + // committer + // message + + m_authorInfo = new InfoBox(tr("Author"), author, this); + l->addWidget(m_authorInfo, Row::Author, 0, 1, 2); + + m_committerInfo = new InfoBox(tr("Committer"), committer, this); + l->addWidget(m_committerInfo, Row::Committer, 0, 1, 2); + + auto *lMessage = new QLabel(tr("Commit Message:"), this); + m_commitMessage = new QTextEdit(commitMessage, this); + m_commitMessage->setObjectName("Textlabel Commit Message"); + l->addWidget(lMessage, Row::CommitMessageLabel, 0); + l->addWidget(m_commitMessage, Row::CommitMessage, 0, 1, 2); + + auto *ok = new QPushButton(tr("Amend"), this); + auto *cancel = new QPushButton(tr("Cancel"), this); + + connect(ok, &QPushButton::clicked, this, &QDialog::accept); + connect(cancel, &QPushButton::clicked, this, &QDialog::reject); + + auto *hl = new QHBoxLayout(); + hl->addWidget(cancel); + hl->addWidget(ok); + + l->addLayout(hl, Buttons, 1); + + setLayout(l); +} + +AmendInfo AmendDialog::getInfo() const { + AmendInfo ai; + ai.authorInfo = m_authorInfo->getInfo(); + ai.committerInfo = m_committerInfo->getInfo(); + ai.commitMessage = commitMessage(); + + return ai; +} + +QString AmendDialog::commitMessage() const { + return m_commitMessage->toPlainText(); +} + +#include "AmendDialog.moc" diff --git a/src/dialogs/AmendDialog.h b/src/dialogs/AmendDialog.h new file mode 100644 index 00000000..670be074 --- /dev/null +++ b/src/dialogs/AmendDialog.h @@ -0,0 +1,41 @@ +#include +#include +#include "git/Signature.h" + +class QLineEdit; +class QTextEdit; +class QCheckBox; +class QDateTimeEdit; +class DateSelectionGroupWidget; +class QLabel; +class InfoBox; +class AmendDialog; + +struct ContributorInfo { + enum class SelectedDateTimeType { Current, Manual, Original }; + QString name; + QString email; + QDateTime commitDate; + SelectedDateTimeType commitDateType; +}; + +struct AmendInfo { + ContributorInfo authorInfo; + ContributorInfo committerInfo; + QString commitMessage; +}; + +class AmendDialog : public QDialog { +public: + AmendDialog(const git::Signature &author, const git::Signature &committer, + const QString &commitMessage, QWidget *parent = nullptr); + + AmendInfo getInfo() const; + +private: + QString commitMessage() const; + + InfoBox *m_authorInfo; + InfoBox *m_committerInfo; + QTextEdit *m_commitMessage; +}; \ No newline at end of file diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt index e0a65f6a..bcb82b87 100644 --- a/src/dialogs/CMakeLists.txt +++ b/src/dialogs/CMakeLists.txt @@ -3,6 +3,7 @@ add_library( AboutDialog.cpp AccountDialog.cpp AddRemoteDialog.cpp + AmendDialog.cpp BranchDelegate.cpp BranchTableModel.cpp CheckoutDialog.cpp diff --git a/src/git/Commit.cpp b/src/git/Commit.cpp index ad47a982..771dbccb 100644 --- a/src/git/Commit.cpp +++ b/src/git/Commit.cpp @@ -20,6 +20,7 @@ #include "git2/diff.h" #include "git2/refs.h" #include "git2/revert.h" +#include "git2/commit.h" #include #include #include @@ -240,6 +241,16 @@ bool Commit::revert() const { return !error; } +bool Commit::amend(const Signature &author, const Signature &committer, + const QString &commitMessage, const Tree &tree) const { + Repository repo = this->repo(); + git_oid oid; + int error = git_commit_amend(&oid, *this, "HEAD", &*author, &*committer, NULL, + commitMessage.toUtf8(), tree); + emit repo.notifier()->referenceUpdated(repo.head()); + return !error; +} + bool Commit::reset(git_reset_t type, const QStringList &paths) const { QVector rawPaths; QVector storage; diff --git a/src/git/Commit.h b/src/git/Commit.h index 9821bfb6..1a30d7b9 100644 --- a/src/git/Commit.h +++ b/src/git/Commit.h @@ -65,6 +65,9 @@ public: // Revert this commit in the index and workdir. bool revert() const; + bool amend(const Signature &author, const Signature &committer, + const QString &commitMessage, const Tree &tree) const; + // Reset HEAD to this commit. bool reset(git_reset_t type = GIT_RESET_MIXED, const QStringList &paths = QStringList()) const; diff --git a/src/git/Repository.cpp b/src/git/Repository.cpp index 11690cfb..cd0d9692 100644 --- a/src/git/Repository.cpp +++ b/src/git/Repository.cpp @@ -188,6 +188,15 @@ Config Repository::appConfig() const { bool Repository::isBare() const { return git_repository_is_bare(d->repo); } +Signature Repository::signature(const QString &name, const QString &email) { + return Signature(name, email); +} + +Signature Repository::signature(const QString &name, const QString &email, + const QDateTime &date) { + return Signature(name, email, date); +} + Signature Repository::defaultSignature(bool *fake, const QString &overrideUser, const QString &overrideEmail) const { QString name, email; @@ -538,6 +547,24 @@ Commit Repository::lookupCommit(const Id &id) const { return Commit(commit); } +bool Repository::amend(const git::Commit &commitToAmend, + const git::Signature &author, + const git::Signature &committer, + const QString &commitMessage) { + + // Write the index tree. + Index idx = index(); + if (!idx.isValid()) + return false; + + // Add new staged files to the amended commit + Tree tree = idx.writeTree(); + if (!tree.isValid()) + return false; + + return commitToAmend.amend(author, committer, commitMessage, tree); +} + Commit Repository::commit(const QString &message, const AnnotatedCommit &mergeHead, bool *fakeSignature, const QString &overrideUser, diff --git a/src/git/Repository.h b/src/git/Repository.h index 1277e8b4..b8baa1bb 100644 --- a/src/git/Repository.h +++ b/src/git/Repository.h @@ -92,6 +92,10 @@ public: const QString &overrideUser = QString(), const QString &overrideEmail = QString()) const; + Signature signature(const QString &name, const QString &email); + Signature signature(const QString &name, const QString &email, + const QDateTime &date); + // ignore bool isIgnored(const QString &path) const; @@ -151,6 +155,9 @@ public: const QString &message, const AnnotatedCommit &mergeHead = AnnotatedCommit()); + bool amend(const Commit &commitToAmend, const Signature &author, + const Signature &committer, const QString &commitMessage); + QList starredCommits() const; bool isCommitStarred(const Id &commit) const; void setCommitStarred(const Id &commit, bool starred); diff --git a/src/git/Signature.cpp b/src/git/Signature.cpp index 1236e09c..55cace9e 100644 --- a/src/git/Signature.cpp +++ b/src/git/Signature.cpp @@ -17,6 +17,23 @@ Signature::Signature(git_signature *signature, bool owned) : d( signature, owned ? git_signature_free : [](git_signature *) {}) {} +Signature::Signature(const QString &name, const QString &email) { + git_signature *signature = nullptr; + + git_signature_now(&signature, name.toUtf8(), email.toUtf8()); + d = QSharedPointer(signature, git_signature_free); +} + +Signature::Signature(const QString &name, const QString &email, + const QDateTime &date) { + git_signature *signature = nullptr; + + auto offset = date.offsetFromUtc() / 60; + git_signature_new(&signature, name.toUtf8(), email.toUtf8(), + date.toSecsSinceEpoch(), offset); + d = QSharedPointer(signature, git_signature_free); +} + Signature::operator const git_signature *() const { return d.data(); } QString Signature::name() const { return d->name; } diff --git a/src/git/Signature.h b/src/git/Signature.h index c541875c..c3a516e0 100644 --- a/src/git/Signature.h +++ b/src/git/Signature.h @@ -35,6 +35,8 @@ public: private: Signature(git_signature *signature = nullptr, bool owned = false); + Signature(const QString &name, const QString &email); + Signature(const QString &name, const QString &email, const QDateTime &date); operator const git_signature *() const; QSharedPointer d; diff --git a/src/ui/CommitList.cpp b/src/ui/CommitList.cpp index a4a784b7..cd533262 100644 --- a/src/ui/CommitList.cpp +++ b/src/ui/CommitList.cpp @@ -48,8 +48,6 @@ const QString kPathspecFmt = "pathspec:%1"; // FIXME: Use 'core.abbrev' config instead? const int kShortIdSize = 7; -enum Role { DiffRole = Qt::UserRole, CommitRole, GraphRole, GraphColorRole }; - enum GraphSegment { Dot, Top, @@ -344,7 +342,7 @@ public: return mStatus.isFinished() ? QVariant() : mProgress; - case DiffRole: { + case CommitList::Role::DiffRole: { if (status) return QVariant::fromValue(this->status()); @@ -354,10 +352,10 @@ public: return QVariant::fromValue(diff); } - case CommitRole: + case CommitList::Role::CommitRole: return status ? QVariant() : QVariant::fromValue(row.commit); - case GraphRole: { + case CommitList::Role::GraphRole: { QVariantList columns; foreach (const Column &column, row.columns) { QVariantList segments; @@ -369,7 +367,7 @@ public: return columns; } - case GraphColorRole: { + case CommitList::Role::GraphColorRole: { QVariantList columns; foreach (const Column &column, row.columns) { QVariantList segments; @@ -571,7 +569,7 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { switch (role) { - case DiffRole: { + case CommitList::Role::DiffRole: { git::Commit commit = mCommits.at(index.row()); bool ignoreWhitespace = Settings::instance()->isWhitespaceIgnored(); git::Diff diff = commit.diff(git::Commit(), -1, ignoreWhitespace); @@ -579,7 +577,7 @@ public: return QVariant::fromValue(diff); } - case CommitRole: + case CommitList::Role::CommitRole: return QVariant::fromValue(mCommits.at(index.row())); } @@ -650,8 +648,9 @@ public: // Draw graph. painter->save(); - QVariantList columns = index.data(GraphRole).toList(); - QVariantList colorColumns = index.data(GraphColorRole).toList(); + QVariantList columns = index.data(CommitList::Role::GraphRole).toList(); + QVariantList colorColumns = + index.data(CommitList::Role::GraphColorRole).toList(); for (int i = 0; i < columns.size(); ++i) { int x = rect.x(); int y = rect.y(); @@ -759,7 +758,8 @@ public: rect.setWidth(rect.width() - constants.hMargin); // Draw content. - git::Commit commit = index.data(CommitRole).value(); + git::Commit commit = + index.data(CommitList::Role::CommitRole).value(); if (commit.isValid()) { const QFontMetrics &fm = opt.fontMetrics; QRect star = rect; diff --git a/src/ui/CommitList.h b/src/ui/CommitList.h index 0af6e192..3b218fd4 100644 --- a/src/ui/CommitList.h +++ b/src/ui/CommitList.h @@ -24,6 +24,8 @@ class CommitList : public QListView { Q_OBJECT public: + enum Role { DiffRole = Qt::UserRole, CommitRole, GraphRole, GraphColorRole }; + CommitList(Index *index, QWidget *parent = nullptr); // Get the status diff item. diff --git a/src/ui/DetailView.cpp b/src/ui/DetailView.cpp index e328aba4..90c65cb6 100644 --- a/src/ui/DetailView.cpp +++ b/src/ui/DetailView.cpp @@ -353,8 +353,8 @@ public: committer.join(", ")); // Set date range. - QDate lastDate = last.committer().date().date(); - QDate firstDate = first.committer().date().date(); + QDate lastDate = last.committer().date().toLocalTime().date(); + QDate firstDate = first.committer().date().toLocalTime().date(); QString lastDateStr = lastDate.toString(Qt::DefaultLocaleShortDate); QString firstDateStr = firstDate.toString(Qt::DefaultLocaleShortDate); QString dateStr = (lastDate == firstDate) @@ -391,7 +391,7 @@ public: git::Commit commit = commits.first(); git::Signature author = commit.author(); git::Signature committer = commit.committer(); - QDateTime date = commit.committer().date(); + QDateTime date = commit.committer().date().toLocalTime(); mHash->setText(brightText(tr("Id:")) + " " + commit.shortId()); mAuthorCommitterDate->setDate( brightText(date.toString(Qt::DefaultLocaleLongDate))); diff --git a/src/ui/RepoView.cpp b/src/ui/RepoView.cpp index 1d79f496..4e818185 100644 --- a/src/ui/RepoView.cpp +++ b/src/ui/RepoView.cpp @@ -23,6 +23,7 @@ #include "ToolBar.h" #include "app/Application.h" #include "conf/Settings.h" +#include "dialogs/AmendDialog.h" #include "dialogs/CheckoutDialog.h" #include "dialogs/CommitDialog.h" #include "dialogs/DeleteBranchDialog.h" @@ -40,6 +41,7 @@ #include "git/Signature.h" #include "git/TagRef.h" #include "git/Tree.h" +#include "git/Signature.h" #include "git2/merge.h" #include "host/Accounts.h" #include "index/Index.h" @@ -1877,25 +1879,7 @@ void RepoView::amendCommit() { if (!commit.isValid()) return; - QList parents = commit.parents(); - switch (parents.size()) { - case 0: - // Return to the unborn HEAD state. - // FIXME: Prompt to reset? - head.remove(true); - mDetails->setCommitMessage(commit.message()); - refresh(); - break; - - case 1: - // Reset to parent commit. - promptToReset(parents.first(), GIT_RESET_SOFT, commit); - break; - - default: - // FIXME: Return to merging state? - break; - } + promptToAmend(commit); } void RepoView::promptToCheckout() { @@ -2159,18 +2143,54 @@ void RepoView::promptToDeleteTag(const git::Reference &ref) { dialog->open(); } -void RepoView::promptToReset(const git::Commit &commit, git_reset_t type, - const git::Commit &commitToAmend) { +void RepoView::promptToAmend(const git::Commit &commit) { + auto *d = new AmendDialog(commit.author(), commit.committer(), + commit.message(), this); + d->setAttribute(Qt::WA_DeleteOnClose); + connect(d, &QDialog::accepted, [this, d, commit]() { + auto info = d->getInfo(); + + git::Signature author = getSignature(info.authorInfo); + git::Signature committer = getSignature(info.committerInfo); + + amend(commit, author, committer, info.commitMessage); + }); + + d->show(); +} + +void RepoView::amend(const git::Commit &commit, const git::Signature &author, + const git::Signature &committer, + const QString &commitMessage) { + git::Reference head = mRepo.head(); + Q_ASSERT(head.isValid()); + + QString title = tr("Amend"); + + if (!mRepo.amend(commit, author, committer, commitMessage)) { + error(addLogEntry(tr("Amending commit %1").arg(commit.link()), title), + tr("amend"), head.name()); + } else { + head = mRepo.head(); + Q_ASSERT(head.isValid()); + + QString text = + tr("%1 to %2", "update ref").arg(head.name(), head.target().link()); + addLogEntry(text, title); + } +} + +void RepoView::promptToReset(const git::Commit &commit, git_reset_t type) { git::Branch head = mRepo.head(); if (!head.isValid()) { - QString title = commitToAmend ? tr("Amend") : tr("Reset"); + QString title = tr("Reset"); LogEntry *entry = addLogEntry(tr("no branch"), title); entry->addEntry(LogEntry::Error, tr("You are not currently on a branch.")); return; } - QString id = commitToAmend ? commitToAmend.shortId() : commit.shortId(); - QString title = commitToAmend ? tr("Amend") : tr("Reset"); + QString id = commit.shortId(); + QString title = tr("Reset"); switch (type) { case GIT_RESET_SOFT: title += " Soft"; @@ -2184,10 +2204,8 @@ void RepoView::promptToReset(const git::Commit &commit, git_reset_t type, } title += "?"; - QString text = commitToAmend - ? tr("Are you sure you want to amend '%1'?").arg(id) - : tr("Are you sure you want to reset '%1' to '%2'?") - .arg(head.name(), id); + QString text = + tr("Are you sure you want to reset '%1' to '%2'?").arg(head.name(), id); QMessageBox *dialog = new QMessageBox(QMessageBox::Warning, title, text, QMessageBox::Cancel, this); dialog->setAttribute(Qt::WA_DeleteOnClose); @@ -2211,16 +2229,10 @@ void RepoView::promptToReset(const git::Commit &commit, git_reset_t type, dialog->setInformativeText(info); - QString buttonText = commitToAmend ? tr("Amend") : tr("Reset"); + QString buttonText = tr("Reset"); QPushButton *accept = dialog->addButton(buttonText, QMessageBox::AcceptRole); connect(accept, &QPushButton::clicked, this, - [this, commit, type, commitToAmend] { - reset(commit, type, commitToAmend); - - // Pre-populate the commit message editor. - if (commitToAmend) - mDetails->setCommitMessage(commitToAmend.message()); - }); + [this, commit, type] { reset(commit, type); }); dialog->open(); } @@ -2901,6 +2913,13 @@ bool RepoView::checkForConflicts(LogEntry *parent, const QString &action) { return true; } +git::Signature RepoView::getSignature(const ContributorInfo &info) { + if (info.commitDateType != ContributorInfo::SelectedDateTimeType::Current) + return mRepo.signature(info.name, info.email, info.commitDate); + + return mRepo.signature(info.name, info.email); +} + bool RepoView::match(QObject *search, QObject *parent) { QObjectList children = parent->children(); for (auto child : children) { diff --git a/src/ui/RepoView.h b/src/ui/RepoView.h index 91fb107b..6c13ed04 100644 --- a/src/ui/RepoView.h +++ b/src/ui/RepoView.h @@ -41,6 +41,7 @@ class PathspecWidget; class ReferenceWidget; class RemoteCallbacks; class ToolBar; +class ContributorInfo; namespace git { class Result; @@ -262,9 +263,12 @@ public: void promptToAddTag(const git::Commit &commit); void promptToDeleteTag(const git::Reference &ref); + void promptToAmend(const git::Commit &commit); + void amend(const git::Commit &commit, const git::Signature &author, + const git::Signature &committer, const QString &commitMessage); + // reset - void promptToReset(const git::Commit &commit, git_reset_t type, - const git::Commit &commitToAmend = git::Commit()); + void promptToReset(const git::Commit &commit, git_reset_t type); void reset(const git::Commit &commit, git_reset_t type, const git::Commit &commitToAmend = git::Commit()); @@ -392,6 +396,8 @@ private: bool checkForConflicts(LogEntry *parent, const QString &action); + git::Signature getSignature(const ContributorInfo &info); + git::Repository mRepo; Index *mIndex; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6174c885..ab1a523c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -101,4 +101,5 @@ test(NAME rebase) test(NAME TreeView) test(NAME Submodule) test(NAME referencelist) +test(NAME amend) test(NAME SshConfig) diff --git a/test/amend.cpp b/test/amend.cpp new file mode 100644 index 00000000..2725e9c7 --- /dev/null +++ b/test/amend.cpp @@ -0,0 +1,368 @@ +#include "Test.h" + +#include "git/Signature.h" +#include "git/Reference.h" +#include "git/Tree.h" +#include "ui/MainWindow.h" +#include "ui/DoubleTreeWidget.h" +#include "ui/RepoView.h" +#include "ui/TreeView.h" +#include "dialogs/AmendDialog.h" + +#include +#include +#include +#include + +#define INIT_REPO(repoPath, /* bool */ useTempDir) \ + QString path = Test::extractRepository(repoPath, useTempDir); \ + QVERIFY(!path.isEmpty()); \ + git::Repository repo = git::Repository::open(path); \ + QVERIFY(repo.isValid()); + +class TestAmend : public QObject { + Q_OBJECT + +private slots: + void testAmend(); + void testAmendAddFile(); + void testAmendDialog(); + void testAmendDialog2(); +}; + +using namespace git; + +void TestAmend::testAmend() { + INIT_REPO("CherryPickAuthorEmail.zip", true); + + git::Reference master = repo.lookupRef(QString("refs/heads/master")); + QVERIFY(master.isValid()); + auto c = master.annotatedCommit().commit(); + QCOMPARE(c.message(), "changes"); + QCOMPARE(c.author().email(), "martin.marmsoler@gmail.com"); + QCOMPARE(c.author().name(), "Martin Marmsoler"); + QCOMPARE( + c.author().date(), + QDateTime::fromString("Sun May 22 10:36:26 2022 +0200", Qt::RFC2822Date)); + QCOMPARE(c.committer().name(), "Martin Marmsoler"); + QCOMPARE(c.committer().email(), "martin.marmsoler@gmail.com"); + QCOMPARE( + c.committer().date(), + QDateTime::fromString("Sun May 22 10:36:26 2022 +0200", Qt::RFC2822Date)); + + const QString commitMessage = "New commit message"; + + auto authorSignature = repo.signature( + "New Author", "New Author Email", + QDateTime::fromString("Sun May 23 10:36:26 2022 +0200", Qt::RFC2822Date)); + auto committerSignature = repo.signature( + "New Committer", "New Committer Email", + QDateTime::fromString("Sun May 23 11:36:26 2022 +0200", Qt::RFC2822Date)); + + Tree tree; + c.amend(authorSignature, committerSignature, commitMessage, tree); + + master = repo.lookupRef(QString("refs/heads/master")); + QVERIFY(master.isValid()); + c = master.annotatedCommit().commit(); + QCOMPARE(c.message(), "New commit message"); + QCOMPARE(c.author().email(), "New Author Email"); + QCOMPARE(c.author().name(), "New Author"); + QCOMPARE( + c.author().date(), + QDateTime::fromString("Sun May 23 10:36:26 2022 +0200", Qt::RFC2822Date)); + QCOMPARE(c.committer().name(), "New Committer"); + QCOMPARE(c.committer().email(), "New Committer Email"); + QCOMPARE( + c.committer().date(), + QDateTime::fromString("Sun May 23 11:36:26 2022 +0200", Qt::RFC2822Date)); +} + +void TestAmend::testAmendAddFile() { + + // Create repo + Test::ScratchRepository mRepo; + auto mMainBranch = mRepo->unbornHeadName(); + auto mWindow = new MainWindow(mRepo); + mWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(mWindow)); + RepoView *view = mWindow->currentView(); + + { + // Add file and refresh. + QFile file(mRepo->workdir().filePath("test")); + QVERIFY(file.open(QFile::WriteOnly)); + QTextStream(&file) << "This will be a test." << endl; + + Test::refresh(view); + + auto doubleTree = view->findChild(); + QVERIFY(doubleTree); + + auto files = doubleTree->findChild("Unstaged"); + QVERIFY(files); + + QAbstractItemModel *model = files->model(); + QCOMPARE(model->rowCount(), 1); + + // Click on the check box. + QModelIndex index = model->index(0, 0); + QTest::mouseClick(files->viewport(), Qt::LeftButton, + Qt::KeyboardModifiers(), + files->checkRect(index).center()); + + // Commit and refresh. + QTextEdit *editor = view->findChild("MessageEditor"); + QVERIFY(editor); + + editor->setText("base commit"); + view->commit(); + Test::refresh(view, false); + + // Create branch and stage changes + git::Branch branch2 = + mRepo->createBranch("branch2", mRepo->head().target()); + QVERIFY(branch2.isValid()); + + view->checkout(branch2); + QCOMPARE(mRepo->head().name(), QString("branch2")); + + // Check if file has correct content + QVERIFY(branch2.isValid()); + auto c = branch2.annotatedCommit().commit(); + QCOMPARE(c.blob("test").content(), "This will be a test.\n"); + } + + // Stage file with changes + { + QFile file(mRepo->workdir().filePath("test")); + QVERIFY(file.open(QFile::WriteOnly)); + QTextStream(&file) << "Changes made" << endl; + + Test::refresh(view); + + auto doubleTree = view->findChild(); + QVERIFY(doubleTree); + + // Staging the file + auto files = doubleTree->findChild("Unstaged"); + QVERIFY(files); + + QAbstractItemModel *model = files->model(); + QCOMPARE(model->rowCount(), 1); + + // Click on the check box. to stage file + QModelIndex index = model->index(0, 0); + QTest::mouseClick(files->viewport(), Qt::LeftButton, + Qt::KeyboardModifiers(), + files->checkRect(index).center()); + } + + // Check that changes applied after amending + { + // Amend changes + git::Reference branch2 = mRepo->lookupRef(QString("refs/heads/branch2")); + QVERIFY(branch2.isValid()); + auto c = branch2.annotatedCommit().commit(); + auto authorSignature = mRepo->signature("New Author", "New Author Email"); + auto committerSignature = + mRepo->signature("New Committer", "New Committer Email"); + QCOMPARE(mRepo->amend(c, authorSignature, committerSignature, + "New commit message"), + true); + + { + branch2 = mRepo->lookupRef(QString("refs/heads/branch2")); + QVERIFY(branch2.isValid()); + c = branch2.annotatedCommit().commit(); + QCOMPARE(c.message(), "New commit message"); + QCOMPARE(c.author().email(), "New Author Email"); + QCOMPARE(c.author().name(), "New Author"); + QCOMPARE(c.committer().name(), "New Committer"); + QCOMPARE(c.committer().email(), "New Committer Email"); + + QCOMPARE(c.blob("test").content(), "Changes made\n"); + } + } +} + +void TestAmend::testAmendDialog() { + // Checking that original information is passed to the return function + // correctly Name and email will not be changed. Only different datetypes are + // tested + Test::ScratchRepository repo; + auto authorSignature = repo->signature( + "New Author", "New Author Email", + QDateTime::fromString("Sun May 23 10:36:26 2022 +0200", Qt::RFC2822Date)); + auto committerSignature = repo->signature( + "New Committer", "New Committer Email", + QDateTime::fromString("Sun May 23 11:36:26 2022 +0200", Qt::RFC2822Date)); + + { + AmendDialog d(authorSignature, committerSignature, "Test commit message"); + d.show(); + + { + const auto *authorCommitDateTimeTypeSelection = + d.findChild(tr("Author") + "CommitDateType"); + QVERIFY(authorCommitDateTimeTypeSelection); + auto *authorCurrent = + authorCommitDateTimeTypeSelection->findChild( + "Current"); + QVERIFY(authorCurrent); + auto *authorOriginal = + authorCommitDateTimeTypeSelection->findChild( + "Original"); + QVERIFY(authorOriginal); + auto *authorManual = + authorCommitDateTimeTypeSelection->findChild( + "Manual"); + QVERIFY(authorManual); + auto *authorCommitDate = + d.findChild(tr("Author") + "CommitDate"); + QVERIFY(authorCommitDate); + authorCommitDate->setDateTime( + QDateTime(QDate(2012, 7, 6), QTime(8, 30, 5))); + + // current + authorCurrent->click(); + auto info = d.getInfo(); + QCOMPARE(info.authorInfo.commitDateType, + ContributorInfo::SelectedDateTimeType::Current); + QCOMPARE(authorCommitDate->isVisible(), false); + + // original + authorOriginal->click(); + info = d.getInfo(); + QCOMPARE(info.authorInfo.commitDateType, + ContributorInfo::SelectedDateTimeType::Original); + QCOMPARE(authorCommitDate->isVisible(), false); + QCOMPARE(info.authorInfo.commitDate, + QDateTime::fromString("Sun May 23 10:36:26 2022 +0200", + Qt::RFC2822Date)); + + // manual + authorManual->click(); + info = d.getInfo(); + QCOMPARE(info.authorInfo.commitDateType, + ContributorInfo::SelectedDateTimeType::Manual); + QCOMPARE(authorCommitDate->isVisible(), true); + QCOMPARE(info.authorInfo.commitDate, + QDateTime(QDate(2012, 7, 6), QTime(8, 30, 5))); + } + + { + const auto *committerCommitDateTimeTypeSelection = + d.findChild(tr("Committer") + "CommitDateType"); + QVERIFY(committerCommitDateTimeTypeSelection); + auto *committerCurrent = + committerCommitDateTimeTypeSelection->findChild( + "Current"); + QVERIFY(committerCurrent); + auto *committerOriginal = + committerCommitDateTimeTypeSelection->findChild( + "Original"); + QVERIFY(committerOriginal); + auto *committerManual = + committerCommitDateTimeTypeSelection->findChild( + "Manual"); + QVERIFY(committerManual); + auto *committerCommitDate = + d.findChild(tr("Committer") + "CommitDate"); + QVERIFY(committerCommitDate); + committerCommitDate->setDateTime( + QDateTime(QDate(2013, 5, 2), QTime(11, 22, 7))); + + // current + committerCurrent->click(); + auto info = d.getInfo(); + QCOMPARE(info.committerInfo.commitDateType, + ContributorInfo::SelectedDateTimeType::Current); + QCOMPARE(committerCommitDate->isVisible(), false); + + // original + committerOriginal->click(); + info = d.getInfo(); + QCOMPARE(info.committerInfo.commitDateType, + ContributorInfo::SelectedDateTimeType::Original); + QCOMPARE(committerCommitDate->isVisible(), false); + QCOMPARE(info.committerInfo.commitDate, + QDateTime::fromString("Sun May 23 11:36:26 2022 +0200", + Qt::RFC2822Date)); + + // manual + committerManual->click(); + info = d.getInfo(); + QCOMPARE(info.committerInfo.commitDateType, + ContributorInfo::SelectedDateTimeType::Manual); + QCOMPARE(committerCommitDate->isVisible(), true); + QCOMPARE(info.committerInfo.commitDate, + QDateTime(QDate(2013, 5, 2), QTime(11, 22, 7))); + } + + auto info = d.getInfo(); + QCOMPARE(info.authorInfo.name, "New Author"); + QCOMPARE(info.authorInfo.email, "New Author Email"); + QCOMPARE(info.committerInfo.name, "New Committer"); + QCOMPARE(info.committerInfo.email, "New Committer Email"); + QCOMPARE(info.commitMessage, "Test commit message"); + } +} + +void TestAmend::testAmendDialog2() { + // Test changing author name, author email, committer name, committer email + + Test::ScratchRepository repo; + auto authorSignature = repo->signature( + "New Author", "New Author Email", + QDateTime::fromString("Sun May 23 10:36:26 2022 +0200", Qt::RFC2822Date)); + auto committerSignature = repo->signature( + "New Committer", "New Committer Email", + QDateTime::fromString("Sun May 23 11:36:26 2022 +0200", Qt::RFC2822Date)); + + AmendDialog d(authorSignature, committerSignature, "Test commit message"); + + auto commitMessageEditor = + d.findChild("Textlabel Commit Message"); + QVERIFY(commitMessageEditor); + commitMessageEditor->setText("Changing the commit message"); + + // Author + { + const auto *author = d.findChild(tr("Author")); + QVERIFY(author); + + auto *name = author->findChild("Name"); + QVERIFY(name); + auto *email = author->findChild("Email"); + QVERIFY(email); + + name->setText("Another author name"); + email->setText("Another author email address"); + } + + // Committer + { + const auto *committer = d.findChild(tr("Committer")); + QVERIFY(committer); + + auto *name = committer->findChild("Name"); + QVERIFY(name); + auto *email = committer->findChild("Email"); + QVERIFY(email); + + name->setText("Another committer name"); + email->setText("Another committer email address"); + } + + const auto info = d.getInfo(); + + QCOMPARE(info.authorInfo.name, "Another author name"); + QCOMPARE(info.authorInfo.email, "Another author email address"); + QCOMPARE(info.committerInfo.name, "Another committer name"); + QCOMPARE(info.committerInfo.email, "Another committer email address"); + QCOMPARE(info.commitMessage, "Changing the commit message"); +} + +TEST_MAIN(TestAmend) +#include "amend.moc" diff --git a/test/init_repo.cpp b/test/init_repo.cpp index 25473c49..932045bc 100644 --- a/test/init_repo.cpp +++ b/test/init_repo.cpp @@ -8,8 +8,10 @@ // #include "Test.h" +#include "dialogs/AmendDialog.h" #include "dialogs/CloneDialog.h" #include "dialogs/StartDialog.h" +#include "qnamespace.h" #include "ui/CommitList.h" #include "ui/DetailView.h" #include "ui/DiffView/DiffView.h" @@ -22,6 +24,7 @@ #include #include #include +#include #include #include @@ -141,6 +144,13 @@ void TestInitRepo::amendCommit() { view->amendCommit(); + auto dialog = view->findChild(); + QVERIFY(dialog); + dialog->findChild()->setText("Some other commit message"); + dialog->accept(); + + qWait(300); + { auto timeout = Timeout(10000, "Repository didn't detect status change in time"); @@ -153,8 +163,9 @@ void TestInitRepo::amendCommit() { QVERIFY(commitList); QAbstractItemModel *commitModel = commitList->model(); QModelIndex index = commitModel->index(0, 0); - QString name = commitModel->data(index, Qt::DisplayRole).toString(); - QCOMPARE(name, QString("Uncommitted changes")); + auto commit = commitModel->data(index, CommitList::Role::CommitRole) + .value(); + QCOMPARE(commit.message(), QString("Some other commit message")); } void TestInitRepo::editFile() {